CreateMod/src/main/java/com/simibubi/create/content/schematics/client/SchematicHandler.java
2023-05-21 21:48:39 +02:00

416 lines
12 KiB
Java

package com.simibubi.create.content.schematics.client;
import java.util.List;
import java.util.Vector;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllKeys;
import com.simibubi.create.AllPackets;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.StructureTransform;
import com.simibubi.create.content.schematics.SchematicInstances;
import com.simibubi.create.content.schematics.SchematicItem;
import com.simibubi.create.content.schematics.SchematicWorld;
import com.simibubi.create.content.schematics.client.tools.ToolType;
import com.simibubi.create.content.schematics.packet.SchematicPlacePacket;
import com.simibubi.create.content.schematics.packet.SchematicSyncPacket;
import com.simibubi.create.foundation.outliner.AABBOutline;
import com.simibubi.create.foundation.render.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.client.gui.ForgeIngameGui;
import net.minecraftforge.client.gui.IIngameOverlay;
public class SchematicHandler {
private String displayedSchematic;
private SchematicTransformation transformation;
private AABB bounds;
private boolean deployed;
private boolean active;
private ToolType currentTool;
private static final int SYNC_DELAY = 10;
private int syncCooldown;
private int activeHotbarSlot;
private ItemStack activeSchematicItem;
private AABBOutline outline;
private Vector<SchematicRenderer> renderers;
private SchematicHotbarSlotOverlay overlay;
private ToolSelectionScreen selectionScreen;
private final IIngameOverlay overlayRenderer = this::renderOverlay;
public SchematicHandler() {
renderers = new Vector<>(3);
for (int i = 0; i < renderers.capacity(); i++)
renderers.add(new SchematicRenderer());
overlay = new SchematicHotbarSlotOverlay();
currentTool = ToolType.DEPLOY;
selectionScreen = new ToolSelectionScreen(ImmutableList.of(ToolType.DEPLOY), this::equip);
transformation = new SchematicTransformation();
}
public void tick() {
Minecraft mc = Minecraft.getInstance();
if (mc.gameMode.getPlayerMode() == GameType.SPECTATOR) {
if (active) {
active = false;
syncCooldown = 0;
activeHotbarSlot = 0;
activeSchematicItem = null;
renderers.forEach(r -> r.setActive(false));
}
return;
}
if (activeSchematicItem != null && transformation != null)
transformation.tick();
LocalPlayer player = mc.player;
ItemStack stack = findBlueprintInHand(player);
if (stack == null) {
active = false;
syncCooldown = 0;
if (activeSchematicItem != null && itemLost(player)) {
activeHotbarSlot = 0;
activeSchematicItem = null;
renderers.forEach(r -> r.setActive(false));
}
return;
}
if (!active || !stack.getTag()
.getString("File")
.equals(displayedSchematic))
init(player, stack);
if (!active)
return;
renderers.forEach(SchematicRenderer::tick);
if (syncCooldown > 0)
syncCooldown--;
if (syncCooldown == 1)
sync();
selectionScreen.update();
currentTool.getTool()
.updateSelection();
}
private void init(LocalPlayer player, ItemStack stack) {
loadSettings(stack);
displayedSchematic = stack.getTag()
.getString("File");
active = true;
if (deployed) {
setupRenderer();
ToolType toolBefore = currentTool;
selectionScreen = new ToolSelectionScreen(ToolType.getTools(player.isCreative()), this::equip);
if (toolBefore != null) {
selectionScreen.setSelectedElement(toolBefore);
equip(toolBefore);
}
} else
selectionScreen = new ToolSelectionScreen(ImmutableList.of(ToolType.DEPLOY), this::equip);
}
private void setupRenderer() {
StructureTemplate schematic = SchematicItem.loadSchematic(activeSchematicItem);
Vec3i size = schematic.getSize();
if (size.equals(Vec3i.ZERO))
return;
Level clientWorld = Minecraft.getInstance().level;
SchematicWorld w = new SchematicWorld(clientWorld);
SchematicWorld wMirroredFB = new SchematicWorld(clientWorld);
SchematicWorld wMirroredLR = new SchematicWorld(clientWorld);
StructurePlaceSettings placementSettings = new StructurePlaceSettings();
StructureTransform transform;
BlockPos pos;
pos = BlockPos.ZERO;
try {
schematic.placeInWorld(w, pos, pos, placementSettings, w.getRandom(), Block.UPDATE_CLIENTS);
} catch (Exception e) {
Minecraft.getInstance().player.displayClientMessage(Lang.translate("schematic.error")
.component(), false);
Create.LOGGER.error("Failed to load Schematic for Previewing", e);
return;
}
placementSettings.setMirror(Mirror.FRONT_BACK);
pos = BlockPos.ZERO.east(size.getX() - 1);
schematic.placeInWorld(wMirroredFB, pos, pos, placementSettings, wMirroredFB.getRandom(), Block.UPDATE_CLIENTS);
transform = new StructureTransform(placementSettings.getRotationPivot(), Axis.Y, Rotation.NONE,
placementSettings.getMirror());
for (BlockEntity be : wMirroredFB.getRenderedBlockEntities())
transform.apply(be);
placementSettings.setMirror(Mirror.LEFT_RIGHT);
pos = BlockPos.ZERO.south(size.getZ() - 1);
schematic.placeInWorld(wMirroredLR, pos, pos, placementSettings, wMirroredFB.getRandom(), Block.UPDATE_CLIENTS);
transform = new StructureTransform(placementSettings.getRotationPivot(), Axis.Y, Rotation.NONE,
placementSettings.getMirror());
for (BlockEntity be : wMirroredLR.getRenderedBlockEntities())
transform.apply(be);
renderers.get(0)
.display(w);
renderers.get(1)
.display(wMirroredFB);
renderers.get(2)
.display(wMirroredLR);
}
public void render(PoseStack ms, SuperRenderTypeBuffer buffer, Vec3 camera) {
boolean present = activeSchematicItem != null;
if (!active && !present)
return;
if (active) {
ms.pushPose();
currentTool.getTool()
.renderTool(ms, buffer, camera);
ms.popPose();
}
ms.pushPose();
transformation.applyTransformations(ms, camera);
if (!renderers.isEmpty()) {
float pt = AnimationTickHolder.getPartialTicks();
boolean lr = transformation.getScaleLR()
.getValue(pt) < 0;
boolean fb = transformation.getScaleFB()
.getValue(pt) < 0;
if (lr && !fb)
renderers.get(2)
.render(ms, buffer);
else if (fb && !lr)
renderers.get(1)
.render(ms, buffer);
else
renderers.get(0)
.render(ms, buffer);
}
if (active)
currentTool.getTool()
.renderOnSchematic(ms, buffer);
ms.popPose();
}
public void updateRenderers() {
for (SchematicRenderer renderer : renderers) {
renderer.update();
}
}
public IIngameOverlay getOverlayRenderer() {
return overlayRenderer;
}
public void renderOverlay(ForgeIngameGui gui, PoseStack poseStack, float partialTicks, int width, int height) {
if (Minecraft.getInstance().options.hideGui || !active)
return;
if (activeSchematicItem != null)
this.overlay.renderOn(poseStack, activeHotbarSlot);
currentTool.getTool()
.renderOverlay(gui, poseStack, partialTicks, width, height);
selectionScreen.renderPassive(poseStack, partialTicks);
}
public void onMouseInput(int button, boolean pressed) {
if (!active)
return;
if (!pressed || button != 1)
return;
Minecraft mc = Minecraft.getInstance();
if (mc.player.isShiftKeyDown())
return;
if (mc.hitResult instanceof BlockHitResult) {
BlockHitResult blockRayTraceResult = (BlockHitResult) mc.hitResult;
BlockState clickedBlock = mc.level.getBlockState(blockRayTraceResult.getBlockPos());
if (AllBlocks.SCHEMATICANNON.has(clickedBlock))
return;
if (AllBlocks.DEPLOYER.has(clickedBlock))
return;
}
currentTool.getTool()
.handleRightClick();
}
public void onKeyInput(int key, boolean pressed) {
if (!active)
return;
if (key != AllKeys.TOOL_MENU.getBoundCode())
return;
if (pressed && !selectionScreen.focused)
selectionScreen.focused = true;
if (!pressed && selectionScreen.focused) {
selectionScreen.focused = false;
selectionScreen.onClose();
}
}
public boolean mouseScrolled(double delta) {
if (!active)
return false;
if (selectionScreen.focused) {
selectionScreen.cycle((int) delta);
return true;
}
if (AllKeys.ctrlDown())
return currentTool.getTool()
.handleMouseWheel(delta);
return false;
}
private ItemStack findBlueprintInHand(Player player) {
ItemStack stack = player.getMainHandItem();
if (!AllItems.SCHEMATIC.isIn(stack))
return null;
if (!stack.hasTag())
return null;
activeSchematicItem = stack;
activeHotbarSlot = player.getInventory().selected;
return stack;
}
private boolean itemLost(Player player) {
for (int i = 0; i < Inventory.getSelectionSize(); i++) {
if (!player.getInventory()
.getItem(i)
.sameItem(activeSchematicItem))
continue;
if (!ItemStack.tagMatches(player.getInventory()
.getItem(i), activeSchematicItem))
continue;
return false;
}
return true;
}
public void markDirty() {
syncCooldown = SYNC_DELAY;
}
public void sync() {
if (activeSchematicItem == null)
return;
AllPackets.getChannel().sendToServer(new SchematicSyncPacket(activeHotbarSlot, transformation.toSettings(),
transformation.getAnchor(), deployed));
}
public void equip(ToolType tool) {
this.currentTool = tool;
currentTool.getTool()
.init();
}
public void loadSettings(ItemStack blueprint) {
CompoundTag tag = blueprint.getTag();
BlockPos anchor = BlockPos.ZERO;
StructurePlaceSettings settings = SchematicItem.getSettings(blueprint);
transformation = new SchematicTransformation();
deployed = tag.getBoolean("Deployed");
if (deployed)
anchor = NbtUtils.readBlockPos(tag.getCompound("Anchor"));
Vec3i size = NBTHelper.readVec3i(tag.getList("Bounds", Tag.TAG_INT));
bounds = new AABB(0, 0, 0, size.getX(), size.getY(), size.getZ());
outline = new AABBOutline(bounds);
outline.getParams()
.colored(0x6886c5)
.lineWidth(1 / 16f);
transformation.init(anchor, settings, bounds);
}
public void deploy() {
if (!deployed) {
List<ToolType> tools = ToolType.getTools(Minecraft.getInstance().player.isCreative());
selectionScreen = new ToolSelectionScreen(tools, this::equip);
}
deployed = true;
setupRenderer();
}
public String getCurrentSchematicName() {
return displayedSchematic != null ? displayedSchematic : "-";
}
public void printInstantly() {
AllPackets.getChannel().sendToServer(new SchematicPlacePacket(activeSchematicItem.copy()));
CompoundTag nbt = activeSchematicItem.getTag();
nbt.putBoolean("Deployed", false);
activeSchematicItem.setTag(nbt);
SchematicInstances.clearHash(activeSchematicItem);
renderers.forEach(r -> r.setActive(false));
active = false;
markDirty();
}
public boolean isActive() {
return active;
}
public AABB getBounds() {
return bounds;
}
public SchematicTransformation getTransformation() {
return transformation;
}
public boolean isDeployed() {
return deployed;
}
public ItemStack getActiveSchematicItem() {
return activeSchematicItem;
}
public AABBOutline getOutline() {
return outline;
}
}