Schematic and Quill

- Added Schematic and Quill for recording schematics in game
- Schematicannon no longer attempts to place liquids
This commit is contained in:
simibubi 2019-07-17 17:13:09 +02:00
parent a34034379f
commit d7bcc1ea28
10 changed files with 333 additions and 8 deletions

View file

@ -1,6 +1,7 @@
package com.simibubi.create;
import com.simibubi.create.item.ItemBlueprint;
import com.simibubi.create.item.ItemBlueprintAndQuill;
import com.simibubi.create.item.ItemWandSymmetry;
import net.minecraft.item.Item;
@ -14,6 +15,7 @@ public enum AllItems {
SYMMETRY_WAND(new ItemWandSymmetry(standardProperties())),
EMPTY_BLUEPRINT(new Item(standardProperties().maxStackSize(1))),
BLUEPRINT_AND_QUILL(new ItemBlueprintAndQuill(standardProperties().maxStackSize(1))),
BLUEPRINT(new ItemBlueprint(standardProperties()));
public Item item;

View file

@ -5,6 +5,7 @@ import org.apache.logging.log4j.Logger;
import com.simibubi.create.gui.Keyboard;
import com.simibubi.create.networking.Packets;
import com.simibubi.create.schematic.BlueprintAndQuillHandler;
import com.simibubi.create.schematic.BlueprintHandler;
import com.simibubi.create.schematic.SchematicHologram;
@ -63,6 +64,7 @@ public class Create {
new BlueprintHandler();
ScrollFixer.init();
ScrollFixer.addMouseWheelListener(BlueprintHandler.instance::onScroll);
ScrollFixer.addMouseWheelListener(BlueprintAndQuillHandler::onScroll);
TOOL_MENU = new KeyBinding("Tool Menu (Hold)", Keyboard.LALT, NAME);
ClientRegistry.registerKeyBinding(TOOL_MENU);

View file

@ -40,7 +40,7 @@ public class SchematicTableContainer extends Container {
inputSlot = new SlotItemHandler(te.inventory, 0, -9, 40) {
@Override
public boolean isItemValid(ItemStack stack) {
return AllItems.EMPTY_BLUEPRINT.typeOf(stack);
return AllItems.EMPTY_BLUEPRINT.typeOf(stack) || AllItems.BLUEPRINT_AND_QUILL.typeOf(stack);
}
};
@ -53,7 +53,7 @@ public class SchematicTableContainer extends Container {
addSlot(inputSlot);
addSlot(outputSlot);
// player Slots
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 9; ++col) {
@ -64,7 +64,7 @@ public class SchematicTableContainer extends Container {
for (int hotbarSlot = 0; hotbarSlot < 9; ++hotbarSlot) {
this.addSlot(new Slot(player.inventory, hotbarSlot, 12 + hotbarSlot * 18, 160));
}
detectAndSendChanges();
}
@ -86,9 +86,9 @@ public class SchematicTableContainer extends Container {
ItemStack stack = clickedSlot.getStack();
if (index < 2)
mergeItemStack(stack, 2, inventorySlots.size(), false);
else
else
mergeItemStack(stack, 0, 1, false);
return ItemStack.EMPTY;
}

View file

@ -592,6 +592,10 @@ public class SchematicannonTileEntity extends TileEntitySynced implements ITicka
return false;
if (!replaceTileEntities && toReplace.hasTileEntity())
return false;
// Block doesnt have a mapping (Water, lava, etc)
if (getItemForBlock(state).getItem() == Items.AIR && state.getBlock() != Blocks.AIR)
return false;
if (replaceMode == 3)
return true;
@ -611,7 +615,7 @@ public class SchematicannonTileEntity extends TileEntitySynced implements ITicka
for (LaunchedBlock b : flyingBlocks) {
b.update();
if (b.ticksRemaining <= 0 && !world.isRemote) {
world.setBlockState(b.target, b.state);
world.setBlockState(b.target, b.state, 18);
toRemove.add(b);
}
}
@ -709,6 +713,8 @@ public class SchematicannonTileEntity extends TileEntitySynced implements ITicka
if (!shouldPlace(pos.add(schematicAnchor), required))
continue;
ItemStack requiredItem = getItemForBlock(required);
if (requiredItem.isEmpty())
continue;
checklist.require(requiredItem.getItem());
blocksToPlace++;
}

View file

@ -0,0 +1,101 @@
package com.simibubi.create.gui;
import java.util.function.Consumer;
import org.lwjgl.glfw.GLFW;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.gui.widget.button.Button;
public class GuiTextPrompt extends AbstractSimiScreen {
private Consumer<String> callback;
private Consumer<String> abortCallback;
private TextFieldWidget nameField;
private Button confirm;
private Button abort;
private String buttonTextConfirm;
private String buttonTextAbort;
private String title;
private boolean confirmed;
public GuiTextPrompt(Consumer<String> callBack, Consumer<String> abortCallback) {
super();
this.callback = callBack;
this.abortCallback = abortCallback;
buttonTextConfirm = "Confirm";
buttonTextAbort = "Abort";
confirmed = false;
}
@Override
public void init() {
super.init();
setWindowSize(GuiResources.TEXT_INPUT.width, GuiResources.TEXT_INPUT.height + 30);
this.nameField = new TextFieldWidget(font, topLeftX + 33, topLeftY + 26, 128, 8, "");
this.nameField.setTextColor(-1);
this.nameField.setDisabledTextColour(-1);
this.nameField.setEnableBackgroundDrawing(false);
this.nameField.setMaxStringLength(35);
this.nameField.changeFocus(true);
confirm = new Button(topLeftX - 5, topLeftY + 50, 100, 20, buttonTextConfirm, button -> {
callback.accept(nameField.getText());
confirmed = true;
minecraft.displayGuiScreen(null);
});
abort = new Button(topLeftX + 100, topLeftY + 50, 100, 20, buttonTextAbort, button -> {
minecraft.displayGuiScreen(null);
});
widgets.add(confirm);
widgets.add(abort);
widgets.add(nameField);
}
@Override
public void renderWindow(int mouseX, int mouseY, float partialTicks) {
GuiResources.TEXT_INPUT.draw(this, topLeftX, topLeftY);
font.drawString(title, topLeftX + (sWidth / 2) - (font.getStringWidth(title) / 2), topLeftY + 11,
GuiResources.FONT_COLOR);
}
@Override
public void removed() {
if (!confirmed)
abortCallback.accept(nameField.getText());
super.removed();
}
public void setButtonTextConfirm(String buttonTextConfirm) {
this.buttonTextConfirm = buttonTextConfirm;
}
public void setButtonTextAbort(String buttonTextAbort) {
this.buttonTextAbort = buttonTextAbort;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public boolean keyPressed(int keyCode, int p_keyPressed_2_, int p_keyPressed_3_) {
if (keyCode == GLFW.GLFW_KEY_ENTER) {
confirm.onPress();
return true;
}
if (keyCode == 256 && this.shouldCloseOnEsc()) {
this.onClose();
return true;
}
return nameField.keyPressed(keyCode, p_keyPressed_2_, p_keyPressed_3_);
}
}

View file

@ -0,0 +1,11 @@
package com.simibubi.create.item;
import net.minecraft.item.Item;
public class ItemBlueprintAndQuill extends Item {
public ItemBlueprintAndQuill(Properties properties) {
super(properties);
}
}

View file

@ -0,0 +1,196 @@
package com.simibubi.create.schematic;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import org.apache.commons.io.IOUtils;
import org.lwjgl.glfw.GLFW;
import com.mojang.blaze3d.platform.GlStateManager;
import com.simibubi.create.AllItems;
import com.simibubi.create.gui.GuiOpener;
import com.simibubi.create.gui.GuiTextPrompt;
import com.simibubi.create.gui.Keyboard;
import com.simibubi.create.utility.FilesHelper;
import com.simibubi.create.utility.RaycastHelper;
import com.simibubi.create.utility.TessellatorHelper;
import net.minecraft.block.Blocks;
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.nbt.CompoundNBT;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.MutableBoundingBox;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraft.util.text.StringTextComponent;
import net.minecraft.world.gen.feature.template.Template;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.InputEvent.MouseInputEvent;
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(value = Dist.CLIENT, bus = Bus.FORGE)
public class BlueprintAndQuillHandler {
static BlockPos firstPos;
static BlockPos selectedPos;
static int range = 10;
private static boolean active() {
return present() && AllItems.BLUEPRINT_AND_QUILL.typeOf(Minecraft.getInstance().player.getHeldItemMainhand());
}
private static boolean present() {
return Minecraft.getInstance() != null && Minecraft.getInstance().world != null
&& Minecraft.getInstance().currentScreen == null && !Minecraft.getInstance().player.isSneaking();
}
public static boolean onScroll(double delta) {
if (!active())
return false;
if (!Keyboard.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
return false;
range = (int) MathHelper.clamp(range + delta, 1, 100);
return true;
}
@SubscribeEvent
public static void onClick(MouseInputEvent event) {
if (event.getAction() != Keyboard.PRESS)
return;
if (event.getButton() != 1)
return;
if (!active() && !Minecraft.getInstance().player.isSneaking())
return;
if (selectedPos == null)
return;
if (Minecraft.getInstance().player.isSneaking()) {
firstPos = null;
return;
}
if (firstPos != null) {
GuiTextPrompt guiScreenIn = new GuiTextPrompt(BlueprintAndQuillHandler::saveSchematic, s -> {
firstPos = null;
});
guiScreenIn.setTitle("Enter a name for the Schematic:");
guiScreenIn.setButtonTextConfirm("Save");
guiScreenIn.setButtonTextAbort("Cancel");
GuiOpener.open(guiScreenIn);
return;
}
firstPos = selectedPos;
}
public static void saveSchematic(String string) {
Template t = new Template();
MutableBoundingBox bb = new MutableBoundingBox(firstPos, selectedPos);
t.takeBlocksFromWorld(Minecraft.getInstance().world, new BlockPos(bb.minX, bb.minY, bb.minZ),
new BlockPos(bb.getXSize(), bb.getYSize(), bb.getZSize()), false, Blocks.AIR);
if (string.isEmpty())
string = "My Schematic";
String folderPath = "schematics";
FilesHelper.createFolderIfMissing(folderPath);
String filename = FilesHelper.findFirstValidFilename(string, folderPath, "nbt");
String filepath = folderPath + "/" + filename;
OutputStream outputStream = null;
try {
outputStream = Files.newOutputStream(Paths.get(filepath), StandardOpenOption.CREATE);
CompoundNBT nbttagcompound = t.writeToNBT(new CompoundNBT());
CompressedStreamTools.writeCompressed(nbttagcompound, outputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null)
IOUtils.closeQuietly(outputStream);
}
firstPos = null;
Minecraft.getInstance().player.sendStatusMessage(new StringTextComponent("Saved as " + filepath), true);
}
@SubscribeEvent
public static void onRenderWorld(RenderWorldLastEvent event) {
if (!active())
return;
TessellatorHelper.prepareForDrawing();
GlStateManager.lineWidth(3);
GlStateManager.color4f(1, 1, 1, 1);
GlStateManager.disableTexture();
if (firstPos != null) {
MutableBoundingBox bb = new MutableBoundingBox(firstPos, firstPos.add(1, 1, 1));
BlockPos min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
drawBox(min, max);
}
if (selectedPos != null) {
MutableBoundingBox bb = new MutableBoundingBox(selectedPos, selectedPos.add(1, 1, 1));
BlockPos min = new BlockPos(bb.minX, bb.minY, bb.minZ);
BlockPos max = new BlockPos(bb.maxX, bb.maxY, bb.maxZ);
drawBox(min, max);
if (firstPos != null) {
bb = new MutableBoundingBox(firstPos, selectedPos);
min = new BlockPos(bb.minX, bb.minY, bb.minZ);
max = new BlockPos(bb.maxX + 1, bb.maxY + 1, bb.maxZ + 1);
drawBox(min, max);
}
}
GlStateManager.lineWidth(1);
GlStateManager.enableTexture();
TessellatorHelper.cleanUpAfterDrawing();
}
protected static void drawBox(BlockPos min, BlockPos max) {
WorldRenderer.drawBoundingBox(min.getX() - 1 / 16d, min.getY() - 1 / 16d, min.getZ() - 1 / 16d,
max.getX() + 1 / 16d, max.getY() + 1 / 16d, max.getZ() + 1 / 16d, .3f, .4f, 1, 1);
}
@SubscribeEvent
public static void onClientTick(ClientTickEvent event) {
if (!active())
return;
ClientPlayerEntity player = Minecraft.getInstance().player;
selectedPos = null;
if (Keyboard.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL)) {
selectedPos = new BlockPos(player.getEyePosition(Minecraft.getInstance().getRenderPartialTicks())
.add(player.getLookVec().scale(range)));
} else {
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;
}
}
}
}

View file

@ -1,9 +1,10 @@
{
"item.create.symmetry_wand": "Staff of Symmetry",
"item.create.empty_blueprint": "Empty Schematic",
"item.create.blueprint_and_quill": "Schematic and Quill",
"item.create.blueprint": "Schematic",
"block.create.schematicannon": "SchematiCannon 9000",
"block.create.schematicannon": "Schematicannon",
"block.create.schematic_table": "Schematic Table",
"block.create.creative_crate": "SchematiCannon Creatifier",
"block.create.creative_crate": "Schematicannon Creatifier",
"itemGroup.create": "Create"
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "create:item/blueprint_and_quill"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B