Trekking Inventory

- Contraption storage now accepts more chests and barrels from other mods
- Players can now open chests and barrels on assembled contraptions
- Added a `#contraption_inventory_deny` block tag as a way to opt out
This commit is contained in:
simibubi 2023-03-22 22:50:00 +01:00
parent 379b8d1f26
commit 36cd43997d
9 changed files with 168 additions and 8 deletions

View file

@ -567,7 +567,7 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
f1bedeb51c35e70a2247178634e61ea637a6622e assets/create/lang/en_ud.json
59bd0d1e0f74f1dbfd2443b3e6cb8c683b57827a assets/create/lang/en_us.json
59fd557ef593efa3c7b783195ec5dc789eae1834 assets/create/lang/en_us.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
@ -5652,6 +5652,7 @@ ac265a674626e0e832330086fd18fe0be37fc327 data/create/recipes/weathered_copper_ti
5942a571f79c40524bbf408775cf91de4715f2b6 data/create/recipes/weathered_copper_tile_stairs_from_weathered_copper_tiles_stonecutting.json
2a2700b43614f86d3294726595cb28ed7dca4387 data/create/tags/blocks/brittle.json
d99d5c67bdffff60789a19bd51a5c5267c75e0a4 data/create/tags/blocks/casing.json
74700d556ca80c7a1db5fd4efb09c3ddb26cad66 data/create/tags/blocks/contraption_inventory_deny.json
bc203f09dd7f48965d146d0bd035fb904cb75e7d data/create/tags/blocks/copycat_allow.json
d4a3b66f4b763b9a2dcdea74b7273f0ae85cb335 data/create/tags/blocks/copycat_deny.json
2b4c93e5a752ebf54217594766f30d8d60cb4343 data/create/tags/blocks/fan_transparent.json

View file

@ -1048,6 +1048,7 @@
"create.minecart_coupling.removed": "Removed all couplings from minecart",
"create.minecart_coupling.too_far": "Minecarts are too far apart",
"create.contraptions.moving_container": "Moving %1$s",
"create.contraptions.movement_mode": "Movement Mode",
"create.contraptions.movement_mode.move_place": "Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "Place only in Starting Position",

View file

@ -0,0 +1,4 @@
{
"replace": false,
"values": []
}

View file

