Add Chemical Vat multiplace
- ability to place entire vat levels in 1 click
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
package com.drmangotea.tfmg.content.machinery.vat.base;
|
||||
|
||||
import com.drmangotea.tfmg.registry.TFMGBlockEntities;
|
||||
import com.simibubi.create.api.connectivity.ConnectivityHandler;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.items.ItemStackHandler;
|
||||
|
||||
public class VatItem extends BlockItem {
|
||||
|
||||
String vatType;
|
||||
|
||||
public VatItem(Block block, Properties properties, String vatType) {
|
||||
super(block, properties);
|
||||
this.vatType = vatType;
|
||||
}
|
||||
|
||||
public static class SteelVatItem extends VatItem {
|
||||
|
||||
public SteelVatItem(Block block, Properties properties) {
|
||||
super(block, properties,"tfmg:steel_vat");
|
||||
}
|
||||
}
|
||||
|
||||
public static class CastIronVatItem extends VatItem {
|
||||
|
||||
public CastIronVatItem(Block block, Properties properties) {
|
||||
super(block, properties,"tfmg:cast_iron_vat");
|
||||
}
|
||||
}
|
||||
|
||||
public static class FireproofVatItem extends VatItem {
|
||||
|
||||
public FireproofVatItem(Block block, Properties properties) {
|
||||
super(block, properties,"tfmg:firebrick_lined_vat");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult place(BlockPlaceContext ctx) {
|
||||
InteractionResult initialResult = super.place(ctx);
|
||||
if (!initialResult.consumesAction())
|
||||
return initialResult;
|
||||
tryMultiPlace(ctx);
|
||||
return initialResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean updateCustomBlockEntityTag(BlockPos pos, Level level, Player player,
|
||||
ItemStack stack, BlockState state) {
|
||||
CompoundTag nbt = stack.getTagElement("BlockEntityTag");
|
||||
if (nbt == null) return false;
|
||||
|
||||
// 1. Preserve multi-block structure data
|
||||
nbt.remove("Controller"); // Let the vat rebuild connectivity
|
||||
nbt.remove("LastKnownPos");
|
||||
|
||||
// 2. Process inventories (input/output slots)
|
||||
sanitizeInventory(nbt, "InputItems", 4);
|
||||
sanitizeInventory(nbt, "OutputItems", 4);
|
||||
|
||||
// 3. Handle fluids via Create's SmartFluidTankBehaviour
|
||||
if (nbt.contains("CreateData")) {
|
||||
CompoundTag createData = nbt.getCompound("CreateData");
|
||||
int capacity = VatBlockEntity.getCapacityMultiplier(); // Single-block capacity
|
||||
|
||||
// Clamp all tanks (both input and output)
|
||||
clampTankGroup(createData, "InputTanks", capacity);
|
||||
clampTankGroup(createData, "OutputTanks", capacity);
|
||||
}
|
||||
|
||||
// 4. Clean transient and vat-specific data
|
||||
nbt.remove("Luminosity");
|
||||
nbt.remove("Machines"); // Machines must re-attach after placement
|
||||
nbt.remove("Timer"); // Reset recipe progress
|
||||
nbt.remove("RecipeId");
|
||||
|
||||
return super.updateCustomBlockEntityTag(pos, level, player, stack, state);
|
||||
}
|
||||
|
||||
private void sanitizeInventory(CompoundTag nbt, String key, int slots) {
|
||||
if (nbt.contains(key)) {
|
||||
ItemStackHandler handler = new ItemStackHandler(slots);
|
||||
handler.deserializeNBT(nbt.getCompound(key));
|
||||
|
||||
// Validate items (prevent overstacked/illegal items)
|
||||
for (int i = 0; i < slots; i++) {
|
||||
ItemStack stack = handler.getStackInSlot(i);
|
||||
if (stack.getCount() > stack.getMaxStackSize()) {
|
||||
stack.setCount(stack.getMaxStackSize());
|
||||
}
|
||||
}
|
||||
nbt.put(key, handler.serializeNBT());
|
||||
}
|
||||
}
|
||||
|
||||
private void clampTankGroup(CompoundTag createData, String tankGroup, int capacity) {
|
||||
if (!createData.contains(tankGroup)) return;
|
||||
|
||||
ListTag tanks = createData.getList(tankGroup, Tag.TAG_COMPOUND);
|
||||
for (int i = 0; i < tanks.size(); i++) {
|
||||
CompoundTag tankTag = tanks.getCompound(i);
|
||||
if (tankTag.contains("TankContent")) {
|
||||
FluidStack fluid = FluidStack.loadFluidStackFromNBT(tankTag.getCompound("TankContent"));
|
||||
if (!fluid.isEmpty()) {
|
||||
fluid.setAmount(Math.min(capacity, fluid.getAmount()));
|
||||
tankTag.put("TankContent", fluid.writeToNBT(new CompoundTag()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryMultiPlace(BlockPlaceContext ctx) {
|
||||
|
||||
Player player = ctx.getPlayer();
|
||||
if (player == null)
|
||||
return;
|
||||
if (player.isShiftKeyDown())
|
||||
return;
|
||||
Direction face = ctx.getClickedFace();
|
||||
if (!face.getAxis()
|
||||
.isVertical())
|
||||
return;
|
||||
ItemStack stack = ctx.getItemInHand();
|
||||
Level world = ctx.getLevel();
|
||||
BlockPos pos = ctx.getClickedPos();
|
||||
BlockPos placedOnPos = pos.relative(face.getOpposite());
|
||||
BlockState placedOnState = world.getBlockState(placedOnPos);
|
||||
|
||||
if (!(placedOnState.getBlock() instanceof VatBlock placedOnVat)) return;
|
||||
if (!placedOnVat.vatType.equals(this.vatType)) return;
|
||||
|
||||
|
||||
VatBlockEntity vatAt = ConnectivityHandler.partAt(
|
||||
TFMGBlockEntities.CHEMICAL_VAT.get(), world, placedOnPos);
|
||||
if (vatAt == null)
|
||||
return;
|
||||
VatBlockEntity controllerTE = vatAt.getControllerBE();
|
||||
if (controllerTE == null)
|
||||
return;
|
||||
|
||||
int width = controllerTE.getWidth();
|
||||
if (width == 1)
|
||||
return;
|
||||
|
||||
int vatsToPlace = 0;
|
||||
BlockPos startPos = face == Direction.DOWN ? controllerTE.getBlockPos()
|
||||
.below()
|
||||
: controllerTE.getBlockPos()
|
||||
.above(controllerTE.getHeight());
|
||||
|
||||
if (startPos.getY() != pos.getY())
|
||||
return;
|
||||
|
||||
for (int xOffset = 0; xOffset < width; xOffset++) {
|
||||
for (int zOffset = 0; zOffset < width; zOffset++) {
|
||||
BlockPos offsetPos = startPos.offset(xOffset, 0, zOffset);
|
||||
BlockState blockState = world.getBlockState(offsetPos);
|
||||
if (VatBlock.isVat(blockState))
|
||||
continue;
|
||||
if (!blockState.canBeReplaced())
|
||||
return;
|
||||
vatsToPlace++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!player.isCreative() && stack.getCount() < vatsToPlace)
|
||||
return;
|
||||
|
||||
for (int xOffset = 0; xOffset < width; xOffset++) {
|
||||
for (int zOffset = 0; zOffset < width; zOffset++) {
|
||||
BlockPos offsetPos = startPos.offset(xOffset, 0, zOffset);
|
||||
BlockState blockState = world.getBlockState(offsetPos);
|
||||
if (VatBlock.isVat(blockState))
|
||||
continue;
|
||||
BlockPlaceContext context = BlockPlaceContext.at(ctx, offsetPos, face);
|
||||
player.getPersistentData()
|
||||
.putBoolean("SilenceTankSound", true);
|
||||
super.place(context);
|
||||
player.getPersistentData()
|
||||
.remove("SilenceTankSound");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.drmangotea.tfmg.content.machinery.vat.base;
|
||||
|
||||
import com.drmangotea.tfmg.base.TFMGSpriteShifts;
|
||||
import com.simibubi.create.api.connectivity.ConnectivityHandler;
|
||||
import com.simibubi.create.content.fluids.tank.FluidTankCTBehaviour;
|
||||
import com.simibubi.create.foundation.block.connected.CTModel;
|
||||
import com.simibubi.create.foundation.block.connected.CTSpriteShiftEntry;
|
||||
import net.createmod.catnip.data.Iterate;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
import net.minecraftforge.client.model.data.ModelProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class VatModel extends CTModel {
|
||||
protected static final ModelProperty<CullData> CULL_PROPERTY = new ModelProperty<>();
|
||||
|
||||
public static VatModel steelVat(BakedModel originalModel) {
|
||||
return new VatModel(originalModel, TFMGSpriteShifts.STEEL_VAT, TFMGSpriteShifts.STEEL_VAT_TOP,
|
||||
TFMGSpriteShifts.STEEL_VAT_INNER);
|
||||
}
|
||||
public static VatModel castIronVat(BakedModel originalModel) {
|
||||
return new VatModel(originalModel, TFMGSpriteShifts.CAST_IRON_VAT, TFMGSpriteShifts.CAST_IRON_VAT_TOP,
|
||||
TFMGSpriteShifts.CAST_IRON_VAT_INNER);
|
||||
}
|
||||
public static VatModel fireproofVat(BakedModel originalModel) {
|
||||
return new VatModel(originalModel, TFMGSpriteShifts.FIREPROOF_VAT, TFMGSpriteShifts.STEEL_VAT_TOP,
|
||||
TFMGSpriteShifts.STEEL_VAT_INNER);
|
||||
}
|
||||
|
||||
|
||||
private VatModel(BakedModel originalModel, CTSpriteShiftEntry side, CTSpriteShiftEntry top,
|
||||
CTSpriteShiftEntry inner) {
|
||||
super(originalModel, new FluidTankCTBehaviour(side, top, inner));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ModelData.Builder gatherModelData(ModelData.Builder builder, BlockAndTintGetter world, BlockPos pos, BlockState state,
|
||||
ModelData blockEntityData) {
|
||||
super.gatherModelData(builder, world, pos, state, blockEntityData);
|
||||
CullData cullData = new CullData();
|
||||
for (Direction d : Iterate.horizontalDirections)
|
||||
cullData.setCulled(d, ConnectivityHandler.isConnected(world, pos, pos.relative(d)));
|
||||
return builder.with(CULL_PROPERTY, cullData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(BlockState state, Direction side, RandomSource rand, ModelData extraData, RenderType renderType) {
|
||||
if (side != null)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<BakedQuad> quads = new ArrayList<>();
|
||||
for (Direction d : Iterate.directions) {
|
||||
if (extraData.has(CULL_PROPERTY) && extraData.get(CULL_PROPERTY)
|
||||
.isCulled(d))
|
||||
continue;
|
||||
quads.addAll(super.getQuads(state, d, rand, extraData, renderType));
|
||||
}
|
||||
quads.addAll(super.getQuads(state, null, rand, extraData, renderType));
|
||||
return quads;
|
||||
}
|
||||
private class CullData {
|
||||
boolean[] culledFaces;
|
||||
|
||||
public CullData() {
|
||||
culledFaces = new boolean[4];
|
||||
Arrays.fill(culledFaces, false);
|
||||
}
|
||||
|
||||
void setCulled(Direction face, boolean cull) {
|
||||
if (face.getAxis()
|
||||
.isVertical())
|
||||
return;
|
||||
culledFaces[face.get2DDataValue()] = cull;
|
||||
}
|
||||
|
||||
boolean isCulled(Direction face) {
|
||||
if (face.getAxis()
|
||||
.isVertical())
|
||||
return false;
|
||||
return culledFaces[face.get2DDataValue()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,8 @@ import com.drmangotea.tfmg.content.machinery.oil_processing.pumpjack.pumpjack.ha
|
||||
import com.drmangotea.tfmg.content.machinery.oil_processing.surface_scanner.SurfaceScannerBlock;
|
||||
import com.drmangotea.tfmg.content.machinery.vat.base.VatBlock;
|
||||
import com.drmangotea.tfmg.content.machinery.vat.base.VatGenerator;
|
||||
import com.drmangotea.tfmg.content.machinery.vat.base.VatItem;
|
||||
import com.drmangotea.tfmg.content.machinery.vat.base.VatModel;
|
||||
import com.drmangotea.tfmg.content.machinery.vat.electrode_holder.ElectrodeHolderBlock;
|
||||
import com.drmangotea.tfmg.content.machinery.vat.industrial_mixer.IndustrialMixerBlock;
|
||||
import com.simibubi.create.AllMountedStorageTypes;
|
||||
@@ -457,10 +459,10 @@ public class TFMGBlocks {
|
||||
.properties(p -> p.isRedstoneConductor((p1, p2, p3) -> true))
|
||||
.transform(pickaxeOnly())
|
||||
.blockstate(new VatGenerator()::generate)
|
||||
.onRegister(CreateRegistrate.blockModel(() -> SteelFluidTankModel::steelVat))
|
||||
.onRegister(CreateRegistrate.blockModel(() -> VatModel::steelVat))
|
||||
.addLayer(() -> RenderType::cutoutMipped)
|
||||
.item(SteelTankItem::new)
|
||||
.model(AssetLookup.customBlockItemModel("_", "block_single_window"))
|
||||
.item(VatItem.SteelVatItem::new)
|
||||
.model(AssetLookup.customBlockItemModel("_", "block_single"))
|
||||
.build()
|
||||
.register();
|
||||
public static final BlockEntry<VatBlock> CAST_IRON_CHEMICAL_VAT =
|
||||
@@ -471,10 +473,10 @@ public class TFMGBlocks {
|
||||
.properties(p -> p.isRedstoneConductor((p1, p2, p3) -> true))
|
||||
.transform(pickaxeOnly())
|
||||
.blockstate(new VatGenerator()::generate)
|
||||
.onRegister(CreateRegistrate.blockModel(() -> SteelFluidTankModel::castIronVat))
|
||||
.onRegister(CreateRegistrate.blockModel(() -> VatModel::castIronVat))
|
||||
.addLayer(() -> RenderType::cutoutMipped)
|
||||
.item(SteelTankItem::new)
|
||||
.model(AssetLookup.customBlockItemModel("_", "block_single_window"))
|
||||
.item(VatItem.CastIronVatItem::new)
|
||||
.model(AssetLookup.customBlockItemModel("_", "block_single"))
|
||||
.build()
|
||||
.register();
|
||||
public static final BlockEntry<VatBlock> FIREPROOF_CHEMICAL_VAT =
|
||||
@@ -485,9 +487,9 @@ public class TFMGBlocks {
|
||||
.properties(p -> p.isRedstoneConductor((p1, p2, p3) -> true))
|
||||
.transform(pickaxeOnly())
|
||||
.blockstate(new VatGenerator()::generate)
|
||||
.onRegister(CreateRegistrate.blockModel(() -> SteelFluidTankModel::fireproofVat))
|
||||
.onRegister(CreateRegistrate.blockModel(() -> VatModel::fireproofVat))
|
||||
.addLayer(() -> RenderType::cutoutMipped)
|
||||
.item(SteelTankItem::new)
|
||||
.item(VatItem.FireproofVatItem::new)
|
||||
.model(AssetLookup.customBlockItemModel("_", "block_single"))
|
||||
.build()
|
||||
.register();
|
||||
|
||||
Reference in New Issue
Block a user