Blueprint Placement Tools

- New tools for modifying the blueprints location and orientation
- Fixed client classes loading on physical servers
This commit is contained in:
simibubi 2019-07-15 12:10:57 +02:00
parent c6c9a8f8eb
commit 56139aab20
39 changed files with 1309 additions and 125 deletions

View file

@ -6,6 +6,8 @@ import com.simibubi.create.item.ItemWandSymmetry;
import net.minecraft.item.Item;
import net.minecraft.item.Item.Properties;
import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.registries.IForgeRegistry;
public enum AllItems {
@ -39,6 +41,7 @@ public enum AllItems {
return stack.getItem() == item;
}
@OnlyIn(Dist.CLIENT)
public static void initColorHandlers() {
}

View file

@ -10,6 +10,8 @@ import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.ClientRegistry;
@ -42,10 +44,12 @@ public enum AllTileEntities {
}
}
@OnlyIn(Dist.CLIENT)
public static void registerRenderers() {
bind(SchematicannonTileEntity.class, new SchematicannonRenderer());
}
@OnlyIn(Dist.CLIENT)
private static <T extends TileEntity> void bind(Class<T> clazz, TileEntityRenderer<? super T> renderer) {
ClientRegistry.bindTileEntitySpecialRenderer(clazz, renderer);
}

View file

@ -3,10 +3,13 @@ package com.simibubi.create;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.simibubi.create.gui.Keyboard;
import com.simibubi.create.networking.Packets;
import com.simibubi.create.schematic.BlueprintHandler;
import com.simibubi.create.schematic.SchematicHologram;
import net.minecraft.block.Block;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraftforge.api.distmarker.Dist;
@ -15,6 +18,7 @@ import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
@ -37,6 +41,8 @@ public class Create {
@OnlyIn(Dist.CLIENT)
public static ClientSchematicLoader cSchematicLoader;
@OnlyIn(Dist.CLIENT)
public static KeyBinding TOOL_MENU;
public static ServerSchematicLoader sSchematicLoader;
@ -47,17 +53,24 @@ public class Create {
}
private void clientInit(FMLClientSetupEvent event) {
AllItems.initColorHandlers();
AllTileEntities.registerRenderers();
cSchematicLoader = new ClientSchematicLoader();
sSchematicLoader = new ServerSchematicLoader();
new SchematicHologram();
// ScrollFixer.init();
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
AllItems.initColorHandlers();
AllTileEntities.registerRenderers();
cSchematicLoader = new ClientSchematicLoader();
new SchematicHologram();
new BlueprintHandler();
ScrollFixer.init();
ScrollFixer.addMouseWheelListener(BlueprintHandler.instance::onScroll);
TOOL_MENU = new KeyBinding("Tool Menu (Hold)", Keyboard.LALT, NAME);
ClientRegistry.registerKeyBinding(TOOL_MENU);
});
}
private void init(final FMLCommonSetupEvent event) {
Packets.registerPackets();
DistExecutor.runWhenOn(Dist.CLIENT, () -> AllContainers::registerScreenFactories);
sSchematicLoader = new ServerSchematicLoader();
}
@OnlyIn(Dist.CLIENT)

View file

