diff --git a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt index 6172879df..3df05168a 100644 --- a/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt +++ b/src/main/kotlin/com/lambda/command/commands/TransferCommand.kt @@ -32,9 +32,9 @@ import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.findContainersWithMaterial import com.lambda.interaction.material.container.ContainerManager.findContainersWithSpace -import com.lambda.interaction.material.transfer.TransferResult -import com.lambda.task.RootTask.run -import com.lambda.threading.runSafe +import com.lambda.task.RootTask +import com.lambda.task.Task +import com.lambda.threading.runSafeAutomated import com.lambda.util.Communication.info import com.lambda.util.extension.CommandBuilder @@ -43,7 +43,7 @@ object TransferCommand : LambdaCommand( usage = "transfer ", description = "Transfer items from anywhere to anywhere", ) { - private var lastContainerTransfer: TransferResult.ContainerTransfer? = null + private var lastContainerTransfer: Task<*>? = null override fun CommandBuilder.create() { required(itemStack("stack", registry)) { stack -> @@ -54,12 +54,10 @@ object TransferCommand : LambdaCommand( val selection = selectStack(count) { isItem(stack(ctx).value().item) } - with(AutomationConfig.Companion.DEFAULT) { - runSafe { - selection.findContainersWithMaterial().forEachIndexed { i, container -> - builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) - } - } + AutomationConfig.Companion.DEFAULT.runSafeAutomated { + selection.findContainersWithMaterial().forEachIndexed { i, container -> + builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) + } } builder.buildFuture() } @@ -68,11 +66,9 @@ object TransferCommand : LambdaCommand( val selection = selectStack(amount(ctx).value()) { isItem(stack(ctx).value().item) } - with(AutomationConfig.Companion.DEFAULT) { - runSafe { - findContainersWithSpace(selection).forEachIndexed { i, container -> - builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) - } + AutomationConfig.Companion.DEFAULT.runSafeAutomated { + selection.findContainersWithSpace().forEachIndexed { i, container -> + builder.suggest("\"${i + 1}. ${container.name}\"", container.description(selection)) } } builder.buildFuture() @@ -81,35 +77,17 @@ object TransferCommand : LambdaCommand( val selection = selectStack(amount().value()) { isItem(stack().value().item) } - val fromContainer = ContainerManager.containers().find { - it.name == from().value().split(".").last().trim() - } ?: return@executeWithResult failure("From container not found") + AutomationConfig.Companion.DEFAULT.runSafeAutomated { + val fromContainer = ContainerManager.containers().find { + it.name == from().value().split(".").last().trim() + } ?: return@executeWithResult failure("From container not found") - val toContainer = ContainerManager.containers().find { - it.name == to().value().split(".").last().trim() - } ?: return@executeWithResult failure("To container not found") + val toContainer = ContainerManager.containers().find { + it.name == to().value().split(".").last().trim() + } ?: return@executeWithResult failure("To container not found") - with(AutomationConfig.Companion.DEFAULT) { - when (val transaction = fromContainer.transfer(selection, toContainer)) { - is TransferResult.ContainerTransfer -> { - info("${transaction.name} started.") - lastContainerTransfer = transaction - transaction.finally { - info("${transaction.name} completed.") - }.run() - return@executeWithResult success() - } - - is TransferResult.MissingItems -> { - return@executeWithResult failure("Missing items: ${transaction.missing}") - } - - is TransferResult.NoSpace -> { - return@executeWithResult failure("No space in ${toContainer.name}") - } - } + fromContainer.transferByTask(selection, toContainer).execute(RootTask) } - return@executeWithResult success() } } diff --git a/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt b/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt index e4b2fab9d..9c4cad6ca 100644 --- a/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt +++ b/src/main/kotlin/com/lambda/config/groups/BuildConfig.kt @@ -34,8 +34,8 @@ interface BuildConfig : ISettingGroup { val actionTimeout: Int val maxBuildDependencies: Int - val entityReach: Double val blockReach: Double + val entityReach: Double val scanReach: Double val checkSideVisibility: Boolean diff --git a/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt b/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt index 18464823d..e1fa36913 100644 --- a/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/BuildSettings.kt @@ -44,8 +44,8 @@ class BuildSettings( override val actionTimeout by c.setting("Action Timeout", 10, 1..30, 1, "Timeout for block breaks in ticks", unit = " ticks").group(baseGroup, Group.General).index() override val maxBuildDependencies by c.setting("Max Sim Dependencies", 3, 0..10, 1, "Maximum dependency build results").group(baseGroup, Group.General).index() - override var entityReach by c.setting("Attack Reach", 3.0, 1.0..7.0, 0.01, "Maximum entity interaction distance").group(baseGroup, Group.Reach).index() override var blockReach by c.setting("Interact Reach", 4.5, 1.0..7.0, 0.01, "Maximum block interaction distance").group(baseGroup, Group.Reach).index() + override var entityReach by c.setting("Attack Reach", 3.0, 1.0..7.0, 0.01, "Maximum entity interaction distance").group(baseGroup, Group.Reach).index() override val scanReach: Double get() = max(entityReach, blockReach) override val checkSideVisibility by c.setting("Visibility Check", true, "Whether to check if an AABB side is visible").group(baseGroup, Group.Scan).index() diff --git a/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt b/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt index 33c56aec5..2dc25e38a 100644 --- a/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt +++ b/src/main/kotlin/com/lambda/config/groups/InventorySettings.kt @@ -41,9 +41,8 @@ class InventorySettings( override val providerPriority by c.setting("Provider Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when retrieving the item from").group(baseGroup, Group.Container).index() override val storePriority by c.setting("Store Priority", InventoryConfig.Priority.WithMinItems, "What container to prefer when storing the item to").group(baseGroup, Group.Container).index() - override val immediateAccessOnly by c.setting("Immediate Access Only", false, "Only allow access to inventories that can be accessed immediately").group(baseGroup, Group.Access).index() - override val accessShulkerBoxes by c.setting("Access Shulker Boxes", true, "Allow access to the player's shulker boxes") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() - override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() - override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() - override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes") { !immediateAccessOnly }.group(baseGroup, Group.Access).index() + override val accessShulkerBoxes by c.setting("Access Shulker Boxes", false, "Allow access to the player's shulker boxes").group(baseGroup, Group.Access).index() + override val accessChests by c.setting("Access Chests", false, "Allow access to the player's normal chests").group(baseGroup, Group.Access).index() + override val accessEnderChest by c.setting("Access Ender Chest", false, "Allow access to the player's ender chest").group(baseGroup, Group.Access).index() + override val accessStashes by c.setting("Access Stashes", false, "Allow access to the player's stashes").group(baseGroup, Group.Access).index() } \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt b/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt index 97a448780..0efcda8b6 100644 --- a/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt +++ b/src/main/kotlin/com/lambda/interaction/BaritoneManager.kt @@ -21,11 +21,11 @@ import baritone.api.BaritoneAPI import baritone.api.IBaritone import baritone.api.Settings import baritone.api.pathing.goals.Goal +import com.lambda.config.AutomationConfig import com.lambda.config.Configurable import com.lambda.config.configurations.LambdaConfig import com.lambda.config.groups.RotationSettings import com.lambda.context.Automated -import com.lambda.config.AutomationConfig import com.lambda.util.BlockUtils.blockPos import com.lambda.util.NamedEnum import net.fabricmc.loader.api.FabricLoader @@ -352,8 +352,10 @@ object BaritoneManager : Configurable(LambdaConfig), Automated by AutomationConf * Whether Baritone is active (pathing, calculating goal, etc.) */ val isActive: Boolean - get() = isBaritoneLoaded && (primary?.customGoalProcess?.isActive == true || primary?.pathingBehavior?.isPathing == true || primary?.pathingControlManager?.mostRecentInControl() - ?.orElse(null)?.isActive == true) + get() = isBaritoneLoaded && + (primary?.customGoalProcess?.isActive == true || + primary?.pathingBehavior?.isPathing == true || + primary?.pathingControlManager?.mostRecentInControl()?.orElse(null)?.isActive == true) /** * Sets the current Baritone goal and starts pathing diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt index 5d48ef331..322ef7b19 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/BreakSim.kt @@ -185,10 +185,9 @@ class BreakSim private constructor(simInfo: SimInfo) } val hotbarCandidates = stackSelection - .findContainersWithMaterial(silentSwapSelection) - .map { it.matchingStacks(stackSelection) } - .flatten() - if (hotbarCandidates.isEmpty()) { + .findContainersWithMaterial(silentSwapSelection) + .flatMap { it.matchingStacks(stackSelection) } + if (hotbarCandidates.isEmpty()) { result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack)) return null } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt index e67862fd5..97d99604e 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/checks/InteractSim.kt @@ -47,6 +47,7 @@ import com.lambda.util.item.ItemUtils.blockItem import com.lambda.util.math.MathUtils.floorToInt import com.lambda.util.math.minus import com.lambda.util.player.MovementUtils.sneaking +import com.lambda.util.player.SlotUtils.hotbarStacks import com.lambda.util.player.copyPlayer import com.lambda.util.world.raycast.RayCastUtils.blockResult import kotlinx.coroutines.CoroutineScope @@ -223,8 +224,9 @@ class InteractSim private constructor(simInfo: InteractSimInfo) result(GenericResult.WrongItemSelection(pos, stackSelection, player.mainHandStack)) return null } + val hotbarStacks = player.hotbarStacks return stackSelection.filterStacks(container.stacks).run { - firstOrNull { it.inventoryIndex == player.inventory.selectedSlot } + firstOrNull { hotbarStacks.indexOf(it) == player.inventory.selectedSlot } ?: firstOrNull() } } @@ -271,7 +273,7 @@ class InteractSim private constructor(simInfo: InteractSimInfo) } private suspend fun AutomatedSafeContext.testPlaceState(context: ItemPlacementContext): BlockState? { - val resultState = context.stack.blockItem.getPlacementState(context) + val resultState = (context.stack.blockItem ?: return null).getPlacementState(context) ?: run { handleEntityBlockage(context) return null @@ -285,7 +287,7 @@ class InteractSim private constructor(simInfo: InteractSimInfo) private suspend fun AutomatedSafeContext.handleEntityBlockage(context: ItemPlacementContext): List { val pos = context.blockPos - val theoreticalState = context.stack.blockItem.block.getPlacementState(context) + val theoreticalState = (context.stack.blockItem ?: return emptyList()).block.getPlacementState(context) ?: return emptyList() val collisionShape = theoreticalState.getCollisionShape( diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt index ba8f70371..afbff5de1 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/Resolvable.kt @@ -17,14 +17,13 @@ package com.lambda.interaction.construction.simulation.result -import com.lambda.context.Automated -import com.lambda.context.SafeContext +import com.lambda.context.AutomatedSafeContext import com.lambda.task.Task /** * Represents a [BuildResult] with a resolvable [Task] */ interface Resolvable { - context(automated: Automated, safeContext: SafeContext) - fun resolve(): Task<*>? + context(task: Task<*>, _: AutomatedSafeContext) + fun resolve() } diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt index 4356dc53d..ed85fd9e8 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/BreakResult.kt @@ -19,11 +19,9 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalBlock import baritone.api.pathing.goals.GoalInverted -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.graphics.renderer.esp.DirectionMask.mask -import com.lambda.graphics.esp.ShapeScope +import com.lambda.context.AutomatedSafeContext import com.lambda.graphics.mc.TransientRegionESP +import com.lambda.graphics.renderer.esp.DirectionMask.mask import com.lambda.interaction.construction.simulation.context.BreakContext import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult @@ -34,8 +32,9 @@ import com.lambda.interaction.construction.simulation.result.Navigable import com.lambda.interaction.construction.simulation.result.Rank import com.lambda.interaction.construction.simulation.result.Resolvable import com.lambda.interaction.material.StackSelection.Companion.selectStack -import com.lambda.interaction.material.container.ContainerManager.transfer -import com.lambda.interaction.material.container.containers.MainHandContainer +import com.lambda.interaction.material.container.ContainerManager.transferByTask +import com.lambda.interaction.material.container.containers.HotbarContainer +import com.lambda.task.Task import net.minecraft.block.BlockState import net.minecraft.item.Item import net.minecraft.util.math.BlockPos @@ -98,11 +97,12 @@ sealed class BreakResult : BuildResult() { ) : Resolvable, BreakResult() { override val rank = Rank.BreakItemCantMine - context(automated: Automated, safeContext: SafeContext) - override fun resolve() = + context(task: Task<*>, _: AutomatedSafeContext) + override fun resolve() { selectStack { isItem(badItem).not() - }.transfer(MainHandContainer) + }.transferByTask(HotbarContainer)?.execute(task) + } override fun compareResult(other: ComparableResult) = when (other) { diff --git a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt index dc8716f3f..0694c3c6d 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/simulation/result/results/GenericResult.kt @@ -18,9 +18,7 @@ package com.lambda.interaction.construction.simulation.result.results import baritone.api.pathing.goals.GoalNear -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.graphics.esp.ShapeScope +import com.lambda.context.AutomatedSafeContext import com.lambda.graphics.mc.TransientRegionESP import com.lambda.interaction.construction.simulation.result.BuildResult import com.lambda.interaction.construction.simulation.result.ComparableResult @@ -29,8 +27,9 @@ import com.lambda.interaction.construction.simulation.result.Navigable import com.lambda.interaction.construction.simulation.result.Rank import com.lambda.interaction.construction.simulation.result.Resolvable import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.container.ContainerManager.transfer -import com.lambda.interaction.material.container.containers.MainHandContainer +import com.lambda.interaction.material.container.ContainerManager.transferByTask +import com.lambda.interaction.material.container.containers.HotbarContainer +import com.lambda.task.Task import net.minecraft.client.data.TextureMap.side import net.minecraft.item.ItemStack import net.minecraft.util.math.BlockPos @@ -98,8 +97,10 @@ sealed class GenericResult : BuildResult() { override val rank = Rank.WrongItem private val color = Color(3, 252, 169, 25) - context(automated: Automated, safeContext: SafeContext) - override fun resolve() = neededSelection.transfer(MainHandContainer) + context(task: Task<*>, _: AutomatedSafeContext) + override fun resolve() { + neededSelection.transferByTask(HotbarContainer)?.execute(task) + } override fun render(esp: TransientRegionESP) { esp.shapes(pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble()) { diff --git a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt index aa7eaacaf..3bbfcee78 100644 --- a/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt +++ b/src/main/kotlin/com/lambda/interaction/construction/verify/TargetState.kt @@ -34,7 +34,7 @@ import net.minecraft.state.property.Property import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -sealed class TargetState() : StateMatcher { +sealed class TargetState : StateMatcher { data object Empty : TargetState() { override fun toString() = "Empty" @@ -87,7 +87,7 @@ sealed class TargetState() : StateMatcher { override fun getStack(pos: BlockPos) = with(automatedSafeContext) { findDisposable()?.stacks?.firstOrNull { - it.item.block in inventoryConfig.disposables && it.item.block !in replace + it.item in inventoryConfig.disposables && it.item.block !in replace } ?: ItemStack(Items.NETHERRACK) } @@ -114,7 +114,7 @@ sealed class TargetState() : StateMatcher { override fun getStack(pos: BlockPos) = with(automatedSafeContext) { findDisposable()?.stacks?.firstOrNull { - it.item.block in inventoryConfig.disposables + it.item in inventoryConfig.disposables } ?: ItemStack(Items.NETHERRACK) } @@ -165,8 +165,7 @@ sealed class TargetState() : StateMatcher { } data class Stack(val itemStack: ItemStack) : TargetState() { - private val startStack: ItemStack = itemStack.copy() - override fun toString() = "Stack of ${startStack.item.name.string.capitalize()}" + override fun toString() = "Stack of ${itemStack.item.name.string.capitalize()}" private val block = itemStack.item.block diff --git a/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt b/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt index e9b7b02df..60bcc6386 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/interacting/InteractManager.kt @@ -256,7 +256,7 @@ object InteractManager : Manager( ) { ActionResult.PASS } else { - val item = itemStack.blockItem + val item = itemStack.blockItem ?: return ActionResult.FAIL place(interactContext, request, hand, hitResult, item, ItemPlacementContext(context)) } } diff --git a/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt b/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt index a21fd7842..41a879922 100644 --- a/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt +++ b/src/main/kotlin/com/lambda/interaction/managers/inventory/InventoryConfig.kt @@ -18,36 +18,36 @@ package com.lambda.interaction.managers.inventory import com.lambda.config.ISettingGroup +import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.interaction.material.ContainerSelection import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer import com.lambda.util.Describable import com.lambda.util.NamedEnum -import net.minecraft.block.Block +import net.minecraft.item.Item interface InventoryConfig : ISettingGroup { val actionsPerSecond: Int val tickStageMask: Collection - val disposables: Collection + val disposables: Collection val swapWithDisposables: Boolean val providerPriority: Priority val storePriority: Priority - val immediateAccessOnly: Boolean val accessShulkerBoxes: Boolean - val accessEnderChest: Boolean val accessChests: Boolean + val accessEnderChest: Boolean val accessStashes: Boolean val containerSelection: ContainerSelection get() = ContainerSelection.selectContainer { val allowedContainers = buildSet { addAll(MaterialContainer.Rank.entries) - if (!accessShulkerBoxes || immediateAccessOnly) remove(MaterialContainer.Rank.ShulkerBox) - if (!accessEnderChest || immediateAccessOnly) remove(MaterialContainer.Rank.EnderChest) - if (!accessChests || immediateAccessOnly) remove(MaterialContainer.Rank.Chest) - if (!accessStashes || immediateAccessOnly) remove(MaterialContainer.Rank.Stash) + if (!accessShulkerBoxes) remove(MaterialContainer.Rank.ShulkerBox) + if (!accessEnderChest) remove(MaterialContainer.Rank.EnderChest) + if (!accessChests) remove(MaterialContainer.Rank.Chest) + if (!accessStashes) remove(MaterialContainer.Rank.Stash) } ofAnyType(*allowedContainers.toTypedArray()) } @@ -59,6 +59,7 @@ interface InventoryConfig : ISettingGroup { WithMinItems("With Min Items", "Pick containers with the fewest matching items (or least space) first; useful for topping off or clearing leftovers."), WithMaxItems("With Max Items", "Pick containers with the most matching items (or most space) first; ideal for bulk moves with fewer transfers."); + context(_: SafeContext) fun materialComparator(selection: StackSelection) = when (this) { WithMaxItems -> compareBy { it.rank } @@ -70,6 +71,7 @@ interface InventoryConfig : ISettingGroup { .thenBy { it.name } } + context(_: SafeContext) fun spaceComparator(selection: StackSelection) = when (this) { WithMaxItems -> compareBy { it.rank } diff --git a/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt b/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt index 706e67018..88f8a2483 100644 --- a/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt +++ b/src/main/kotlin/com/lambda/interaction/material/ContainerSelection.kt @@ -38,8 +38,9 @@ class ContainerSelection { * which matches the given StackSelection. */ @ContainerSelectionDsl + context(_: SafeContext) fun matches(stackSelection: StackSelection): (MaterialContainer) -> Boolean = - { container -> container.matchingStacks(stackSelection).isNotEmpty() } + { container -> container.matchingSlots(stackSelection).isNotEmpty() } /** * Returns a function that checks whether a given MaterialContainer matches the criteria @@ -63,11 +64,6 @@ class ContainerSelection { fun noneOfType(vararg types: MaterialContainer.Rank): (MaterialContainer) -> Boolean = { container -> !types.contains(container.rank) } - @ContainerSelectionDsl - context(_: SafeContext) - fun immediateOnly(): (MaterialContainer) -> Boolean = - { container -> !container.isImmediatelyAccessible() } - /** * Returns a function that combines two container predicates using logical AND. */ diff --git a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt index 61bb65f12..594ac5efe 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ContainerManager.kt @@ -17,7 +17,7 @@ package com.lambda.interaction.material.container -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.core.Loadable import com.lambda.event.events.InventoryEvent @@ -36,6 +36,7 @@ import net.minecraft.block.entity.ChestBlockEntity import net.minecraft.block.entity.EnderChestBlockEntity import net.minecraft.screen.GenericContainerScreenHandler import net.minecraft.screen.ScreenHandlerType +import net.minecraft.screen.slot.Slot // ToDo: Make this a Configurable to save container caches. Should use a cached region based storage system. object ContainerManager : Loadable { @@ -45,7 +46,7 @@ object ContainerManager : Loadable { private val compileContainers = getInstances() private val runtimeContainers = mutableSetOf() - private var lastInteractedBlockEntity: BlockEntity? = null + var lastInteractedBlockEntity: BlockEntity? = null override fun load() = "Loaded ${compileContainers.size} containers" @@ -82,48 +83,73 @@ object ContainerManager : Loadable { } } + context(_: SafeContext) fun containers() = containers.flatMap { setOf(it) + it.shulkerContainer }.sorted() - context(automated: Automated, _: SafeContext) + context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.transfer(destination: MaterialContainer) = - findContainerWithMaterial()?.transfer(this, destination) + with(automatedSafeContext) { + findContainerWithMaterial( + inventoryConfig.containerSelection + )?.transfer(this@transfer, destination) + ?: false + } + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.transferByTask(destination: MaterialContainer) = + with(automatedSafeContext) { + findContainerWithMaterial()?.transferByTask(this@transferByTask, destination) + } + context(_: SafeContext) fun findContainer( block: (MaterialContainer) -> Boolean, ): MaterialContainer? = containers().find(block) - context(automated: Automated, _: SafeContext) + context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.findContainerWithMaterial( - containerSelection: ContainerSelection = automated.inventoryConfig.containerSelection + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection ): MaterialContainer? = findContainersWithMaterial(containerSelection).firstOrNull() - context(_: Automated, _: SafeContext) - fun findContainerWithSpace(selection: StackSelection): MaterialContainer? = - findContainersWithSpace(selection).firstOrNull() - - context(automated: Automated, safeContext: SafeContext) + context(automatedSafeContext: AutomatedSafeContext) fun StackSelection.findContainersWithMaterial( - containerSelection: ContainerSelection = automated.inventoryConfig.containerSelection, + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection, ): List = containers() - .filter { !automated.inventoryConfig.immediateAccessOnly || it.isImmediatelyAccessible() } - .filter { it.materialAvailable(this) >= count } .filter { containerSelection.matches(it) } - .sortedWith(automated.inventoryConfig.providerPriority.materialComparator(this)) + .filter { it.materialAvailable(this) >= count } + .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.materialComparator(this)) - context(automated: Automated, safeContext: SafeContext) - fun findContainersWithSpace( - selection: StackSelection, + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findContainerWithSpace( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ): MaterialContainer? = findContainersWithSpace(containerSelection).firstOrNull() + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findContainersWithSpace( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection ): List = containers() - .filter { !automated.inventoryConfig.immediateAccessOnly || it.isImmediatelyAccessible() } - .filter { it.spaceAvailable(selection) >= selection.count } - .filter { automated.inventoryConfig.containerSelection.matches(it) } - .sortedWith(automated.inventoryConfig.providerPriority.spaceComparator(selection)) + .filter { containerSelection.matches(it) } + .filter { it.spaceAvailable(this) >= count } + .sortedWith(automatedSafeContext.inventoryConfig.providerPriority.spaceComparator(this)) + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findSlotWithMaterial( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ) = findSlotsWithMaterial(containerSelection).firstOrNull() + + context(automatedSafeContext: AutomatedSafeContext) + fun StackSelection.findSlotsWithMaterial( + containerSelection: ContainerSelection = automatedSafeContext.inventoryConfig.containerSelection + ): List = + findContainersWithMaterial(containerSelection) + .flatMap { filterSlots(it.slots) } + - context(automated: Automated) + context(automatedSafeContext: AutomatedSafeContext) fun findDisposable() = containers().find { container -> - automated.inventoryConfig.disposables.any { container.materialAvailable(it.asItem().select()) > 0 } + automatedSafeContext.inventoryConfig.disposables.any { container.materialAvailable(it.asItem().select()) > 0 } } class NoContainerFound(selection: StackSelection) : Exception("No container found matching $selection") diff --git a/src/main/kotlin/com/lambda/interaction/material/ContainerTask.kt b/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt similarity index 57% rename from src/main/kotlin/com/lambda/interaction/material/ContainerTask.kt rename to src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt index 630c8d664..291cbbaa2 100644 --- a/src/main/kotlin/com/lambda/interaction/material/ContainerTask.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/ExternalContainer.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 Lambda + * Copyright 2026 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,26 +15,13 @@ * along with this program. If not, see . */ -package com.lambda.interaction.material +package com.lambda.interaction.material.container -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.context.AutomatedSafeContext import com.lambda.task.Task +import com.lambda.task.TaskGenerator -abstract class ContainerTask : Task() { - private var finish = false - private val delay = 5 - private var currentDelay = 0 - - fun delayedFinish() { - finish = true - } - - init { - listen { - if (finish) { - if (currentDelay++ > delay) success() - } - } - } -} +interface ExternalContainer { + context(_: AutomatedSafeContext) + fun accessThen(exitAfter: Boolean = true, taskGenerator: TaskGenerator): Task<*>? +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt index 7279c308d..dd4ce6cda 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/MaterialContainer.kt @@ -18,15 +18,14 @@ package com.lambda.interaction.material.container import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.inventory.InventoryRequest +import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial import com.lambda.interaction.material.container.containers.ShulkerBoxContainer -import com.lambda.interaction.material.transfer.TransferResult import com.lambda.task.Task -import com.lambda.util.Communication.logError +import com.lambda.task.tasks.ContainerTransferTask import com.lambda.util.Nameable import com.lambda.util.item.ItemStackUtils.count import com.lambda.util.item.ItemStackUtils.empty @@ -40,17 +39,38 @@ import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal import com.lambda.util.text.text +import net.minecraft.component.DataComponentTypes import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.text.Text // ToDo: Make jsonable to persistently store them abstract class MaterialContainer( val rank: Rank ) : Nameable, Comparable { + context(_: SafeContext) + abstract val slots: List abstract var stacks: List + open val swapMethodPriority = 0 abstract val description: Text + context(automated: Automated) + open val replaceSorter get() = compareByDescending { + it.stack.isEmpty + }.thenByDescending { + it.stack.item in automated.inventoryConfig.disposables + }.thenByDescending { + !it.stack.item.components.contains(DataComponentTypes.TOOL) + }.thenByDescending { + !it.stack.item.components.contains(DataComponentTypes.FOOD) + }.thenByDescending { + !it.stack.item.components.contains(DataComponentTypes.CONSUMABLE) + }.thenByDescending { + it.stack.isStackable + } + @TextDsl + context(_: SafeContext) fun TextBuilder.stock(selection: StackSelection) { literal("\n") literal("Contains ") @@ -66,6 +86,7 @@ abstract class MaterialContainer( highlighted("${selection.optimalStack?.name?.string}") } + context(_: SafeContext) fun description(selection: StackSelection) = buildText { text(description) @@ -75,20 +96,21 @@ abstract class MaterialContainer( override val name: String get() = buildText { text(description) }.string + context(_: SafeContext) val shulkerContainer get() = - stacks.filter { - it.item in ItemUtils.shulkerBoxes - }.map { stack -> + slots.filter { + it.stack.item in ItemUtils.shulkerBoxes + }.map { slot -> ShulkerBoxContainer( - stack.shulkerBoxContents, + slot.stack.shulkerBoxContents, containedIn = this@MaterialContainer, - shulkerStack = stack + shulkerSlot = slot ) }.toSet() - fun update(stacks: List) { - this.stacks = stacks + fun update(slots: List) { + this.stacks = slots } class FailureTask(override val name: String) : Task() { @@ -97,68 +119,57 @@ abstract class MaterialContainer( } } - class AwaitItemTask( - override val name: String, - val selection: StackSelection, - automated: Automated - ) : Task(), Automated by automated { - init { - listen { - if (selection.findContainerWithMaterial() != null) { - success() - } - } + context(_: SafeContext) + open fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + if (fromHere.stack.isEmpty) pickupAndPlace(toSlot.id, fromHere.id) + else { + pickupAndPlace(fromHere.id, toSlot.id) + if (!toSlot.stack.isEmpty) pickup(fromHere.id) } + } - override fun SafeContext.onStart() { - logError(name) + context(automatedSafeContext: AutomatedSafeContext) + fun transfer(stackSelection: StackSelection, destination: MaterialContainer): Boolean = + with(automatedSafeContext) { + val fromSlot = getSlot(stackSelection) ?: return false + val toSlot = destination.getReplaceableSlot() ?: return false + return inventoryRequest { + if (swapMethodPriority > destination.swapMethodPriority) transfer(fromSlot, toSlot) + else with(destination) { transfer(toSlot, fromSlot) } + }.submit().done } - } - /** - * Withdraws items from the container to the player's inventory. - */ - @Task.Ta5kBuilder - context(automated: Automated) - open fun withdraw(selection: StackSelection): Task<*>? = null + context(automatedSafeContext: AutomatedSafeContext) + fun transferByTask(stackSelection: StackSelection, destination: MaterialContainer, failIfNoMaterial: Boolean = false) = + ContainerTransferTask(this, destination, stackSelection, automatedSafeContext, failIfNoMaterial) - /** - * Deposits items from the player's inventory into the container. - */ - @Task.Ta5kBuilder - context(automated: Automated) - open fun deposit(selection: StackSelection): Task<*>? = null + protected fun InventoryRequest.InvRequestBuilder.pickupAndPlace(fromId: Int, toId: Int) { + pickup(fromId) + pickup(toId) + } + context(_: SafeContext) open fun matchingStacks(selection: StackSelection) = selection.filterStacks(stacks) + context(_: SafeContext) + open fun matchingSlots(selection: StackSelection) = + selection.filterSlots(slots) + + context(_: SafeContext) open fun materialAvailable(selection: StackSelection) = matchingStacks(selection).count + context(_: SafeContext) open fun spaceAvailable(selection: StackSelection) = matchingStacks(selection).spaceLeft + stacks.empty * selection.stackSize - context(safeContext: SafeContext) - abstract fun isImmediatelyAccessible(): Boolean - - context(automated: Automated) - fun transfer(selection: StackSelection, destination: MaterialContainer): TransferResult { - val amount = materialAvailable(selection) - if (amount < selection.count) { - return TransferResult.MissingItems(selection.count - amount) - } - -// val space = destination.spaceAvailable(selection) -// if (space == 0) { -// return TransferResult.NoSpace -// } + context(_: AutomatedSafeContext) + open fun getReplaceableSlot() = slots.sortedWith(replaceSorter).firstOrNull() -// val transferAmount = minOf(amount, space) -// selection.selector = { true } -// selection.count = transferAmount - - return TransferResult.ContainerTransfer(selection, from = this, to = destination, automated) - } + context(_: SafeContext) + open fun getSlot(stackSelection: StackSelection): Slot? = + stackSelection.filterSlots(slots).firstOrNull() enum class Rank { MainHand, diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt index 1eadb0b53..2bc6f264d 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ChestContainer.kt @@ -17,25 +17,35 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.deposit -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.withdraw -import com.lambda.task.tasks.OpenContainer -import com.lambda.util.Communication.info +import com.lambda.task.Task +import com.lambda.task.TaskGenerator +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal +import net.minecraft.block.entity.ChestBlockEntity import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.util.math.BlockPos data class ChestContainer( override var stacks: List, val blockPos: BlockPos, val containedInStash: StashContainer? = null -) : MaterialContainer(Rank.Chest) { +) : MaterialContainer(Rank.Chest), ExternalContainer { + context(safeContext: SafeContext) + override val slots + get(): List = + if (ContainerManager.lastInteractedBlockEntity is ChestBlockEntity) + safeContext.player.currentScreenHandler.containerSlots + else emptyList() + override val description = buildText { literal("Chest at ") @@ -47,33 +57,11 @@ data class ChestContainer( } } -// override fun prepare() = -// moveIntoEntityRange(blockPos).onSuccess { _, _ -> -//// when { -//// ChestBlock.hasBlockOnTop(world, blockPos) -> breakBlock(blockPos.up()) -//// ChestBlock.hasCatOnTop(world, blockPos) -> kill(cat) -//// } -// if (ChestBlock.isChestBlocked(world, blockPos)) { -// throw ChestBlockedException() -// } -// } - - context(automated: Automated) - override fun withdraw(selection: StackSelection) = - OpenContainer(blockPos, automated) - .then { - info("Withdrawing $selection from ${it.type}") - withdraw(it, selection) - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = - OpenContainer(blockPos, automated) - .then { - info("Depositing $selection to ${it.type}") - deposit(it, selection) + context(automatedSafeContext: AutomatedSafeContext) + override fun accessThen(exitAfter: Boolean, taskGenerator: TaskGenerator): Task<*> = + OpenContainerTask(blockPos, automatedSafeContext).then { + taskGenerator.invoke(automatedSafeContext, Unit).finally { + if (exitAfter) automatedSafeContext.player.closeScreen() } - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt index 398e6f014..116156449 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/CreativeContainer.kt @@ -17,92 +17,58 @@ package com.lambda.interaction.material.container.containers -import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.Task -import com.lambda.util.item.ItemStackUtils.equal -import com.lambda.util.player.gamemode import com.lambda.util.text.buildText import com.lambda.util.text.literal +import net.minecraft.client.gui.screen.ingame.CreativeInventoryScreen +import net.minecraft.entity.player.PlayerEntity +import net.minecraft.inventory.SingleStackInventory import net.minecraft.item.ItemStack +import net.minecraft.screen.PlayerScreenHandler +import net.minecraft.screen.slot.Slot data object CreativeContainer : MaterialContainer(Rank.Creative) { - override var stacks = emptyList() + context(_: SafeContext) + override val slots: List + get() = emptyList() + override var stacks = emptyList() - override val description = buildText { literal("Creative") } - - override fun materialAvailable(selection: StackSelection): Int = - if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 - - override fun spaceAvailable(selection: StackSelection): Int = - if (mc.player?.isCreative == true && selection.optimalStack != null) Int.MAX_VALUE else 0 + override val swapMethodPriority = 11 - class CreativeDeposit @Ta5kBuilder constructor(val selection: StackSelection, automated: Automated) : Task(), Automated by automated { - override val name: String get() = "Removing $selection from creative inventory" - - init { - listen { - if (!gamemode.isCreative) { - // ToDo: Maybe switch gamemode? - failure(NotInCreativeModeException()) - } + override val description = buildText { literal("Creative") } - inventoryRequest { - player.currentScreenHandler?.slots?.let { slots -> - selection.filterSlots(slots).forEach { - clickCreativeStack(ItemStack.EMPTY, it.id) - } - } - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - } - } + context(safeContext : SafeContext) + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + clickCreativeStack(fromHere.stack, toSlot.id) + safeContext.player.currentScreenHandler.slots[toSlot.id].stack = fromHere.stack } - context(automated: Automated) - override fun deposit(selection: StackSelection) = CreativeDeposit(selection, automated) - - class CreativeWithdrawal @Ta5kBuilder constructor(val selection: StackSelection, automated: Automated) : Task(), Automated by automated { - override val name: String get() = "Withdrawing $selection from creative inventory" - - init { - listen { - selection.optimalStack?.let { optimalStack -> - if (player.mainHandStack.equal(optimalStack)) { - success() - return@listen - } - - if (!gamemode.isCreative) { - // ToDo: Maybe switch gamemode? - failure(NotInCreativeModeException()) - } - - inventoryRequest { - clickCreativeStack(optimalStack, 36 + player.inventory.selectedSlot) - action { player.inventory.selectedStack = optimalStack } - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - return@listen - } - - failure(IllegalStateException("Cannot move item: no optimal stack")) - } + context(safeContext: SafeContext) + override fun getSlot(stackSelection: StackSelection) = + stackSelection.optimalStack?.let { stack -> + Slot( + object : SingleStackInventory { + override fun getStack() = stack + override fun setStack(stack: ItemStack?) {} + override fun markDirty() {} + override fun canPlayerUse(player: PlayerEntity?) = false + }, + 0, 0, 0 + ) } - } - // Withdraws items from the creative menu to the player's main hand - context(automated: Automated) - override fun withdraw(selection: StackSelection) = CreativeWithdrawal(selection, automated) + context(safeContext: SafeContext) + override fun materialAvailable(selection: StackSelection): Int = + if (safeContext.player.isCreative && correctScreenHandler && selection.optimalStack != null) Int.MAX_VALUE else 0 context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = safeContext.gamemode.isCreative + override fun spaceAvailable(selection: StackSelection): Int = + if (safeContext.player.isCreative && correctScreenHandler && selection.optimalStack != null) Int.MAX_VALUE else 0 - class NotInCreativeModeException : IllegalStateException("Insufficient permission: not in creative mode") + context(safeContext: SafeContext) + private val correctScreenHandler + get() = safeContext.player.currentScreenHandler is PlayerScreenHandler || safeContext.player.currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt index a290a050a..a0bd695eb 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/EnderChestContainer.kt @@ -17,61 +17,52 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.StackSelection.Companion.select +import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ContainerManager.findSlotsWithMaterial +import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.Task -import com.lambda.util.Communication.info +import com.lambda.task.TaskGenerator +import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.task.tasks.PlaceContainerTask +import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText import com.lambda.util.text.literal +import net.minecraft.block.entity.EnderChestBlockEntity import net.minecraft.item.ItemStack +import net.minecraft.item.Items import net.minecraft.util.math.BlockPos -object EnderChestContainer : MaterialContainer(Rank.EnderChest) { +object EnderChestContainer : MaterialContainer(Rank.EnderChest), ExternalContainer { + context(safeContext: SafeContext) + override val slots + get() = + if (ContainerManager.lastInteractedBlockEntity is EnderChestBlockEntity) + safeContext.player.currentScreenHandler.containerSlots + else emptyList() override var stacks = emptyList() - private var placePos: BlockPos? = null override val description = buildText { literal("Ender Chest") } -// override fun prepare(): Task<*> { -// TODO("Not yet implemented") -// } -// findBlock(Blocks.ENDER_CHEST).onSuccess { pos -> -// moveIntoEntityRange(pos) -// placePos = pos -// }.onFailure { -// acquireStack(Items.ENDER_CHEST.select()).onSuccess { _, stack -> -// placeContainer(stack).onSuccess { _, pos -> -// placePos = pos -// } -// } -// } - - class EnderchestWithdrawal @Ta5kBuilder constructor(selection: StackSelection) : Task() { - override val name = "Withdrawing $selection from ender chest" - - override fun SafeContext.onStart() { - info("Not yet implemented") - success() - } - } - - context(automated: Automated) - override fun withdraw(selection: StackSelection) = EnderchestWithdrawal(selection) + private var placePos = BlockPos.ORIGIN - class EnderchestDeposit @Ta5kBuilder constructor(selection: StackSelection) : Task() { - override val name = "Depositing $selection into ender chest" - - override fun SafeContext.onStart() { - info("Not yet implemented") - success() - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = EnderchestDeposit(selection) - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false + context(automatedSafeContext: AutomatedSafeContext) + override fun accessThen(exitAfter: Boolean, taskGenerator: TaskGenerator) = + Items.ENDER_CHEST + .select() + .findSlotsWithMaterial() + .firstOrNull()?.let { slot -> + PlaceContainerTask(slot, automatedSafeContext).then { pos -> + placePos = pos + OpenContainerTask(pos, automatedSafeContext).then { + taskGenerator.invoke(automatedSafeContext, Unit).thenOrNull { + if (exitAfter) automatedSafeContext.breakAndCollectBlock(placePos, lifeMaintenance = false) + else null + } + } + } + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt index 3945ecdee..12b57dfa9 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/HotbarContainer.kt @@ -18,41 +18,29 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.ContainerTask -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.deposit +import com.lambda.util.player.SlotUtils.hotbarSlots import com.lambda.util.player.SlotUtils.hotbarStacks import com.lambda.util.text.buildText import com.lambda.util.text.literal -import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot object HotbarContainer : MaterialContainer(Rank.Hotbar) { - override var stacks: List + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.hotbarSlots + override var stacks get() = mc.player?.hotbarStacks ?: emptyList() set(_) {} - override val description = buildText { literal("Hotbar") } - - class HotbarDeposit @Ta5kBuilder constructor( - val selection: StackSelection, - automated: Automated - ) : ContainerTask(), Automated by automated { - override val name: String get() = "Depositing $selection into hotbar" + override val swapMethodPriority = 9 - override fun SafeContext.onStart() { - val handler = player.currentScreenHandler - deposit(handler, selection).finally { - delayedFinish() - }.execute(this@HotbarDeposit) - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = HotbarDeposit(selection, automated) + override val description = buildText { literal("Hotbar") } context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + swap(toSlot.id, safeContext.player.hotbarSlots.indexOf(fromHere)) + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt index 772548494..af1b0646c 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/InventoryContainer.kt @@ -20,18 +20,20 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc import com.lambda.context.SafeContext import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.player.SlotUtils.allStacks +import com.lambda.util.player.SlotUtils.inventorySlots +import com.lambda.util.player.SlotUtils.inventoryStacks import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot object InventoryContainer : MaterialContainer(Rank.Inventory) { + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.inventorySlots override var stacks: List - get() = mc.player?.allStacks ?: emptyList() + get() = mc.player?.inventoryStacks ?: emptyList() set(_) {} override val description = buildText { literal("Inventory") } - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt index 64206d15e..2b12e47de 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/MainHandContainer.kt @@ -18,73 +18,29 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest -import com.lambda.interaction.material.ContainerTask -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.util.item.ItemStackUtils.equal +import com.lambda.util.player.SlotUtils.mainHandSlots import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack -import net.minecraft.util.Hand +import net.minecraft.screen.slot.Slot object MainHandContainer : MaterialContainer(Rank.MainHand) { + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.mainHandSlots override var stacks: List get() = mc.player?.mainHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override val description = buildText { literal("MainHand") } - - class HandDeposit @Ta5kBuilder constructor( - val selection: StackSelection, - val hand: Hand, - automated: Automated - ) : ContainerTask(), Automated by automated { - override val name: String get() = "Depositing [$selection] to ${hand.name.lowercase().replace("_", " ")}" - - init { - listen { - val moveStack = InventoryContainer.matchingStacks(selection).firstOrNull() ?: run { - failure("No matching stacks found in inventory") - return@listen - } - - val handStack = player.getStackInHand(hand) - if (moveStack.equal(handStack)) { - success() - return@listen - } - - inventoryRequest { - val stackInOffHand = moveStack.equal(player.offHandStack) - val stackInMainHand = moveStack.equal(player.mainHandStack) - if ((hand == Hand.MAIN_HAND && stackInOffHand) || (hand == Hand.OFF_HAND && stackInMainHand)) { - swapHands() - return@inventoryRequest - } + override val swapMethodPriority = 10 - val slot = player.currentScreenHandler.slots.firstOrNull { it.stack == moveStack } - ?: run { - failure(IllegalStateException("Cannot find stack in inventory")) - return@inventoryRequest - } - swap(slot.id, player.inventory.selectedSlot) - - if (hand == Hand.OFF_HAND) swapHands() - - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - } - } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = HandDeposit(selection, Hand.MAIN_HAND, automated) + override val description = buildText { literal("MainHand") } context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + swap(toSlot.id, safeContext.player.inventory.selectedSlot) + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt index 3820f499b..235350717 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/OffHandContainer.kt @@ -18,25 +18,29 @@ package com.lambda.interaction.material.container.containers import com.lambda.Lambda.mc -import com.lambda.context.Automated import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.managers.inventory.InventoryRequest import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.util.player.SlotUtils.offHandSlots import com.lambda.util.text.buildText import com.lambda.util.text.literal import net.minecraft.item.ItemStack -import net.minecraft.util.Hand +import net.minecraft.screen.slot.Slot object OffHandContainer : MaterialContainer(Rank.OffHand) { + context(safeContext: SafeContext) + override val slots: List + get() = safeContext.player.offHandSlots override var stacks: List get() = mc.player?.offHandStack?.let { listOf(it) } ?: emptyList() set(_) {} - override val description = buildText { literal("OffHand") } + override val swapMethodPriority = 10 - context(automated: Automated) - override fun deposit(selection: StackSelection) = MainHandContainer.HandDeposit(selection, Hand.OFF_HAND, automated) + override val description = buildText { literal("OffHand") } - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = true + context(_: SafeContext) + override fun InventoryRequest.InvRequestBuilder.transfer(fromHere: Slot, toSlot: Slot) { + swap(toSlot.id, 40) + } } diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt index 7307bb73d..bacee449b 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/ShulkerBoxContainer.kt @@ -17,83 +17,60 @@ package com.lambda.interaction.material.container.containers -import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ContainerManager +import com.lambda.interaction.material.container.ExternalContainer import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.deposit -import com.lambda.interaction.material.transfer.SlotTransfer.Companion.withdraw -import com.lambda.task.Task +import com.lambda.task.TaskGenerator import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock -import com.lambda.task.tasks.OpenContainer -import com.lambda.task.tasks.PlaceContainer +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.task.tasks.PlaceContainerTask +import com.lambda.threading.runSafe +import com.lambda.util.extension.containerSlots import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal +import net.minecraft.block.entity.ShulkerBoxBlockEntity import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot +import net.minecraft.util.math.BlockPos data class ShulkerBoxContainer( override var stacks: List, val containedIn: MaterialContainer, - val shulkerStack: ItemStack, -) : MaterialContainer(Rank.ShulkerBox) { + val shulkerSlot: Slot, +) : MaterialContainer(Rank.ShulkerBox), ExternalContainer { + context(safeContext: SafeContext) + override val slots + get(): List = + if (ContainerManager.lastInteractedBlockEntity is ShulkerBoxBlockEntity) + safeContext.player.currentScreenHandler.containerSlots + else emptyList() + override val description = buildText { - highlighted(shulkerStack.name.string) + highlighted(shulkerSlot.stack.name.string) literal(" in ") highlighted(containedIn.name) literal(" in slot ") - highlighted("$slotInContainer") - } - - private val slotInContainer: Int get() = containedIn.stacks.indexOf(shulkerStack) - - class ShulkerWithdraw( - private val selection: StackSelection, - private val shulkerStack: ItemStack, - automated: Automated - ) : Task(), Automated by automated { - override val name = "Withdraw $selection from ${shulkerStack.name.string}" - - override fun SafeContext.onStart() { - PlaceContainer(shulkerStack, this@ShulkerWithdraw).then { placePos -> - OpenContainer(placePos, this@ShulkerWithdraw).then { screen -> - withdraw(screen, selection).then { - breakAndCollectBlock(placePos).finally { - success() - } - } - } - }.execute(this@ShulkerWithdraw) + highlighted("${runSafe { slotInContainer }}") } - } - context(automated: Automated) - override fun withdraw(selection: StackSelection) = ShulkerWithdraw(selection, shulkerStack, automated) + context(_: SafeContext) + private val slotInContainer: Int get() = containedIn.slots.indexOf(shulkerSlot) - class ShulkerDeposit( - private val selection: StackSelection, - private val shulkerStack: ItemStack, - automated: Automated - ) : Task(), Automated by automated { - override val name = "Deposit $selection into ${shulkerStack.name.string}" + private var placePos = BlockPos.ORIGIN - override fun SafeContext.onStart() { - PlaceContainer(shulkerStack, this@ShulkerDeposit).then { placePos -> - OpenContainer(placePos, this@ShulkerDeposit).then { screen -> - deposit(screen, selection).then { - breakAndCollectBlock(placePos).finally { - success() - } - } + context(automatedSafeContext: AutomatedSafeContext) + override fun accessThen(exitAfter: Boolean, taskGenerator: TaskGenerator) = + PlaceContainerTask(shulkerSlot, automatedSafeContext).then { pos -> + placePos = pos + OpenContainerTask(pos, automatedSafeContext).then { + taskGenerator.invoke(automatedSafeContext, Unit).thenOrNull { + if (exitAfter) automatedSafeContext.breakAndCollectBlock(placePos) + else null } - }.execute(this@ShulkerDeposit) + } } - } - - context(automated: Automated) - override fun deposit(selection: StackSelection) = ShulkerDeposit(selection, shulkerStack, automated) - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false -} +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt b/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt index bd317df73..282fcd7da 100644 --- a/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt +++ b/src/main/kotlin/com/lambda/interaction/material/container/containers/StashContainer.kt @@ -25,12 +25,16 @@ import com.lambda.util.text.buildText import com.lambda.util.text.highlighted import com.lambda.util.text.literal import net.minecraft.item.ItemStack +import net.minecraft.screen.slot.Slot import net.minecraft.util.math.Box data class StashContainer( val chests: Set, val pos: Box, ) : MaterialContainer(Rank.Stash) { + context(_: SafeContext) + override val slots: List + get() = chests.flatMap { it.slots } override var stacks: List get() = chests.flatMap { it.stacks } set(_) {} @@ -40,11 +44,9 @@ data class StashContainer( highlighted(pos.center.roundedBlockPos.toShortString()) } + context(_: SafeContext) override fun materialAvailable(selection: StackSelection): Int = chests.sumOf { it.materialAvailable(selection) } - - context(safeContext: SafeContext) - override fun isImmediatelyAccessible() = false } diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt deleted file mode 100644 index 9845dc3a7..000000000 --- a/src/main/kotlin/com/lambda/interaction/material/transfer/InventoryChanges.kt +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.interaction.material.transfer - -import com.lambda.interaction.material.StackSelection -import com.lambda.util.item.ItemStackUtils.equal -import net.minecraft.item.ItemStack -import net.minecraft.screen.slot.Slot - -/** - * A class representing changes in an inventory's state over time. It acts as a tracker for - * detecting, storing, and merging differences between the original and updated states of - * inventory slots. This class extends a map-like structure, where the key is the slot index - * and the value is a list of pairs representing the original and updated states of an inventory slot. - * - * Example: - * ``` - * #0: 64 obsidian -> 0 air, 0 air -> 64 obsidian - * #36: 12 observer -> 11 observer - * ``` - * - Where `#0` is the slot id, first it was emptied and then got `64 obsidian` again - * - Where `#36` is the slot id, that was reduced by `1 observer` - * - * @property slots A list of `Slot` objects representing the current inventory slots being tracked. - * Defaults to an empty list. - */ -class InventoryChanges( - private var slots: List, -) : MutableMap>> by HashMap() { - private val originalStacks = slots.map { it.stack.copy() } - - /** - * Detect and store changes directly in the map. - */ - fun detectChanges() { - require(slots.isNotEmpty()) { "Cannot detect changes on an empty slots list" } - slots.forEachIndexed { index, slot -> - val originalStack = originalStacks[index] - val updatedStack = slot.stack - if (!originalStack.equal(updatedStack)) { - getOrPut(index) { mutableListOf() }.add(originalStack to updatedStack.copy()) - } - } - } - - /** - * Create a new `InventoryChanges` object that merges this changes object with another one. - * - * @param other Another `InventoryChanges` instance to merge with. - * @return A new `InventoryChanges` instance containing merged entries. - */ - infix fun merge(other: InventoryChanges) { - require(slots.isNotEmpty()) { "Cannot merge changes to an empty slots list" } - other.forEach { (key, value) -> - getOrPut(key) { mutableListOf() }.addAll(value) - } - } - - /** - * Evaluates if the current inventory changes fulfill the given selection requirement. - * - * @param to A list of `Slot` objects to evaluate for the selection criteria. - * @param selection A `StackSelection` object that specifies the selection criteria, including the - * target items and their required count. - * @return `true` if the total count of matching items across the filtered slots meets or exceeds - * the required count specified in the `StackSelection`; `false` otherwise. - */ - fun fulfillsSelection(to: List, selection: StackSelection): Boolean { - require(slots.isNotEmpty()) { "Cannot evaluate selection on an empty slots list" } - val targetSlots = selection.filterSlots(to).map { it.id } - return filter { it.key in targetSlots }.entries.sumOf { (_, changes) -> - changes.lastOrNull()?.second?.count ?: 0 - } >= selection.count - } - - override fun toString() = - if (entries.isEmpty()) { - "No changes detected" - } else { - entries.joinToString("\n") { key -> - "#${key.key}: ${key.value.joinToString { "${it.first} -> ${it.second}" }}" - } - } -} diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt deleted file mode 100644 index 70788f938..000000000 --- a/src/main/kotlin/com/lambda/interaction/material/transfer/SlotTransfer.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.interaction.material.transfer - -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.event.events.TickEvent -import com.lambda.event.listener.SafeListener.Companion.listen -import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest -import com.lambda.task.Task -import com.lambda.util.extension.containerSlots -import com.lambda.util.extension.inventorySlots -import com.lambda.util.item.ItemUtils.block -import net.minecraft.screen.ScreenHandler -import net.minecraft.screen.slot.Slot - -class SlotTransfer @Ta5kBuilder constructor( - val screen: ScreenHandler, - private val selection: StackSelection, - val from: List, - val to: List, - private val closeScreen: Boolean = true, - automated: Automated -) : Task(), Automated by automated { - private var selectedFrom = listOf() - private var selectedTo = listOf() - override val name: String - get() = "Moving $selection from slots [${selectedFrom.joinToString { "${it.id}" }}] to slots [${selectedTo.joinToString { "${it.id}" }}] in ${screen::class.simpleName}" - - private var delay = 0 - private lateinit var changes: InventoryChanges - - override fun SafeContext.onStart() { - changes = InventoryChanges(player.currentScreenHandler.slots) - } - - init { - listen { - if (changes.fulfillsSelection(to, selection)) { - if (closeScreen) { player.closeHandledScreen() } - success() - return@listen - } - - val current = player.currentScreenHandler - if (current != screen) { - failure("Screen has changed. Expected ${screen::class.simpleName} (revision ${screen.revision}) but got ${current::class.simpleName} (revision ${current.revision})") - return@listen - } - - selectedFrom = selection.filterSlots(from) - selectedTo = to.filter { it.stack.isEmpty } + to.filter { it.stack.item.block in inventoryConfig.disposables } - - val nextFrom = selectedFrom.firstOrNull() ?: return@listen - val nextTo = selectedTo.firstOrNull() ?: return@listen - - inventoryRequest { - swap(nextTo.id, 1) - swap(nextFrom.id, 1) - onComplete { success() } - }.submit(queueIfMismatchedStage = false) - } - } - - companion object { - @Ta5kBuilder - fun Automated.moveItems( - screen: ScreenHandler, - selection: StackSelection, - from: List, - to: List, - closeScreen: Boolean = true, - ) = SlotTransfer(screen, selection, from, to, closeScreen, this) - - @Ta5kBuilder - context(automated: Automated) - fun withdraw(screen: ScreenHandler, selection: StackSelection, closeScreen: Boolean = true) = - automated.moveItems(screen, selection, screen.containerSlots, screen.inventorySlots, closeScreen) - - @Ta5kBuilder - context(automated: Automated) - fun deposit(screen: ScreenHandler, selection: StackSelection, closeScreen: Boolean = true) = - automated.moveItems(screen, selection, screen.inventorySlots, screen.containerSlots, closeScreen) - } -} diff --git a/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt b/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt deleted file mode 100644 index 0bc68f3fc..000000000 --- a/src/main/kotlin/com/lambda/interaction/material/transfer/TransferResult.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2025 Lambda - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.lambda.interaction.material.transfer - -import com.lambda.context.Automated -import com.lambda.context.SafeContext -import com.lambda.interaction.material.StackSelection -import com.lambda.interaction.material.container.MaterialContainer -import com.lambda.task.Task - -abstract class TransferResult : Task() { - data class ContainerTransfer( - val selection: StackSelection, - val from: MaterialContainer, - val to: MaterialContainer, - val automated: Automated - ) : TransferResult(), Automated by automated { - override val name = "Container Transfer of [$selection] from [${from.name}] to [${to.name}]" - - override fun SafeContext.onStart() { - val withdrawal = from.withdraw(selection) - val deposit = to.deposit(selection) - - val task = when { - withdrawal != null && deposit != null -> { - withdrawal.then { - deposit.finally { success() } - } - } - withdrawal != null -> { - withdrawal.finally { success() } - } - deposit != null -> { - deposit.finally { success() } - } - else -> null - } - - task?.execute(this@ContainerTransfer) - } - } - - data object NoSpace : TransferResult() { - override val name = "No space left in the target container" - - // ToDo: Needs inventory space resolver. compressing or disposing - override fun SafeContext.onStart() { - failure("No space left in the target container") - } - } - - data class MissingItems(val missing: Int) : TransferResult() { - override val name = "Missing $missing items" - - // ToDo: Find other satisfying permutations - override fun SafeContext.onStart() { - failure("Missing $missing items") - } - } -} diff --git a/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt b/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt index f553623be..170556d4c 100644 --- a/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt +++ b/src/main/kotlin/com/lambda/module/modules/combat/CrystalAura.kt @@ -23,18 +23,19 @@ import com.lambda.context.SafeContext import com.lambda.event.events.EntityEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.hotbar.HotbarRequest import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest import com.lambda.interaction.managers.rotating.Rotation.Companion.rotationTo import com.lambda.interaction.managers.rotating.RotationManager import com.lambda.interaction.managers.rotating.visibilty.VisibilityChecker.getVisibleSurfaces import com.lambda.interaction.material.StackSelection.Companion.selectStack import com.lambda.interaction.material.container.ContainerManager.transfer -import com.lambda.interaction.material.container.containers.MainHandContainer +import com.lambda.interaction.material.container.containers.HotbarContainer import com.lambda.interaction.material.container.containers.OffHandContainer import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.task.RootTask.run import com.lambda.threading.runSafe +import com.lambda.threading.runSafeAutomated import com.lambda.threading.runSafeGameScheduled import com.lambda.util.BlockUtils.blockState import com.lambda.util.Communication.info @@ -51,6 +52,7 @@ import com.lambda.util.math.flooredBlockPos import com.lambda.util.math.getHitVec import com.lambda.util.math.minus import com.lambda.util.math.plus +import com.lambda.util.player.SlotUtils.hotbarStacks import com.lambda.util.world.fastEntitySearch import net.minecraft.block.Blocks import net.minecraft.entity.Entity @@ -470,8 +472,14 @@ object CrystalAura : Module( if (swap && (swapHand == Hand.MAIN_HAND && player.mainHandStack.item != selection.item) || (swapHand == Hand.OFF_HAND && player.offHandStack.item != selection.item) - ) selection.transfer(when (swapHand) { Hand.MAIN_HAND -> MainHandContainer; Hand.OFF_HAND -> OffHandContainer }) - ?.run() + ) runSafeAutomated { + val crystalSlot = player.hotbarStacks.indexOfFirst { selection.filterStack(it) } + if (crystalSlot != -1) { + if (!HotbarRequest(crystalSlot, this).submit().done) return@runSafe + } + val swapTo = when (swapHand) { Hand.MAIN_HAND -> HotbarContainer; Hand.OFF_HAND -> OffHandContainer } + if (!selection.transfer(swapTo)) return@runSafe + } placeTimer.runSafeIfPassed(placeDelay.milliseconds) { placeInternal(this@Opportunity, swapHand) diff --git a/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt b/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt index b17420d68..e3ecd9642 100644 --- a/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt +++ b/src/main/kotlin/com/lambda/module/modules/debug/ContainerTest.kt @@ -23,7 +23,7 @@ import com.lambda.interaction.material.StackSelection.Companion.select import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run -import com.lambda.task.tasks.AcquireMaterial.Companion.acquire +import com.lambda.task.tasks.AcquireMaterialTask.Companion.acquire import net.minecraft.item.Items object ContainerTest : Module( diff --git a/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt b/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt index 107a925ad..f0c3868da 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/InventoryTweaks.kt @@ -27,8 +27,8 @@ import com.lambda.module.tag.ModuleTag import com.lambda.task.RootTask.run import com.lambda.task.Task import com.lambda.task.tasks.BuildTask.Companion.breakAndCollectBlock -import com.lambda.task.tasks.OpenContainer -import com.lambda.task.tasks.PlaceContainer +import com.lambda.task.tasks.OpenContainerTask +import com.lambda.task.tasks.PlaceContainerTask import com.lambda.util.item.ItemUtils.shulkerBoxes import net.minecraft.item.Items import net.minecraft.screen.ScreenHandler @@ -55,13 +55,13 @@ object InventoryTweaks : Module( listen { if (it.action != SlotActionType.PICKUP || it.button != 1) return@listen - val stack = it.screenHandler.getSlot(it.slot).stack - if (!(instantShulker && stack.item in shulkerBoxes) && !(instantEChest && stack.item == Items.ENDER_CHEST)) return@listen + val slot = it.screenHandler.getSlot(it.slot) + if (!(instantShulker && slot.stack.item in shulkerBoxes) && !(instantEChest && slot.stack.item == Items.ENDER_CHEST)) return@listen it.cancel() lastOpenScreen = null - placeAndOpen = PlaceContainer(stack, this@InventoryTweaks).then { placePos -> + placeAndOpen = PlaceContainerTask(slot, this@InventoryTweaks).then { placePos -> placedPos = placePos - OpenContainer(placePos, this@InventoryTweaks).finally { screenHandler -> + OpenContainerTask(placePos, this@InventoryTweaks).finally { screenHandler -> lastOpenScreen = screenHandler } }.run() diff --git a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt index 25ba05ffb..3ad052cd5 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Nuker.kt @@ -47,11 +47,7 @@ object Nuker : Module( private var task: Task<*>? = null init { - setDefaultAutomationConfig { - applyEdits { - inventoryConfig::immediateAccessOnly.edit { defaultValue(true) } - } - } + setDefaultAutomationConfig() onEnable { task = tickingBlueprint { diff --git a/src/main/kotlin/com/lambda/module/modules/player/Printer.kt b/src/main/kotlin/com/lambda/module/modules/player/Printer.kt index fe3e57480..587af9f41 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Printer.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Printer.kt @@ -54,7 +54,6 @@ object Printer : Module( editTyped(buildConfig::pathing, buildConfig::stayInRange) { defaultValue(false) } editTyped(breakConfig::efficientOnly, breakConfig::suitableToolsOnly) { defaultValue(false) } interactConfig::airPlace.edit { defaultValue(InteractConfig.AirPlaceMode.Grim) } - inventoryConfig::immediateAccessOnly.edit { defaultValue(true) } } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt b/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt index c5c1c4ea7..0209808a6 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/Scaffold.kt @@ -70,7 +70,6 @@ object Scaffold : Module( ::swapWithDisposables, ::providerPriority, ::storePriority, - ::immediateAccessOnly, ::accessShulkerBoxes, ::accessEnderChest, ::accessChests, diff --git a/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt b/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt index 585bfc669..393ee2558 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/StackReplenish.kt @@ -44,7 +44,6 @@ object StackReplenish : Module( applyEdits { hideAllGroupsExcept(inventoryConfig) inventoryConfig.apply { - ::immediateAccessOnly.edit { defaultValue(true) } hide(::disposables, ::swapWithDisposables, ::providerPriority, ::storePriority) } } diff --git a/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt b/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt index c1cc5e0f0..4480fb2fe 100644 --- a/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt +++ b/src/main/kotlin/com/lambda/module/modules/player/ToolSaver.kt @@ -28,7 +28,6 @@ import com.lambda.util.EnchantmentUtils.forEachEnchantment import com.lambda.util.EnchantmentUtils.getEnchantment import com.lambda.util.player.SlotUtils.hotbarSlots import com.lambda.util.player.SlotUtils.inventorySlots -import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack import net.minecraft.screen.slot.Slot @@ -67,7 +66,8 @@ object ToolSaver : Module( }.thenByDescending { it.stack.isEmpty }.thenByDescending { - (it.stack.item as? BlockItem)?.block in inventoryConfig.disposables + it.stack.item in inventoryConfig.disposables + }.thenByDescending { it.stack.isStackable } val swapWith = inventorySlots @@ -78,6 +78,8 @@ object ToolSaver : Module( endangered to swapWith } + if (swaps.isEmpty()) return@listen + inventoryRequest { swaps.forEach { pickup(it.first.id) diff --git a/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt b/src/main/kotlin/com/lambda/task/tasks/AcquireMaterialTask.kt similarity index 69% rename from src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt rename to src/main/kotlin/com/lambda/task/tasks/AcquireMaterialTask.kt index 60d70f2b4..95472a839 100644 --- a/src/main/kotlin/com/lambda/task/tasks/AcquireMaterial.kt +++ b/src/main/kotlin/com/lambda/task/tasks/AcquireMaterialTask.kt @@ -22,9 +22,11 @@ import com.lambda.context.SafeContext import com.lambda.interaction.material.StackSelection import com.lambda.interaction.material.container.ContainerManager import com.lambda.interaction.material.container.ContainerManager.findContainerWithMaterial +import com.lambda.interaction.material.container.containers.HotbarContainer import com.lambda.task.Task +import com.lambda.threading.runSafeAutomated -class AcquireMaterial @Ta5kBuilder constructor( +class AcquireMaterialTask @Ta5kBuilder constructor( val selection: StackSelection, automated: Automated ) : Task(), Automated by automated { @@ -32,17 +34,19 @@ class AcquireMaterial @Ta5kBuilder constructor( get() = "Acquiring $selection" override fun SafeContext.onStart() { - selection.findContainerWithMaterial() - ?.withdraw(selection) - ?.finally { - success(selection) - }?.execute(this@AcquireMaterial) - ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path + runSafeAutomated { + selection.findContainerWithMaterial() + ?.transferByTask(selection, HotbarContainer) + ?.finally { + success(selection) + }?.execute(this@AcquireMaterialTask) + ?: failure(ContainerManager.NoContainerFound(selection)) // ToDo: Create crafting path + } } companion object { @Ta5kBuilder fun Automated.acquire(selection: () -> StackSelection) = - AcquireMaterial(selection(), this) + AcquireMaterialTask(selection(), this) } } diff --git a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt index 2051800ec..70ed2ec50 100644 --- a/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/BuildTask.kt @@ -22,6 +22,7 @@ import com.lambda.Lambda.LOG import com.lambda.config.AutomationConfig.Companion.DEFAULT import com.lambda.config.groups.EatConfig.Companion.reasonEating import com.lambda.context.Automated +import com.lambda.context.AutomatedSafeContext import com.lambda.context.SafeContext import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen @@ -54,8 +55,7 @@ import com.lambda.task.tasks.EatTask.Companion.eat import com.lambda.threading.runSafeAutomated import com.lambda.util.Formatting.format import com.lambda.util.extension.Structure -import com.lambda.util.extension.inventorySlots -import com.lambda.util.item.ItemUtils.block +import com.lambda.util.extension.playerSlots import com.lambda.util.player.SlotUtils.hotbarAndInventoryStacks import net.minecraft.entity.ItemEntity import net.minecraft.util.math.BlockPos @@ -107,7 +107,7 @@ class BuildTask private constructor( if (collectDrops()) return@listen - simulateAndProcess() + runSafeAutomated { simulateAndProcess() } } listen { @@ -118,7 +118,7 @@ class BuildTask private constructor( } } - private fun SafeContext.simulateAndProcess() { + private fun AutomatedSafeContext.simulateAndProcess() { val results = runSafeAutomated { blueprint.structure.simulate() } DEFAULT.drawables = results @@ -141,7 +141,7 @@ class BuildTask private constructor( handleResult(bestResult, viableResults) } - private fun SafeContext.handleResult(result: BuildResult, allResults: List) { + private fun AutomatedSafeContext.handleResult(result: BuildResult, allResults: List) { if (result !is Dependent && result !is Contextual && pendingInteractions.isNotEmpty()) return when (result) { @@ -193,7 +193,7 @@ class BuildTask private constructor( is Resolvable -> { LOG.info("Resolving: ${result.name}") - result.resolve()?.execute(this@BuildTask) + result.resolve() } } } @@ -211,8 +211,8 @@ class BuildTask private constructor( } if (player.hotbarAndInventoryStacks.none { it.isEmpty }) { - val stackToThrow = player.currentScreenHandler.inventorySlots.firstOrNull { - it.stack.item.block in inventoryConfig.disposables + val stackToThrow = player.currentScreenHandler.playerSlots.firstOrNull { + it.stack.item in inventoryConfig.disposables } ?: run { failure("No item in inventory to throw but inventory is full and cant pick up item drop") return@let true @@ -262,11 +262,10 @@ class BuildTask private constructor( fun Automated.breakAndCollectBlock( blockPos: BlockPos, finishOnDone: Boolean = true, - collectDrops: Boolean = true, lifeMaintenance: Boolean = false ) = BuildTask( blockPos.toStructure(TargetState.Air).toBlueprint(), - finishOnDone, collectDrops, lifeMaintenance, this + finishOnDone, true, lifeMaintenance, this ) } } diff --git a/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt new file mode 100644 index 000000000..8121f76bb --- /dev/null +++ b/src/main/kotlin/com/lambda/task/tasks/ContainerTransferTask.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2026 Lambda + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.lambda.task.tasks + +import com.lambda.context.Automated +import com.lambda.event.events.TickEvent +import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.managers.inventory.InventoryRequest.Companion.inventoryRequest +import com.lambda.interaction.material.StackSelection +import com.lambda.interaction.material.container.ExternalContainer +import com.lambda.interaction.material.container.MaterialContainer +import com.lambda.interaction.material.container.containers.InventoryContainer +import com.lambda.task.Task +import com.lambda.threading.runSafeAutomated + +class ContainerTransferTask( + private var fromContainer: MaterialContainer, + private val toContainer: MaterialContainer, + private val stackSelection: StackSelection, + automated: Automated, + private val failIfNoMaterial: Boolean = false +) : Task(), Automated by automated { + override val name = "Transferring $stackSelection from $fromContainer to $toContainer" + + private var delegateTask: Task<*>? = null + + init { + listen { + if (delegateTask?.isCompleted == true) { + success() + return@listen + } + runSafeAutomated { + val fromExternal = fromContainer as? ExternalContainer + val slots = fromContainer.slots + val toSlots = toContainer.slots + fromExternal.takeIf { slots.isEmpty() }?.let { fromExternal -> + if (toContainer is ExternalContainer && toSlots.isEmpty()) { + fromContainer.transferByTask(stackSelection, InventoryContainer).finally { + fromContainer = InventoryContainer + }.execute(this@ContainerTransferTask) + return@listen + } + delegateTask = fromExternal.accessThen { + fromContainer.transferByTask(stackSelection, toContainer) + }?.execute(this@ContainerTransferTask) ?: run { + checkFail() + return@listen + } + return@listen + } + if (toContainer is ExternalContainer && toSlots.isEmpty()) { + delegateTask = toContainer.accessThen { + fromContainer.transferByTask(stackSelection, toContainer) + }?.execute(this@ContainerTransferTask) ?: run { + checkFail() + return@listen + } + return@listen + } + + fromContainer.getSlot(stackSelection)?.let { fromSlot -> + toContainer.getReplaceableSlot()?.let { toSlot -> + inventoryRequest { + if (fromContainer.swapMethodPriority > toContainer.swapMethodPriority) + with(fromContainer) { transfer(fromSlot, toSlot) } + else with(toContainer) { transfer(toSlot, fromSlot) } + onComplete { success() } + }.submit() + return@listen + } + } + + checkFail() + } + } + } + + + private fun checkFail(): Boolean = + failIfNoMaterial.also { + failure(NoMaterialAccessException(stackSelection)) + } + + private class NoMaterialAccessException(stackSelection: StackSelection) : IllegalStateException("Unable to access $stackSelection.") +} \ No newline at end of file diff --git a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt index b9e6c4f37..31c2deaf9 100644 --- a/src/main/kotlin/com/lambda/task/tasks/EatTask.kt +++ b/src/main/kotlin/com/lambda/task/tasks/EatTask.kt @@ -69,8 +69,9 @@ class EatTask @Ta5kBuilder constructor( mc.options.useKey.isPressed = false holdingUse = false } - foodFinder.transfer(MainHandContainer) - ?.execute(this@EatTask) ?: failure("No food found") + runSafeAutomated { + foodFinder.transfer(MainHandContainer) + } return@listen } eatStack = player.mainHandStack diff --git a/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt b/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt similarity index 84% rename from src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt rename to src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt index 3a8ea0d4a..734ae9d05 100644 --- a/src/main/kotlin/com/lambda/task/tasks/OpenContainer.kt +++ b/src/main/kotlin/com/lambda/task/tasks/OpenContainerTask.kt @@ -17,10 +17,12 @@ package com.lambda.task.tasks +import baritone.api.pathing.goals.GoalNear import com.lambda.context.Automated import com.lambda.event.events.InventoryEvent import com.lambda.event.events.TickEvent import com.lambda.event.listener.SafeListener.Companion.listen +import com.lambda.interaction.BaritoneManager import com.lambda.interaction.managers.rotating.IRotationRequest.Companion.rotationRequest import com.lambda.interaction.managers.rotating.visibilty.lookAtBlock import com.lambda.task.Task @@ -31,7 +33,7 @@ import net.minecraft.util.Hand import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -class OpenContainer @Ta5kBuilder constructor( +class OpenContainerTask @Ta5kBuilder constructor( private val blockPos: BlockPos, private val automated: Automated, private val waitForSlotLoad: Boolean = true, @@ -44,9 +46,10 @@ class OpenContainer @Ta5kBuilder constructor( private var inScope = 0 enum class State { - Scoping, Opening, SlotLoading; + Pathing, Scoping, Opening, SlotLoading; fun description(inScope: Int) = when (this) { + Pathing -> "Pathing closer" Scoping -> "Waiting for scope ($inScope)" Opening -> "Opening container" SlotLoading -> "Waiting for slots to load" @@ -79,9 +82,14 @@ class OpenContainer @Ta5kBuilder constructor( } listen { - if (containerState != State.Scoping) return@listen - - val checkedHit = runSafeAutomated { lookAtBlock(blockPos, sides) } ?: return@listen + if (containerState != State.Scoping && containerState != State.Pathing) return@listen + + val checkedHit = runSafeAutomated { lookAtBlock(blockPos, sides) } + ?: run { + containerState = State.Pathing + if (!BaritoneManager.isActive) BaritoneManager.setGoalAndPath(GoalNear(blockPos, 3)) + return@listen + } if (interactConfig.rotate && !rotationRequest { rotation(checkedHit.rotation) }.submit().done) return@listen interaction.interactBlock(player, Hand.MAIN_HAND, checkedHit.hit.blockResult ?: return@listen) diff --git a/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt b/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt similarity index 88% rename from src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt rename to src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt index 2274eec30..5cef9c8fe 100644 --- a/src/main/kotlin/com/lambda/task/tasks/PlaceContainer.kt +++ b/src/main/kotlin/com/lambda/task/tasks/PlaceContainerTask.kt @@ -36,15 +36,15 @@ import net.minecraft.block.ChestBlock import net.minecraft.entity.mob.ShulkerEntity import net.minecraft.item.ItemStack import net.minecraft.item.Items +import net.minecraft.screen.slot.Slot import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -class PlaceContainer @Ta5kBuilder constructor( - val stack: ItemStack, +class PlaceContainerTask @Ta5kBuilder constructor( + val slot: Slot, automated: Automated ) : Task(), Automated by automated { - private val startStack: ItemStack = stack.copy() - override val name: String get() = "Placing container ${startStack.name.string}" + override val name: String get() = "Placing container ${slot.stack.name.string}" override fun SafeContext.onStart() { val results = runSafeAutomated { @@ -53,29 +53,29 @@ class PlaceContainer @Ta5kBuilder constructor( .asSequence() .filter { !ManagerUtils.isPosBlocked(it) } .flatMap { - it.toStructure(TargetState.Stack(startStack)) + it.toStructure(TargetState.Stack(slot.stack)) .simulate() } } val options = results.filterIsInstance().filter { - canBeOpened(startStack, it.pos, it.context.hitResult.side) + canBeOpened(slot.stack, it.pos, it.context.hitResult.side) } + results.filterIsInstance() val containerPosition = options.filter { // ToDo: Check based on if we can move the player close enough rather than y level once the custom pathfinder is merged it.pos.y == player.blockPos.y }.minByOrNull { it.pos distSq player.pos }?.pos ?: run { - failure("Couldn't find a valid container placement position for ${startStack.name.string}") + failure("Couldn't find a valid container placement position for ${slot.stack.name.string}") return@onStart } containerPosition - .toStructure(TargetState.Stack(startStack)) + .toStructure(TargetState.Stack(slot.stack)) .toBlueprint() .build(finishOnDone = true, collectDrops = false) .finally { success(containerPosition) } - .execute(this@PlaceContainer) + .execute(this@PlaceContainerTask) } private fun SafeContext.canBeOpened( diff --git a/src/main/kotlin/com/lambda/util/extension/Screen.kt b/src/main/kotlin/com/lambda/util/extension/Screen.kt index 8fda215ea..1b6546832 100644 --- a/src/main/kotlin/com/lambda/util/extension/Screen.kt +++ b/src/main/kotlin/com/lambda/util/extension/Screen.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025 Lambda + * Copyright 2026 Lambda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,8 +25,8 @@ import net.minecraft.screen.ScreenHandler import net.minecraft.screen.slot.Slot val ScreenHandler.containerSlots: List get() = slots.filter { it.inventory is SimpleInventory } -val ScreenHandler.inventorySlots: List get() = slots.filter { it.inventory is PlayerInventory } +val ScreenHandler.playerSlots: List get() = slots.filter { it.inventory is PlayerInventory } val ScreenHandler.craftingSlots: List get() = slots.filter { it.inventory is CraftingInventory } val ScreenHandler.containerStacks: List get() = containerSlots.map { it.stack } -val ScreenHandler.inventoryStacks: List get() = inventorySlots.map { it.stack } +val ScreenHandler.playerStacks: List get() = playerSlots.map { it.stack } diff --git a/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt index ff59a6a59..df5745f2b 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemStackUtils.kt @@ -28,6 +28,7 @@ import net.minecraft.entity.attribute.EntityAttributeModifier import net.minecraft.entity.attribute.EntityAttributes import net.minecraft.item.ItemStack import net.minecraft.registry.entry.RegistryEntry +import net.minecraft.screen.slot.Slot object ItemStackUtils { // FixMe: Change this fucking retarded stuff when mojang wake up from their coma and realize they fucked this shit up @@ -74,8 +75,11 @@ object ItemStackUtils { val ItemStack.spaceLeft get() = maxCount - count val ItemStack.hasSpace get() = spaceLeft > 0 val List.spaceLeft get() = sumOf { it.spaceLeft } + val List.spaceLeft @JvmName("slotSpaceLeft") get() = sumOf { it.stack.spaceLeft } val List.empty: Int get() = count { it.isEmpty } + val List.empty: Int @JvmName("slotEmpty") get() = count { it.stack.isEmpty } val List.count: Int get() = if (isEmpty()) -1 else sumOf { it.count } + val List.count: Int @JvmName("slotCount") get() = if (isEmpty()) -1 else sumOf { it.stack.count } val List.copy: List get() = map { it.copy() } context(safeContext: SafeContext) diff --git a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt index c91b3776a..1d60c10ec 100644 --- a/src/main/kotlin/com/lambda/util/item/ItemUtils.kt +++ b/src/main/kotlin/com/lambda/util/item/ItemUtils.kt @@ -18,7 +18,6 @@ package com.lambda.util.item import net.minecraft.block.Block -import net.minecraft.block.Blocks import net.minecraft.component.DataComponentTypes import net.minecraft.item.BlockItem import net.minecraft.item.Item @@ -106,24 +105,24 @@ object ItemUtils { ) val defaultDisposables = setOf( - Blocks.DIRT, - Blocks.GRASS_BLOCK, - Blocks.COBBLESTONE, - Blocks.GRANITE, - Blocks.DIORITE, - Blocks.ANDESITE, - Blocks.SANDSTONE, - Blocks.RED_SANDSTONE, - Blocks.NETHERRACK, - Blocks.END_STONE, - Blocks.STONE, - Blocks.BASALT, - Blocks.BLACKSTONE, - Blocks.COBBLED_DEEPSLATE + Items.DIRT, + Items.GRASS_BLOCK, + Items.COBBLESTONE, + Items.GRANITE, + Items.DIORITE, + Items.ANDESITE, + Items.SANDSTONE, + Items.RED_SANDSTONE, + Items.NETHERRACK, + Items.END_STONE, + Items.STONE, + Items.BASALT, + Items.BLACKSTONE, + Items.COBBLED_DEEPSLATE ) val Item.block: Block get() = Block.getBlockFromItem(this) - val ItemStack.blockItem: BlockItem get() = (item as? BlockItem ?: Items.AIR) as BlockItem + val ItemStack.blockItem get() = item as? BlockItem val Item.nutrition: Int get() = components.get(DataComponentTypes.FOOD)?.nutrition ?: 0 diff --git a/src/main/kotlin/com/lambda/util/player/SlotUtils.kt b/src/main/kotlin/com/lambda/util/player/SlotUtils.kt index 2532b3a21..71815f3d5 100644 --- a/src/main/kotlin/com/lambda/util/player/SlotUtils.kt +++ b/src/main/kotlin/com/lambda/util/player/SlotUtils.kt @@ -29,21 +29,20 @@ import net.minecraft.screen.slot.SlotActionType object SlotUtils { val ClientPlayerEntity.allSlots get() = currentScreenHandler.slots.filter { it.inventory is PlayerInventory } val ClientPlayerEntity.armorSlots get() = allSlots.filterIsInstance() - val ClientPlayerEntity.inventorySlots: List get() { - return if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) emptyList() + val ClientPlayerEntity.inventorySlots: List get() = + if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) emptyList() else if (currentScreenHandler === playerScreenHandler) allSlots.subList(4, 31) else allSlots.subList(0, 27) - } - val ClientPlayerEntity.hotbarSlots: List get() { - return if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots - else if (currentScreenHandler === playerScreenHandler) allSlots.subList(30, 39) - else allSlots.subList(26, 35) - } - val ClientPlayerEntity.hotbarAndInventorySlots: List get() { - return if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots - else if (currentScreenHandler === playerScreenHandler) allSlots.subList(4, 39) - else allSlots.subList(0, 35) - } + val ClientPlayerEntity.hotbarSlots: List get() = + if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots + else if (currentScreenHandler === playerScreenHandler) allSlots.subList(31, 40) + else allSlots.subList(27, 36) + val ClientPlayerEntity.hotbarAndInventorySlots: List get() = + if (currentScreenHandler is CreativeInventoryScreen.CreativeScreenHandler) allSlots + else if (currentScreenHandler === playerScreenHandler) allSlots.subList(4, 40) + else allSlots.subList(0, 36) + val ClientPlayerEntity.mainHandSlots: List get() = listOf(hotbarSlots[inventory.selectedSlot]) + val ClientPlayerEntity.offHandSlots: List get() = if (currentScreenHandler === playerScreenHandler) listOf(allSlots.last()) else emptyList() val ClientPlayerEntity.allStacks: List get() = hotbarAndInventoryStacks + equipmentStacks val ClientPlayerEntity.equipmentStacks: List get() = inventory.equipment.map.map { it.value }