@ -87,6 +87,7 @@ public class AllTags {
WRENCH_PICKUP,
COPYCAT_ALLOW,
COPYCAT_DENY,
CONTRAPTION_INVENTORY_DENY,
RELOCATION_NOT_SUPPORTED(FORGE),
WG_STONE(FORGE),

View file

@ -278,9 +278,12 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
InteractionHand interactionHand) {
int indexOfSeat = contraption.getSeats()
.indexOf(localPos);
if (indexOfSeat == -1 || AllItems.WRENCH.isIn(player.getItemInHand(interactionHand)))
return contraption.interactors.containsKey(localPos) && contraption.interactors.get(localPos)
.handlePlayerInteraction(player, interactionHand, localPos, this);
if (indexOfSeat == -1 || AllItems.WRENCH.isIn(player.getItemInHand(interactionHand))) {
if (contraption.interactors.containsKey(localPos))
return contraption.interactors.get(localPos)
.handlePlayerInteraction(player, interactionHand, localPos, this);
return contraption.storage.handlePlayerStorageInteraction(contraption, player, localPos);
}
if (player.isPassenger())
return false;

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import com.simibubi.create.AllBlockEntityTypes;
import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterBlockEntity;
import com.simibubi.create.content.contraptions.processing.ProcessingInventory;
import com.simibubi.create.content.logistics.block.inventories.BottomlessItemHandler;
@ -15,6 +16,7 @@ import net.minecraft.world.level.block.entity.BarrelBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
@ -47,9 +49,30 @@ public class MountedStorage {
if (be instanceof ItemVaultBlockEntity)
return true;
LazyOptional<IItemHandler> capability = be.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
IItemHandler handler = capability.orElse(null);
return handler instanceof ItemStackHandler && !(handler instanceof ProcessingInventory);
try {
LazyOptional<IItemHandler> capability = be.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY);
IItemHandler handler = capability.orElse(null);
if (handler instanceof ItemStackHandler)
return !(handler instanceof ProcessingInventory);
return canUseModdedInventory(be, handler);
} catch (Exception e) {
return false;
}
}
public static boolean canUseModdedInventory(BlockEntity be, IItemHandler handler) {
if (!(handler instanceof IItemHandlerModifiable validItemHandler))
return false;
BlockState blockState = be.getBlockState();
if (AllBlockTags.CONTRAPTION_INVENTORY_DENY.matches(blockState))
return false;
// There doesn't appear to be much of a standard for tagging chests/barrels
String blockId = blockState.getBlock()
.getRegistryName()
.getPath();
return blockId.endsWith("_chest") || blockId.endsWith("_barrel");
}
public MountedStorage(BlockEntity be) {
@ -182,7 +205,7 @@ public class MountedStorage {
public boolean isValid() {
return valid;
}
public boolean canUseForFuel() {
return !noFuel;
}

View file

@ -0,0 +1,68 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.List;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.RecipeWrapper;
public class MountedStorageInteraction {
public static final List<MenuType<?>> menus = ImmutableList.of(MenuType.GENERIC_9x1, MenuType.GENERIC_9x2,
MenuType.GENERIC_9x3, MenuType.GENERIC_9x4, MenuType.GENERIC_9x5, MenuType.GENERIC_9x6);
public static MenuProvider createMenuProvider(Component displayName, IItemHandlerModifiable handler,
int slotCount, Supplier<Boolean> stillValid) {
int rows = Mth.clamp(slotCount / 9, 1, 6);
MenuType<?> menuType = menus.get(rows - 1);
Component menuName = Lang.translateDirect("contraptions.moving_container", displayName);
return new MenuProvider() {
@Override
public AbstractContainerMenu createMenu(int pContainerId, Inventory pPlayerInventory, Player pPlayer) {
return new ChestMenu(menuType, pContainerId, pPlayerInventory, new StorageInteractionContainer(handler, stillValid),
rows);
}
@Override
public Component getDisplayName() {
return menuName;
}
};
}
public static class StorageInteractionContainer extends RecipeWrapper {
private Supplier<Boolean> stillValid;
public StorageInteractionContainer(IItemHandlerModifiable inv, Supplier<Boolean> stillValid) {
super(inv);
this.stillValid = stillValid;
}
@Override
public boolean stillValid(Player player) {
return stillValid.get();
}
@Override
public int getMaxStackSize() {
return 64;
}
}
}

View file

@ -6,27 +6,38 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption.ContraptionInvWrapper;
import com.simibubi.create.content.contraptions.fluids.tank.FluidTankBlockEntity;
import com.simibubi.create.foundation.fluid.CombinedTankWrapper;
import com.simibubi.create.foundation.utility.Components;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.templates.FluidTank;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.wrapper.CombinedInvWrapper;
public class MountedStorageManager {
@ -202,4 +213,50 @@ public class MountedStorageManager {
return fluidInventory;
}
public boolean handlePlayerStorageInteraction(Contraption contraption, Player player, BlockPos localPos) {
if (player.level.isClientSide()) {
BlockEntity localBE = contraption.presentBlockEntities.get(localPos);
return MountedStorage.canUseAsStorage(localBE);
}
MountedStorageManager storageManager = contraption.getStorageForSpawnPacket();
MountedStorage storage = storageManager.storage.get(localPos);
if (storage == null || storage.getItemHandler() == null)
return false;
IItemHandlerModifiable handler = storage.getItemHandler();
StructureBlockInfo info = contraption.getBlocks()
.get(localPos);
if (info != null && info.state.hasProperty(ChestBlock.TYPE)) {
ChestType chestType = info.state.getValue(ChestBlock.TYPE);
Direction facing = info.state.getOptionalValue(ChestBlock.FACING)
.orElse(Direction.SOUTH);
Direction connectedDirection =
chestType == ChestType.LEFT ? facing.getClockWise() : facing.getCounterClockWise();
if (chestType != ChestType.SINGLE) {
MountedStorage storage2 = storageManager.storage.get(localPos.relative(connectedDirection));
if (storage2 != null && storage2.getItemHandler() != null)
handler = chestType == ChestType.RIGHT ? new CombinedInvWrapper(handler, storage2.getItemHandler())
: new CombinedInvWrapper(storage2.getItemHandler(), handler);
}
}
int slotCount = handler.getSlots();
if (slotCount == 0)
return false;
if (slotCount % 9 != 0)
return false;
Supplier<Boolean> stillValid = () -> contraption.entity.isAlive()
&& player.distanceToSqr(contraption.entity.toGlobalVector(Vec3.atCenterOf(localPos), 0)) < 64;
Component name = info != null ? info.state.getBlock()
.getName() : Components.literal("Container");
player.openMenu(MountedStorageInteraction.createMenuProvider(name, handler, slotCount, stillValid));
Vec3 soundPos = contraption.entity.toGlobalVector(Vec3.atCenterOf(localPos), 0);
player.level.playSound(null, new BlockPos(soundPos), SoundEvents.BARREL_OPEN, SoundSource.BLOCKS, 0.75f, 1f);
return true;
}
}

View file

@ -180,6 +180,8 @@
"create.minecart_coupling.removed": "Removed all couplings from minecart",
"create.minecart_coupling.too_far": "Minecarts are too far apart",
"create.contraptions.moving_container": "Moving %1$s",
"create.contraptions.movement_mode": "Movement Mode",
"create.contraptions.movement_mode.move_place": "Always Place when Stopped",
"create.contraptions.movement_mode.move_place_returned": "Place only in Starting Position",