@ -10,14 +10,13 @@ import java.util.Map;
import com.simibubi.create.block.SchematicTableContainer;
import com.simibubi.create.block.SchematicTableTileEntity;
import com.simibubi.create.item.ItemBlueprint;
import com.simibubi.create.networking.PacketSchematicUpload.DimensionPos;
import com.simibubi.create.utility.FilesHelper;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
public class ServerSchematicLoader {
@ -111,13 +110,7 @@ public class ServerSchematicLoader {
return;
tileEntity.inputStack = ItemStack.EMPTY;
ItemStack blueprint = new ItemStack(AllItems.BLUEPRINT.get());
blueprint.setDisplayName(new StringTextComponent(TextFormatting.RESET + "" + TextFormatting.WHITE
+ "Blueprint (" + TextFormatting.GOLD + schematic + TextFormatting.WHITE + ")"));
blueprint.getTag().putString("Owner", player.getName().getFormattedText());
blueprint.getTag().putString("File", schematic);
tileEntity.outputStack = blueprint;
tileEntity.outputStack = ItemBlueprint.create(schematic, player.getName().getFormattedText());
dimpos.world.notifyBlockUpdate(dimpos.pos, blockState, blockState, 3);

View file

@ -1,12 +1,9 @@
package com.simibubi.create.block;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.HorizontalBlock;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.inventory.container.INamedContainerProvider;
@ -19,8 +16,7 @@ import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
@SuppressWarnings("deprecation")
public class SchematicTableBlock extends HorizontalBlock implements ITileEntityProvider {
public class SchematicTableBlock extends HorizontalBlock {
public SchematicTableBlock() {
super(Properties.from(Blocks.OAK_PLANKS));
@ -43,17 +39,18 @@ public class SchematicTableBlock extends HorizontalBlock implements ITileEntityP
}
@Override
public boolean hasTileEntity() {
public boolean hasTileEntity(BlockState state) {
return true;
}
@Override
public boolean onBlockActivated(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn,
BlockRayTraceResult hit) {
if (worldIn.isRemote) {
return true;
} else {
INamedContainerProvider inamedcontainerprovider = this.getContainer(state, worldIn, pos);
INamedContainerProvider inamedcontainerprovider = (INamedContainerProvider) worldIn.getTileEntity(pos);
if (inamedcontainerprovider != null) {
player.openContainer(inamedcontainerprovider);
}
@ -61,9 +58,9 @@ public class SchematicTableBlock extends HorizontalBlock implements ITileEntityP
return true;
}
}
@Override
public TileEntity createNewTileEntity(IBlockReader worldIn) {
public TileEntity createTileEntity(BlockState state, IBlockReader world) {
return new SchematicTableTileEntity();
}
@ -80,16 +77,4 @@ public class SchematicTableBlock extends HorizontalBlock implements ITileEntityP
}
public boolean eventReceived(BlockState state, World worldIn, BlockPos pos, int id, int param) {
super.eventReceived(state, worldIn, pos, id, param);
TileEntity tileentity = worldIn.getTileEntity(pos);
return tileentity == null ? false : tileentity.receiveClientEvent(id, param);
}
@Nullable
public INamedContainerProvider getContainer(BlockState state, World worldIn, BlockPos pos) {
TileEntity tileentity = worldIn.getTileEntity(pos);
return tileentity instanceof INamedContainerProvider ? (INamedContainerProvider) tileentity : null;
}
}

View file

@ -45,14 +45,14 @@ public class SchematicTableContainer extends Container {
this.player = inv.player;
this.te = te;
inputSlot = new Slot(tableInventory, 0, -9, 15) {
inputSlot = new Slot(tableInventory, 0, -9, 40) {
@Override
public boolean isItemValid(ItemStack stack) {
return AllItems.EMPTY_BLUEPRINT.typeOf(stack);
}
};
outputSlot = new Slot(tableInventory, 1, 75, 15) {
outputSlot = new Slot(tableInventory, 1, 75, 40) {
@Override
public boolean isItemValid(ItemStack stack) {
return false;
@ -72,12 +72,12 @@ public class SchematicTableContainer extends Container {
tableInventory.openInventory(inv.player);
for (int l = 0; l < 3; ++l) {
for (int j1 = 0; j1 < 9; ++j1) {
this.addSlot(new Slot(inv, j1 + l * 9 + 9, -8 + j1 * 18, 77 + l * 18));
this.addSlot(new Slot(inv, j1 + l * 9 + 9, -8 + j1 * 18, 102 + l * 18));
}
}
for (int i1 = 0; i1 < 9; ++i1) {
this.addSlot(new Slot(inv, i1, -8 + i1 * 18, 135));
this.addSlot(new Slot(inv, i1, -8 + i1 * 18, 160));
}
}

View file

@ -32,6 +32,8 @@ import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
@ -262,19 +264,27 @@ public class SchematicannonTileEntity extends TileEntitySynced implements ITicka
anchor = null;
reader = null;
missingBlock = false;
target = getPos().add(1, 0, 0);
world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), SoundEvents.BLOCK_NOTE_BLOCK_BELL,
SoundCategory.BLOCKS, 1, .7f);
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 2);
return;
}
state = reader.getBlockState(anchor.add(currentPos));
} while (state.getBlock() == Blocks.AIR);
target = anchor.add(currentPos);
missingBlock = false;
// Update orientation
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 3);
world.notifyBlockUpdate(getPos(), getBlockState(), getBlockState(), 2);
if (target.withinDistance(getPos(), 2f)) {
return;
}
if (creative) {
launchBlock(currentPos.add(anchor), state);
missingBlock = false;
return;
}
@ -285,7 +295,6 @@ public class SchematicannonTileEntity extends TileEntitySynced implements ITicka
// Overwrite in case its rotated
launchBlock(target, state);
missingBlock = false;
}
// Search for required item

View file

@ -8,7 +8,10 @@ import com.simibubi.create.gui.widgets.AbstractSimiWidget;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
public abstract class AbstractSimiScreen extends Screen {
protected int sWidth, sHeight;

View file

@ -0,0 +1,20 @@
package com.simibubi.create.gui;
import com.mojang.blaze3d.platform.GlStateManager;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.AbstractGui;
public class BlueprintHotbarOverlay extends AbstractGui {
public void renderOn(int slot) {
MainWindow mainWindow = Minecraft.getInstance().mainWindow;
int x = mainWindow.getScaledWidth() / 2 - 92;
int y = mainWindow.getScaledHeight() - 23;
GlStateManager.enableBlend();
GlStateManager.enableAlphaTest();
GuiResources.BLUEPRINT_SLOT.draw(this, x + 20 * slot, y);
}
}

View file

@ -1,5 +0,0 @@
package com.simibubi.create.gui;
public class GuiHandler {
}

View file

@ -2,11 +2,14 @@ package com.simibubi.create.gui;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
@EventBusSubscriber
@OnlyIn(Dist.CLIENT)
@EventBusSubscriber(value = Dist.CLIENT)
public class GuiOpener {
private static Screen openedGuiNextTick;

View file

@ -27,6 +27,8 @@ public enum GuiResources {
INDICATOR_RED("widgets.png", 36, 23, 18, 5),
GRAY("background.png", 0, 0, 16, 16),
BLUEPRINT_SLOT("widgets.png", 90, 0, 24, 24),
// Icons
ICON_NONE("icons.png", 16, 16, 16, 16),
ICON_ADD("icons.png", 16, 16),

View file

@ -1,7 +1,8 @@
package com.simibubi.create.utility;
package com.simibubi.create.gui;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.Minecraft;
import net.minecraft.client.util.InputMappings;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@ -24,7 +25,7 @@ public class Keyboard {
public static final int G = 71;
public static boolean isKeyDown(int key) {
return InputMappings.isKeyDown(Minecraft.getInstance().mainWindow.getHandle(), key);
return GLFW.glfwGetKey(Minecraft.getInstance().mainWindow.getHandle(), key) != 0;
}
}

View file

@ -2,8 +2,6 @@ package com.simibubi.create.gui;
import java.util.List;
import org.lwjgl.opengl.GL11;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
@ -18,15 +16,11 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.IHasContainer;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.client.gui.widget.Widget;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.texture.AtlasTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.client.model.data.EmptyModelData;
public class SchematicTableScreen extends ContainerScreen<SchematicTableContainer>
implements IHasContainer<SchematicTableContainer> {
@ -53,7 +47,7 @@ public class SchematicTableScreen extends ContainerScreen<SchematicTableContaine
protected void init() {
super.init();
xTopLeft = (width - GuiResources.SCHEMATIC_TABLE.width) / 2;
yTopLeft = (height - GuiResources.SCHEMATIC_TABLE.height) / 2;
yTopLeft = (height - GuiResources.SCHEMATIC_TABLE.height + 50) / 2;
xMainWindow = xTopLeft - 40;
yMainWindow = yTopLeft - 80;
buttons.clear();
@ -113,21 +107,17 @@ public class SchematicTableScreen extends ContainerScreen<SchematicTableContaine
GlStateManager.enableBlend();
GlStateManager.enableRescaleNormal();
GlStateManager.enableAlphaTest();
RenderHelper.enableGUIStandardItemLighting();
GlStateManager.alphaFunc(516, 0.1F);
GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
GlStateManager.translated(xTopLeft + 220, yTopLeft + 30, 200);
GlStateManager.rotatef(140, -.1f, 1, -.2f);
GlStateManager.rotatef(50, -.5f, 1, -.2f);
GlStateManager.scaled(50, -50, 50);
Minecraft.getInstance().getTextureManager().bindTexture(AtlasTexture.LOCATION_BLOCKS_TEXTURE);
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
minecraft.getBlockRendererDispatcher().renderBlock(AllBlocks.SCHEMATIC_TABLE.get().getDefaultState(),
new BlockPos(0, 0, 0), minecraft.world, buffer, minecraft.world.rand, EmptyModelData.INSTANCE);
Tessellator.getInstance().draw();
minecraft.getBlockRendererDispatcher().renderBlockBrightness(AllBlocks.SCHEMATIC_TABLE.get().getDefaultState(), 1);
GlStateManager.disableAlphaTest();
GlStateManager.disableRescaleNormal();

View file

@ -0,0 +1,103 @@
package com.simibubi.create.gui;
import java.util.List;
import java.util.function.Consumer;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.schematic.Tools;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.util.text.StringTextComponent;
public class ToolSelectionScreen extends Screen {
protected List<Tools> tools;
protected Consumer<Tools> callback;
public boolean focused;
private float yOffset;
protected int selection;
protected int w;
protected int h;
public ToolSelectionScreen(List<Tools> tools, Consumer<Tools> callback) {
super(new StringTextComponent("Tool Selection"));
this.minecraft = Minecraft.getInstance();
this.tools = tools;
this.callback = callback;
focused = false;
yOffset = 0;
selection = 0;
callback.accept(tools.get(selection));
w = tools.size() * 50 + 30;
h = 30;
}
public void setSelectedElement(Tools tool) {
if (!tools.contains(tool))
return;
selection = tools.indexOf(tool);
}
public void cycle(int direction) {
selection += (direction < 0)? 1 : -1;
selection = (selection + tools.size()) % tools.size();
}
private void draw(float partialTicks) {
MainWindow mainWindow = Minecraft.getInstance().mainWindow;
int x = (mainWindow.getScaledWidth() - w) / 2 + 15;
int y = mainWindow.getScaledHeight() - h - 75;
GlStateManager.pushMatrix();
GlStateManager.translatef(0, -yOffset, 0);
GuiResources gray = GuiResources.GRAY;
GlStateManager.enableBlend();
GlStateManager.enableAlphaTest();
GlStateManager.color4f(1, 1, 1, focused? 7 / 8f : 1 / 2f);
Minecraft.getInstance().getTextureManager().bindTexture(gray.location);
blit(x - 15, y, gray.startX, gray.startY, w, h, gray.width, gray.height);
GlStateManager.color4f(1, 1, 1, 1);
for (int i = 0; i < tools.size(); i++) {
GlStateManager.pushMatrix();
float alpha = focused? 1 : .2f;
if (i == selection) {
GlStateManager.translatef(0, -10, 0);
drawCenteredString(minecraft.fontRenderer, tools.get(i).getDisplayName(), x + i * 50 + 24, y + 28, 0xCCDDFF);
alpha = 1;
}
GlStateManager.color4f(0, 0, 0, alpha);
tools.get(i).getIcon().draw(this, x + i * 50 + 16, y + 12);
GlStateManager.color4f(1, 1, 1, alpha);
tools.get(i).getIcon().draw(this, x + i * 50 + 16, y + 11);
GlStateManager.popMatrix();
}
GlStateManager.popMatrix();
}
public void update() {
if (focused) yOffset += (10 - yOffset) * .1f;
else yOffset *= .9f;
}
public void renderPassive(float partialTicks) {
draw(partialTicks);
}
@Override
public void onClose() {
callback.accept(tools.get(selection));
}
}

View file

@ -2,7 +2,7 @@ package com.simibubi.create.gui.widgets;
import java.util.function.Consumer;
import com.simibubi.create.utility.Keyboard;
import com.simibubi.create.gui.Keyboard;
import net.minecraft.util.text.TextFormatting;

View file

@ -9,6 +9,7 @@ import java.nio.file.StandardOpenOption;
import org.apache.commons.io.IOUtils;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllItems;
import com.simibubi.create.block.SchematicannonTileEntity;
import com.simibubi.create.schematic.SchematicHologram;
@ -24,10 +25,15 @@ import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.ActionResult;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template;
import net.minecraftforge.fml.common.thread.SidedThreadGroups;
public class ItemBlueprint extends Item {
@ -35,6 +41,70 @@ public class ItemBlueprint extends Item {
super(properties.maxStackSize(1));
}
public static ItemStack create(String schematic, String owner) {
ItemStack blueprint = new ItemStack(AllItems.BLUEPRINT.item);
CompoundNBT tag = new CompoundNBT();
tag.putBoolean("Deployed", false);
tag.putString("Owner", owner);
tag.putString("File", schematic);
tag.put("Anchor", NBTUtil.writeBlockPos(BlockPos.ZERO));
tag.putString("Rotation", Rotation.NONE.name());
tag.putString("Mirror", Mirror.NONE.name());
blueprint.setTag(tag);
writeSize(blueprint);
blueprint.setDisplayName(new StringTextComponent(TextFormatting.RESET + "" + TextFormatting.WHITE
+ "Blueprint (" + TextFormatting.GOLD + schematic + TextFormatting.WHITE + ")"));
return blueprint;
}
public static void writeSize(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag();
Template t = getSchematic(blueprint);
tag.put("Bounds", NBTUtil.writeBlockPos(t.getSize()));
blueprint.setTag(tag);
}
public static PlacementSettings getSettings(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag();
PlacementSettings settings = new PlacementSettings();
settings.setRotation(Rotation.valueOf(tag.getString("Rotation")));
settings.setMirror(Mirror.valueOf(tag.getString("Mirror")));
return settings;
}
public static Template getSchematic(ItemStack blueprint) {
Template t = new Template();
String owner = blueprint.getTag().getString("Owner");
String schematic = blueprint.getTag().getString("File");
String filepath = "";
if (Thread.currentThread().getThreadGroup() == SidedThreadGroups.SERVER)
filepath = "schematics/uploaded/" + owner + "/" + schematic;
else
filepath = "schematics/" + schematic;
InputStream stream = null;
try {
stream = Files.newInputStream(Paths.get(filepath), StandardOpenOption.READ);
CompoundNBT nbt = CompressedStreamTools.readCompressed(stream);
t.read(nbt);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null)
IOUtils.closeQuietly(stream);
}
return t;
}
@Override
public ActionResultType onItemUse(ItemUseContext context) {
@ -66,28 +136,6 @@ public class ItemBlueprint extends Item {
return ActionResultType.SUCCESS;
}
if (!(context.getPlayer().getName().getFormattedText().equals(tag.getString("Owner")))) {
context.getPlayer()
.sendStatusMessage(new StringTextComponent("You are not the Owner of this Schematic."), true);
}
String filepath = "schematics/" + tag.getString("File");
Template t = new Template();
InputStream stream = null;
try {
stream = Files.newInputStream(Paths.get(filepath), StandardOpenOption.READ);
CompoundNBT nbt = CompressedStreamTools.readCompressed(stream);
t.read(nbt);
new SchematicHologram().startHologram(t, pos.offset(context.getFace()));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null)
IOUtils.closeQuietly(stream);
}
}
context.getPlayer().getCooldownTracker().setCooldown(this, 10);

View file

@ -31,6 +31,9 @@ import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.network.PacketDistributor;
public class ItemWandSymmetry extends Item {
@ -41,16 +44,16 @@ public class ItemWandSymmetry extends Item {
public ItemWandSymmetry(Properties properties) {
super(properties.maxStackSize(1));
}
@Override
public ActionResultType onItemUse(ItemUseContext context) {
PlayerEntity player = context.getPlayer();
BlockPos pos = context.getPos();
player.getCooldownTracker().setCooldown(this, 5);
if (context.getWorld().isRemote || context.getHand() != Hand.MAIN_HAND)
if (context.getWorld().isRemote || context.getHand() != Hand.MAIN_HAND)
return ActionResultType.SUCCESS;
ItemStack wand = player.getHeldItem(context.getHand());
checkNBT(wand);
CompoundNBT compound = wand.getTag().getCompound($SYMMETRY);
@ -68,8 +71,9 @@ public class ItemWandSymmetry extends Item {
SymmetryElement newElement = new SymmetryPlane(pos3d);
if (previousElement instanceof SymmetryEmptySlot) {
newElement.setOrientation((player.getHorizontalFacing() == Direction.NORTH
|| player.getHorizontalFacing() == Direction.SOUTH) ? SymmetryPlane.Align.XY.ordinal()
newElement.setOrientation(
(player.getHorizontalFacing() == Direction.NORTH || player.getHorizontalFacing() == Direction.SOUTH)
? SymmetryPlane.Align.XY.ordinal()
: SymmetryPlane.Align.YZ.ordinal());
newElement.enable = true;
player.sendStatusMessage(new StringTextComponent(TextFormatting.GREEN + "New Plane created"), true);
@ -105,12 +109,19 @@ public class ItemWandSymmetry extends Item {
@Override
public ActionResult<ItemStack> onItemRightClick(World worldIn, PlayerEntity playerIn, Hand handIn) {
if (worldIn.isRemote) {
GuiOpener.open(new GuiWandSymmetry(playerIn.getHeldItem(handIn)));
playerIn.getCooldownTracker().setCooldown(this, 5);
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
openWandGUI(playerIn.getHeldItem(handIn));
});
playerIn.getCooldownTracker().setCooldown(this, 5);
}
return super.onItemRightClick(worldIn, playerIn, handIn);
}
@OnlyIn(Dist.CLIENT)
private void openWandGUI(ItemStack wand) {
GuiOpener.open(new GuiWandSymmetry(wand));
}
private static void checkNBT(ItemStack wand) {
if (!wand.hasTag() || !wand.getTag().contains($SYMMETRY)) {
wand.setTag(new CompoundNBT());
@ -136,15 +147,14 @@ public class ItemWandSymmetry extends Item {
Map<BlockPos, BlockState> blockSet = new HashMap<>();
blockSet.put(pos, block);
SymmetryElement symmetry = SymmetryElement
.fromNBT((CompoundNBT) wand.getTag().getCompound($SYMMETRY));
SymmetryElement symmetry = SymmetryElement.fromNBT((CompoundNBT) wand.getTag().getCompound($SYMMETRY));
Vec3d mirrorPos = symmetry.getPosition();
if (mirrorPos.distanceTo(new Vec3d(pos)) > 50)
return;
symmetry.process(blockSet);
BlockPos to = new BlockPos(mirrorPos);
List<BlockPos> targets = new ArrayList<>();
@ -152,11 +162,12 @@ public class ItemWandSymmetry extends Item {
for (BlockPos position : blockSet.keySet()) {
if (world.func_217350_a(block, position, ISelectionContext.forEntity(player))) {
world.setBlockState(position, blockSet.get(position));
targets.add(position);
targets.add(position);
}
}
Packets.channel.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player), new PacketSymmetryEffect(to, targets));
Packets.channel.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player),
new PacketSymmetryEffect(to, targets));
}
public static void remove(World world, ItemStack wand, PlayerEntity player, BlockPos pos) {
@ -167,8 +178,7 @@ public class ItemWandSymmetry extends Item {
Map<BlockPos, BlockState> blockSet = new HashMap<>();
blockSet.put(pos, air);
SymmetryElement symmetry = SymmetryElement
.fromNBT((CompoundNBT) wand.getTag().getCompound($SYMMETRY));
SymmetryElement symmetry = SymmetryElement.fromNBT((CompoundNBT) wand.getTag().getCompound($SYMMETRY));
Vec3d mirrorPos = symmetry.getPosition();
if (mirrorPos.distanceTo(new Vec3d(pos)) > 50)
@ -178,14 +188,15 @@ public class ItemWandSymmetry extends Item {
BlockPos to = new BlockPos(mirrorPos);
List<BlockPos> targets = new ArrayList<>();
targets.add(pos);
for (BlockPos position : blockSet.keySet()) {
targets.add(position);
world.setBlockState(position, air);
}
Packets.channel.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player), new PacketSymmetryEffect(to, targets));
Packets.channel.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player),
new PacketSymmetryEffect(to, targets));
}
}

View file

@ -10,26 +10,44 @@ import net.minecraftforge.fml.network.NetworkEvent.Context;
public class PacketNbt {
public ItemStack stack;
public int slot;
public PacketNbt(ItemStack stack) {
this(stack, -1);
}
public PacketNbt(ItemStack stack, int slot) {
this.stack = stack;
this.slot = slot;
}
public PacketNbt(PacketBuffer buffer) {
stack = buffer.readItemStack();
slot = buffer.readInt();
}
public void toBytes(PacketBuffer buffer) {
buffer.writeItemStack(stack);
buffer.writeInt(slot);
}
public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> {
ServerPlayerEntity player = context.get().getSender();
ItemStack heldItem = player.getHeldItemMainhand();
if (heldItem.getItem() == stack.getItem()) {
heldItem.setTag(stack.getTag());
if (slot == -1) {
ItemStack heldItem = player.getHeldItemMainhand();
if (heldItem.getItem() == stack.getItem()) {
heldItem.setTag(stack.getTag());
}
return;
}
ItemStack heldInSlot = player.inventory.getStackInSlot(slot);
if (heldInSlot.getItem() == stack.getItem()) {
heldInSlot.setTag(stack.getTag());
}
});
}

View file

@ -7,8 +7,6 @@ import com.simibubi.create.block.SchematicTableContainer;
import net.minecraft.client.Minecraft;
import net.minecraft.inventory.container.Container;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.network.NetworkEvent.Context;
public class PacketSchematicTableContainer {
@ -33,14 +31,12 @@ public class PacketSchematicTableContainer {
buffer.writeFloat(progress);
}
@OnlyIn(Dist.CLIENT)
public void handle(Supplier<Context> context) {
context.get().enqueueWork(() -> {
Container c = Minecraft.getInstance().player.openContainer;
if (c != null && c instanceof SchematicTableContainer) {
((SchematicTableContainer) c).receiveSchematicInfo(schematic, progress);
}
});
}

View file

@ -43,7 +43,7 @@ public class PacketSchematicUpload {
public PacketSchematicUpload(PacketBuffer buffer) {
code = buffer.readInt();
schematic = buffer.readString();
schematic = buffer.readString(256);
if (code == WRITE)
data = buffer.readByteArray();

View file

@ -10,13 +10,14 @@ public class Packets {
private static final String PROTOCOL_VERSION = "1";
public static final SimpleChannel channel = NetworkRegistry.newSimpleChannel(
new ResourceLocation(Create.ID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals,
PROTOCOL_VERSION::equals);
public static SimpleChannel channel;
public static void registerPackets() {
int i = 0;
channel = NetworkRegistry.newSimpleChannel(new ResourceLocation(Create.ID, "main"), () -> PROTOCOL_VERSION,
PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);
channel.registerMessage(i++, PacketNbt.class, PacketNbt::toBytes, PacketNbt::new, PacketNbt::handle);
channel.registerMessage(i++, PacketSchematicTableContainer.class, PacketSchematicTableContainer::toBytes,
PacketSchematicTableContainer::new, PacketSchematicTableContainer::handle);

View file

@ -0,0 +1,355 @@
package com.simibubi.create.schematic;
import java.util.HashMap;
import org.lwjgl.glfw.GLFW;
import com.google.common.collect.ImmutableList;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.gui.BlueprintHotbarOverlay;
import com.simibubi.create.gui.Keyboard;
import com.simibubi.create.gui.ToolSelectionScreen;
import com.simibubi.create.item.ItemBlueprint;
import com.simibubi.create.networking.PacketNbt;
import com.simibubi.create.networking.Packets;
import com.simibubi.create.utility.TessellatorHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Mirror;
import net.minecraft.util.Rotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.gen.feature.template.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template;
import net.minecraftforge.client.event.InputEvent.KeyInputEvent;
import net.minecraftforge.client.event.InputEvent.MouseInputEvent;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
@EventBusSubscriber(bus = Bus.FORGE)
public class BlueprintHandler {
public static BlueprintHandler instance;
public Template cachedSchematic;
public String cachedSchematicName;
public PlacementSettings cachedSettings;
public BlockPos anchor;
public BlockPos size;
public boolean active;
public boolean deployed;
public int slot;
public ItemStack item;
public Tools currentTool;
public ToolSelectionScreen selectionScreen;
public static final int SYNC_DELAY = 20;
public int syncCooldown;
private BlueprintHotbarOverlay overlay;
public BlueprintHandler() {
instance = this;
currentTool = Tools.Deploy;
overlay = new BlueprintHotbarOverlay();
selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), this::equip);
}
@SubscribeEvent
public static void onClientTick(ClientTickEvent event) {
ClientPlayerEntity player = Minecraft.getInstance().player;
if (player == null)
return;
ItemStack stack = findBlueprintInHand(player);
if (stack == null) {
instance.active = false;
instance.syncCooldown = 0;
instance.slot = 0;
if (instance.item != null && itemLost(player)) {
instance.item = null;
SchematicHologram.reset();
}
return;
}
// Newly equipped
if (!instance.active || !stack.getTag().getString("File").equals(instance.cachedSchematicName)) {
instance.loadSettings(stack);
instance.cachedSchematicName = stack.getTag().getString("File");
instance.active = true;
if (instance.deployed) {
Tools toolBefore = instance.currentTool;
instance.selectionScreen = new ToolSelectionScreen(Tools.getTools(), instance::equip);
if (toolBefore != null) {
instance.selectionScreen.setSelectedElement(toolBefore);
instance.equip(toolBefore);
}
}
else
instance.selectionScreen = new ToolSelectionScreen(ImmutableList.of(Tools.Deploy), instance::equip);
instance.sync();
}
if (!instance.active)
return;
if (instance.syncCooldown > 0)
instance.syncCooldown--;
if (instance.syncCooldown == 1)
instance.sync();
instance.selectionScreen.update();
instance.currentTool.getTool().updateSelection();
}
@SubscribeEvent
public static void onRenderWorld(RenderWorldLastEvent event) {
if (!instance.active)
return;
TessellatorHelper.prepareForDrawing();
instance.currentTool.getTool().renderTool();
TessellatorHelper.cleanUpAfterDrawing();
}
@SubscribeEvent
public static void onRenderOverlay(RenderGameOverlayEvent event) {
if (!instance.active)
return;
instance.overlay.renderOn(instance.slot);
instance.currentTool.getTool().renderOverlay();
instance.selectionScreen.renderPassive(event.getPartialTicks());
}
@SubscribeEvent
public static void onClick(MouseInputEvent event) {
if (Minecraft.getInstance().currentScreen != null)
return;
if (event.getAction() != Keyboard.PRESS)
return;
if (event.getButton() != 1)
return;
if (!instance.active)
return;
instance.currentTool.getTool().handleRightClick();
}
@SubscribeEvent
public static void onKeyTyped(KeyInputEvent event) {
if (Minecraft.getInstance().currentScreen != null)
return;
if (event.getKey() != Create.TOOL_MENU.getKey().getKeyCode())
return;
if (!instance.active)
return;
boolean released = event.getAction() == Keyboard.RELEASE;
ToolSelectionScreen toolSelection = instance.selectionScreen;
if (released && toolSelection.focused) {
toolSelection.focused = false;
toolSelection.onClose();
}
if (!released && !toolSelection.focused)
toolSelection.focused = true;
}
public boolean onScroll(double delta) {
if (!active)
return false;
if (selectionScreen.focused) {
selectionScreen.cycle((int) delta);
return true;
}
if (Keyboard.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL)) {
return currentTool.getTool().handleMouseWheel(delta);
}
return false;
}
private static ItemStack findBlueprintInHand(PlayerEntity player) {
ItemStack stack = player.getHeldItemMainhand();
if (!AllItems.BLUEPRINT.typeOf(stack))
return null;
if (!stack.hasTag())
return null;
instance.item = stack;
instance.slot = player.inventory.currentItem;
return stack;
}
private static boolean itemLost(PlayerEntity player) {
for (int i = 0; i < PlayerInventory.getHotbarSize(); i++) {
if (!player.inventory.getStackInSlot(i).isItemEqual(instance.item))
continue;
if (!ItemStack.areItemStackTagsEqual(player.inventory.getStackInSlot(i), instance.item))
continue;
return false;
}
return true;
}
public void markDirty() {
syncCooldown = SYNC_DELAY;
SchematicHologram.reset();
}
public void sync() {
Minecraft.getInstance().player.sendStatusMessage(new StringTextComponent("Syncing..."), true);
Packets.channel.sendToServer(new PacketNbt(item, slot));
SchematicWorld w = new SchematicWorld(new HashMap<>(), new Cuboid(), anchor);
PlacementSettings settings = cachedSettings.copy();
settings.setBoundingBox(null);
ItemBlueprint.getSchematic(item).addBlocksToWorld(w, anchor, settings);
new SchematicHologram().startHologram(w);
}
public void equip(Tools tool) {
this.currentTool = tool;
currentTool.getTool().init();
}
public void loadSettings(ItemStack blueprint) {
CompoundNBT tag = blueprint.getTag();
cachedSettings = new PlacementSettings();
cachedSettings.setRotation(Rotation.valueOf(tag.getString("Rotation")));
cachedSettings.setMirror(Mirror.valueOf(tag.getString("Mirror")));
deployed = tag.getBoolean("Deployed");
if (deployed)
anchor = NBTUtil.readBlockPos(tag.getCompound("Anchor"));
size = NBTUtil.readBlockPos(tag.getCompound("Bounds"));
}
public void flip(Axis axis) {
Rotation r = cachedSettings.getRotation();
boolean rotationAt90s = r == Rotation.CLOCKWISE_90 || r == Rotation.COUNTERCLOCKWISE_90;
Mirror mirror = axis == Axis.Z ^ rotationAt90s ? Mirror.FRONT_BACK : Mirror.LEFT_RIGHT;
BlockPos coordModifier = new BlockPos((r == Rotation.NONE || r == Rotation.COUNTERCLOCKWISE_90) ? 1 : -1, 0,
(r == Rotation.NONE || r == Rotation.CLOCKWISE_90) ? 1 : -1);
BlockPos anchorOffset = axis == Axis.Z
? new BlockPos(((rotationAt90s ? size.getZ() : size.getX()) - 1) * coordModifier.getX(), 0, 0)
: new BlockPos(0, 0, ((!rotationAt90s ? size.getZ() : size.getX()) - 1) * coordModifier.getZ());
Mirror m = cachedSettings.getMirror();
if (m == Mirror.NONE) {
cachedSettings.setMirror(mirror);
anchor = anchor.add(anchorOffset);
Minecraft.getInstance().player.sendStatusMessage(
new StringTextComponent("Mirror: " + cachedSettings.getMirror().toString()), true);
} else if (m == mirror) {
cachedSettings.setMirror(Mirror.NONE);
anchor = anchor.subtract(anchorOffset);
Minecraft.getInstance().player.sendStatusMessage(
new StringTextComponent("Mirror: " + cachedSettings.getMirror().toString()), true);
} else if (m != mirror) {
cachedSettings.setMirror(Mirror.NONE);
anchor = anchor.add(anchorOffset);
cachedSettings.setRotation(r.add(Rotation.CLOCKWISE_180));
Minecraft.getInstance().player.sendStatusMessage(
new StringTextComponent("Mirror: None, Rotation: " + cachedSettings.getRotation().toString()),
true);
}
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
item.getTag().putString("Mirror", cachedSettings.getMirror().name());
item.getTag().putString("Rotation", r.name());
markDirty();
}
public void rotate(Rotation rotation) {
Rotation r = cachedSettings.getRotation();
BlockPos center = centerOfSchematic();
cachedSettings.setRotation(r.add(rotation));
BlockPos diff = center.subtract(anchor);
BlockPos move = diff.subtract(diff.rotate(rotation));
anchor = anchor.add(move);
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
item.getTag().putString("Rotation", cachedSettings.getRotation().name());
Minecraft.getInstance().player.sendStatusMessage(
new StringTextComponent("Rotation: " + cachedSettings.getRotation().toString()), true);
markDirty();
}
public void moveTo(BlockPos anchor) {
if (!deployed)
instance.selectionScreen = new ToolSelectionScreen(Tools.getTools(), instance::equip);
deployed = true;
this.anchor = anchor;
item.getTag().putBoolean("Deployed", true);
item.getTag().put("Anchor", NBTUtil.writeBlockPos(anchor));
markDirty();
}
public BlockPos getTransformedSize() {
BlockPos flipped = size;
if (cachedSettings.getMirror() == Mirror.FRONT_BACK)
flipped = new BlockPos(-flipped.getX(), flipped.getY(), flipped.getZ());
if (cachedSettings.getMirror() == Mirror.LEFT_RIGHT)
flipped = new BlockPos(flipped.getX(), flipped.getY(), -flipped.getZ());
BlockPos rotate = flipped.rotate(cachedSettings.getRotation());
return rotate;
}
public BlockPos getTransformedAnchor() {
BlockPos anchor = this.anchor;
Rotation r = cachedSettings.getRotation();
BlockPos flipOffset = BlockPos.ZERO;
if (cachedSettings.getMirror() == Mirror.FRONT_BACK)
flipOffset = new BlockPos(1, 0, 0);
if (cachedSettings.getMirror() == Mirror.LEFT_RIGHT)
flipOffset = new BlockPos(0, 0, 1);
flipOffset = flipOffset.rotate(r);
anchor = anchor.add(flipOffset);
if (r == Rotation.CLOCKWISE_90 || r == Rotation.CLOCKWISE_180)
anchor = anchor.add(1, 0, 0);
if (r == Rotation.COUNTERCLOCKWISE_90 || r == Rotation.CLOCKWISE_180)
anchor = anchor.add(0, 0, 1);
return anchor;
}
public BlockPos centerOfSchematic() {
BlockPos size = getTransformedSize();
BlockPos center = new BlockPos(size.getX() / 2, 0, size.getZ() / 2);
return anchor.add(center);
}
}

View file

@ -11,6 +11,10 @@ public class Cuboid {
public int height;
public int length;
public Cuboid() {
this(BlockPos.ZERO, 0, 0, 0);
}
public Cuboid(BlockPos origin, BlockPos size) {
this(origin, size.getX(), size.getY(), size.getZ());
}

View file

@ -0,0 +1,15 @@
package com.simibubi.create.schematic;
public interface ISchematicTool {
public void init();
public void updateSelection();
public boolean handleRightClick();
public boolean handleMouseWheel(double delta);
public void renderTool();
public void renderOverlay();
}

View file

@ -0,0 +1,84 @@
package com.simibubi.create.schematic;
import org.lwjgl.glfw.GLFW;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.gui.Keyboard;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.MutableBoundingBox;
public class SchematicDeployTool extends SchematicPlacementToolBase {
@Override
public void init() {
super.init();
selectionRange = -1;
}
@Override
public void updateSelection() {
if (blueprint.active && selectionRange == -1) {
selectionRange = (int) blueprint.size.manhattanDistance(BlockPos.ZERO) / 2;
selectionRange = MathHelper.clamp(selectionRange, 1, 100);
}
selectIgnoreBlocks = Keyboard.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL);
super.updateSelection();
}
@Override
public void renderTool() {
super.renderTool();
if (selectedPos == null)
return;
BlockPos size = blueprint.getTransformedSize();
BlockPos min = selectedPos.add(Math.round(size.getX() * -.5f), 0, Math.round(size.getZ() * -.5f));
BlockPos max = min.add(size.getX(), size.getY(), size.getZ());
if (blueprint.deployed) {
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(blueprint.getTransformedSize()));
min = new BlockPos(bb.minX, bb.minY, bb.minZ);
max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
}
GlStateManager.lineWidth(3);
GlStateManager.color4f(.5f, .8f, 1, 1);
GlStateManager.disableTexture();
WorldRenderer.drawBoundingBox(min.getX() - 1 / 8d, min.getY() + 1 / 16d, min.getZ() - 1 / 8d,
max.getX() + 1 / 8d, max.getY() + 1 / 8d, max.getZ() + 1 / 8d, 1, 1, 1, 1);
GlStateManager.lineWidth(1);
GlStateManager.enableTexture();
}
@Override
public boolean handleMouseWheel(double delta) {
if (selectIgnoreBlocks) {
selectionRange += delta;
selectionRange = MathHelper.clamp(selectionRange, 1, 100);
return true;
}
return super.handleMouseWheel(delta);
}
@Override
public boolean handleRightClick() {
if (selectedPos == null)
return super.handleRightClick();
BlockPos size = blueprint.getTransformedSize();
blueprint.moveTo(selectedPos.add(Math.round(size.getX() * -.5f), 0, Math.round(size.getZ() * -.5f)));
return true;
}
}

View file

@ -0,0 +1,39 @@
package com.simibubi.create.schematic;
public class SchematicFlipTool extends SchematicPlacementToolBase {
@Override
public void init() {
super.init();
renderSelectedFace = true;
}
@Override
public boolean handleRightClick() {
mirror();
return true;
}
@Override
public boolean handleMouseWheel(double delta) {
mirror();
return true;
}
@Override
public void updateSelection() {
super.updateSelection();
if (!schematicSelected)
return;
renderSelectedFace = selectedFace.getAxis().isHorizontal();
}
private void mirror() {
if (schematicSelected && selectedFace.getAxis().isHorizontal()) {
blueprint.flip(selectedFace.getAxis());
}
}
}

View file

@ -28,6 +28,7 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.gen.feature.template.PlacementSettings;
import net.minecraft.world.gen.feature.template.Template;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.client.model.data.EmptyModelData;
@ -36,6 +37,7 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
@OnlyIn(Dist.CLIENT)
@EventBusSubscriber(Dist.CLIENT)
public class SchematicHologram {
@ -63,6 +65,13 @@ public class SchematicHologram {
active = true;
changed = true;
}
public void startHologram(SchematicWorld world) {
this.anchor = world.anchor;
this.schematic = world;
this.active = true;
this.changed = true;
}
public static SchematicHologram getInstance() {
return instance;

View file

@ -0,0 +1,23 @@
package com.simibubi.create.schematic;
public class SchematicMoveTool extends SchematicPlacementToolBase {
@Override
public void init() {
super.init();
renderSelectedFace = true;
}
@Override
public boolean handleMouseWheel(double delta) {
if (schematicSelected && selectedFace.getAxis().isHorizontal()) {
blueprint.moveTo(delta < 0 ? blueprint.anchor.add(selectedFace.getDirectionVec())
: blueprint.anchor.subtract(selectedFace.getDirectionVec()));
return true;
}
return super.handleMouseWheel(delta);
}
}

View file

@ -0,0 +1,16 @@
package com.simibubi.create.schematic;
public class SchematicMoveVerticalTool extends SchematicPlacementToolBase {
@Override
public boolean handleMouseWheel(double delta) {
if (blueprint.deployed) {
blueprint.moveTo(blueprint.anchor.add(0, delta, 0));
return true;
}
return super.handleMouseWheel(delta);
}
}

View file

@ -0,0 +1,35 @@
package com.simibubi.create.schematic;
public abstract class SchematicPlacementToolBase extends SchematicToolBase {
@Override
public void init() {
super.init();
}
@Override
public void updateSelection() {
super.updateSelection();
}
@Override
public void renderTool() {
super.renderTool();
}
@Override
public void renderOverlay() {
super.renderOverlay();
}
@Override
public boolean handleMouseWheel(double delta) {
return false;
}
@Override
public boolean handleRightClick() {
return false;
}
}

View file

@ -0,0 +1,13 @@
package com.simibubi.create.schematic;
import net.minecraft.util.Rotation;
public class SchematicRotateTool extends SchematicPlacementToolBase {
@Override
public boolean handleMouseWheel(double delta) {
blueprint.rotate(delta > 0 ? Rotation.CLOCKWISE_90 : Rotation.COUNTERCLOCKWISE_90);
return true;
}
}

View file

@ -0,0 +1,122 @@
package com.simibubi.create.schematic;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.utility.RaycastHelper;
import com.simibubi.create.utility.RaycastHelper.PredicateTraceResult;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemUseContext;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.math.Vec3d;
public abstract class SchematicToolBase implements ISchematicTool {
protected BlueprintHandler blueprint;
public BlockPos selectedPos;
public boolean selectIgnoreBlocks;
public int selectionRange;
public boolean schematicSelected;
public boolean renderSelectedFace;
public Direction selectedFace;
public SchematicToolBase() {
blueprint = BlueprintHandler.instance;
}
@Override
public void init() {
selectedPos = null;
selectedFace = null;
schematicSelected = false;
}
@Override
public void updateSelection() {
ClientPlayerEntity player = Minecraft.getInstance().player;
// Select Blueprint
if (blueprint.deployed) {
PredicateTraceResult result = RaycastHelper.rayTraceUntil(player, 70,
pos -> new MutableBoundingBox(BlockPos.ZERO, blueprint.getTransformedSize())
.isVecInside(pos.subtract(blueprint.anchor)));
schematicSelected = !result.missed();
selectedFace = schematicSelected ? result.getFacing() : null;
}
// Select location at distance
if (selectIgnoreBlocks) {
selectedPos = new BlockPos(player.getEyePosition(Minecraft.getInstance().getRenderPartialTicks())
.add(player.getLookVec().scale(selectionRange)));
return;
}
// Select targeted Block
BlockRayTraceResult trace = RaycastHelper.rayTraceRange(player.world, player, 75);
if (trace != null && trace.getType() == Type.BLOCK) {
BlockPos hit = new BlockPos(trace.getHitVec());
boolean replaceable = player.world.getBlockState(hit)
.isReplaceable(new BlockItemUseContext(new ItemUseContext(player, Hand.MAIN_HAND, trace)));
if (trace.getFace().getAxis().isVertical() && !replaceable)
hit = hit.offset(trace.getFace());
selectedPos = hit;
} else {
selectedPos = null;
}
}
@Override
public void renderTool() {
if (blueprint.deployed) {
GlStateManager.lineWidth(5);
GlStateManager.color4f(1, 1, 1, 1);
GlStateManager.disableTexture();
BlockPos min = blueprint.getTransformedAnchor();
MutableBoundingBox bb = new MutableBoundingBox(min, min.add(blueprint.getTransformedSize()));
min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
WorldRenderer.drawBoundingBox(min.getX() - 1 / 8d, min.getY() + 1 / 16d, min.getZ() - 1 / 8d,
max.getX() + 1 / 8d, max.getY() + 1 / 8d, max.getZ() + 1 / 8d, .3f, .4f, 1, 1);
if (schematicSelected && renderSelectedFace) {
Vec3d vec = new Vec3d(selectedFace.getDirectionVec());
Vec3d center = new Vec3d(min.add(max)).scale(1 / 2f);
Vec3d radii = new Vec3d(max.subtract(min)).scale(1 / 2f);
Vec3d onFaceOffset = new Vec3d(1 - Math.abs(vec.x), 1 - Math.abs(vec.y), 1 - Math.abs(vec.z))
.mul(radii);
Vec3d faceMin = center.add(vec.mul(radii).add(onFaceOffset));
Vec3d faceMax = center.add(vec.mul(radii).subtract(onFaceOffset));
GlStateManager.lineWidth(4);
WorldRenderer.drawBoundingBox(faceMin.getX(), faceMin.getY() + 1 / 16d, faceMin.getZ(), faceMax.getX(),
faceMax.getY() + 1 / 8d, faceMax.getZ(), 1, 1, 1, 1);
}
GlStateManager.lineWidth(1);
GlStateManager.enableTexture();
}
}
@Override
public void renderOverlay() {
}
}

View file

@ -45,7 +45,7 @@ public class SchematicWorld implements IWorld {
private Map<BlockPos, BlockState> blocks;
private Cuboid bounds;
private BlockPos anchor;
public BlockPos anchor;
public SchematicWorld(Map<BlockPos, BlockState> blocks, Cuboid bounds, BlockPos anchor) {
this.blocks = blocks;

View file

@ -0,0 +1,46 @@
package com.simibubi.create.schematic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.simibubi.create.gui.GuiResources;
public enum Tools {
Deploy(new SchematicDeployTool(), "Deploy", GuiResources.ICON_3x3),
Move(new SchematicMoveTool(), "Move XZ", GuiResources.ICON_3x3),
MoveY(new SchematicMoveVerticalTool(), "Move Y", GuiResources.ICON_3x3),
Rotate(new SchematicRotateTool(), "Rotate", GuiResources.ICON_3x3),
Flip(new SchematicFlipTool(), "Flip", GuiResources.ICON_3x3);
private ISchematicTool tool;
private String displayName;
private GuiResources icon;
private Tools(ISchematicTool tool, String name, GuiResources icon) {
this.tool = tool;
this.displayName = name;
this.icon = icon;
}
public ISchematicTool getTool() {
return tool;
}
public String getDisplayName() {
return displayName;
}
public GuiResources getIcon() {
return icon;
}
public static List<Tools> getTools() {
List<Tools> tools = new ArrayList<>();
Collections.addAll(tools, Move, MoveY, Deploy, Rotate, Flip);
return tools;
}
}

View file

@ -0,0 +1,195 @@
package com.simibubi.create.utility;
import java.util.function.Predicate;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceContext.BlockMode;
import net.minecraft.util.math.RayTraceContext.FluidMode;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class RaycastHelper {
public static BlockRayTraceResult rayTraceRange(World worldIn, PlayerEntity playerIn, double range) {
Vec3d origin = getTraceOrigin(playerIn);
Vec3d target = getTraceTarget(playerIn, range, origin);
RayTraceContext context = new RayTraceContext(origin, target, BlockMode.COLLIDER, FluidMode.NONE, playerIn);
return worldIn.rayTraceBlocks(context);
}
public static PredicateTraceResult rayTraceUntil(PlayerEntity playerIn, double range, Predicate<BlockPos> predicate) {
Vec3d origin = getTraceOrigin(playerIn);
Vec3d target = getTraceTarget(playerIn, range, origin);
return rayTraceUntil(origin, target, predicate);
}
private static Vec3d getTraceTarget(PlayerEntity playerIn, double range, Vec3d origin) {
float f = playerIn.rotationPitch;
float f1 = playerIn.rotationYaw;
float f2 = MathHelper.cos(-f1 * 0.017453292F - (float) Math.PI);
float f3 = MathHelper.sin(-f1 * 0.017453292F - (float) Math.PI);
float f4 = -MathHelper.cos(-f * 0.017453292F);
float f5 = MathHelper.sin(-f * 0.017453292F);
float f6 = f3 * f4;
float f7 = f2 * f4;
double d3 = range;
Vec3d vec3d1 = origin.add((double) f6 * d3, (double) f5 * d3, (double) f7 * d3);
return vec3d1;
}
private static Vec3d getTraceOrigin(PlayerEntity playerIn) {
double d0 = playerIn.posX;
double d1 = playerIn.posY + (double) playerIn.getEyeHeight();
double d2 = playerIn.posZ;
Vec3d vec3d = new Vec3d(d0, d1, d2);
return vec3d;
}
private static PredicateTraceResult rayTraceUntil(Vec3d start, Vec3d end, Predicate<BlockPos> predicate) {
if (Double.isNaN(start.x) || Double.isNaN(start.y) || Double.isNaN(start.z))
return null;
if (Double.isNaN(end.x) || Double.isNaN(end.y) || Double.isNaN(end.z))
return null;
int dx = MathHelper.floor(end.x);
int dy = MathHelper.floor(end.y);
int dz = MathHelper.floor(end.z);
int x = MathHelper.floor(start.x);
int y = MathHelper.floor(start.y);
int z = MathHelper.floor(start.z);
BlockPos currentPos = new BlockPos(x, y, z);
if (predicate.test(currentPos))
return new PredicateTraceResult(currentPos, Direction.getFacingFromVector(dx - x, dy - y, dz - z));
int remainingDistance = 200;
while (remainingDistance-- >= 0) {
if (Double.isNaN(start.x) || Double.isNaN(start.y) || Double.isNaN(start.z)) {
return null;
}
if (x == dx && y == dy && z == dz) {
return new PredicateTraceResult();
}
boolean flag2 = true;
boolean flag = true;
boolean flag1 = true;
double d0 = 999.0D;
double d1 = 999.0D;
double d2 = 999.0D;
if (dx > x) {
d0 = (double) x + 1.0D;
} else if (dx < x) {
d0 = (double) x + 0.0D;
} else {
flag2 = false;
}
if (dy > y) {
d1 = (double) y + 1.0D;
} else if (dy < y) {
d1 = (double) y + 0.0D;
} else {
flag = false;
}
if (dz > z) {
d2 = (double) z + 1.0D;
} else if (dz < z) {
d2 = (double) z + 0.0D;
} else {
flag1 = false;
}
double d3 = 999.0D;
double d4 = 999.0D;
double d5 = 999.0D;
double d6 = end.x - start.x;
double d7 = end.y - start.y;
double d8 = end.z - start.z;
if (flag2) {
d3 = (d0 - start.x) / d6;
}
if (flag) {
d4 = (d1 - start.y) / d7;
}
if (flag1) {
d5 = (d2 - start.z) / d8;
}
if (d3 == -0.0D) {
d3 = -1.0E-4D;
}
if (d4 == -0.0D) {
d4 = -1.0E-4D;
}
if (d5 == -0.0D) {
d5 = -1.0E-4D;
}
Direction enumfacing;
if (d3 < d4 && d3 < d5) {
enumfacing = dx > x ? Direction.WEST : Direction.EAST;
start = new Vec3d(d0, start.y + d7 * d3, start.z + d8 * d3);
} else if (d4 < d5) {
enumfacing = dy > y ? Direction.DOWN : Direction.UP;
start = new Vec3d(start.x + d6 * d4, d1, start.z + d8 * d4);
} else {
enumfacing = dz > z ? Direction.NORTH : Direction.SOUTH;
start = new Vec3d(start.x + d6 * d5, start.y + d7 * d5, d2);
}
x = MathHelper.floor(start.x) - (enumfacing == Direction.EAST ? 1 : 0);
y = MathHelper.floor(start.y) - (enumfacing == Direction.UP ? 1 : 0);
z = MathHelper.floor(start.z) - (enumfacing == Direction.SOUTH ? 1 : 0);
currentPos = new BlockPos(x, y, z);
if (predicate.test(currentPos))
return new PredicateTraceResult(currentPos, enumfacing);
}
return new PredicateTraceResult();
}
public static class PredicateTraceResult {
private BlockPos pos;
private Direction facing;
public PredicateTraceResult(BlockPos pos, Direction facing) {
this.pos = pos;
this.facing = facing;
}
public PredicateTraceResult() {
// missed, no result
}
public Direction getFacing() {
return facing;
}
public BlockPos getPos() {
return pos;
}
public boolean missed() {
return this.pos == null;
}
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.utility;
import com.simibubi.create.Create;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ResourceLocation;
public enum TessellatorTextures {
Room("inner.png"),
RoomTransparent("inner_transparent.png"),
SelectedRoom("inner_selected.png"),
SuperSelectedRoom("inner_super_selected.png"),
Selection("select.png"),
Exporter("exporter.png"),
Trim("trim.png");
private ResourceLocation location;
private TessellatorTextures(String filename) {
location = new ResourceLocation(Create.ID,
"textures/block/marker/" + filename);
}
public void bind() {
Minecraft.getInstance().getTextureManager().bindTexture(location);
}
}

View file

@ -1,8 +1,9 @@
{
"item.create.symmetry_wand": "Staff of Symmetry",
"item.create.empty_blueprint": "Empty Blueprint",
"item.create.blueprint": "Blueprint",
"item.create.empty_blueprint": "Empty Schematic",
"item.create.blueprint": "Schematic",
"block.create.schematicannon": "SchematiCannon 9000",
"block.create.schematic_table": "Schematic Planner",
"block.create.schematic_table": "Schematic Table",
"block.create.creative_crate": "SchematiCannon Creatifier",
"itemGroup.create": "Create"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB