From 502a231e6f26949818e7ad2095c3bc4592584cfd Mon Sep 17 00:00:00 2001 From: Alex 'mcmonkey' Goodwin Date: Fri, 23 Apr 2021 13:18:39 -0700 Subject: [PATCH] schematic entities --- .../commands/world/SchematicCommand.java | 55 +++++--- .../utilities/blocks/CuboidBlockSet.java | 131 +++++++++++++++++- .../blocks/SpongeSchematicHelper.java | 9 ++ 3 files changed, 174 insertions(+), 21 deletions(-) diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java index 5e328d52fc..373ac7493a 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java +++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/world/SchematicCommand.java @@ -40,8 +40,8 @@ public class SchematicCommand extends AbstractCommand implements Holdable, Liste public SchematicCommand() { setName("schematic"); - setSyntax("schematic [create/load/unload/rotate (angle:<#>)/paste (fake_to:|... fake_duration:)/save/flip_x/flip_y/flip_z) (noair) (mask:|...)] [name:] (filename:) () () (delayed) (max_delay_ms:<#>)"); - setRequiredArguments(2, 11); + setSyntax("schematic [create/load/unload/rotate (angle:<#>)/paste (fake_to:|... fake_duration:)/save/flip_x/flip_y/flip_z) (noair) (mask:|...)] [name:] (filename:) () () (delayed) (max_delay_ms:<#>) (entities)"); + setRequiredArguments(2, 12); TagManager.registerTagHandler(new TagRunnable.RootForm() { @Override public void run(ReplaceableTagEvent event) { @@ -56,10 +56,10 @@ public void run(ReplaceableTagEvent event) { // <--[command] // @Name Schematic - // @Syntax schematic [create/load/unload/rotate (angle:<#>)/paste (fake_to:|... fake_duration:)/save/flip_x/flip_y/flip_z) (noair) (mask:|...)] [name:] (filename:) () () (delayed) (max_delay_ms:<#>) + // @Syntax schematic [create/load/unload/rotate (angle:<#>)/paste (fake_to:|... fake_duration:)/save/flip_x/flip_y/flip_z) (noair) (mask:|...)] [name:] (filename:) () () (delayed) (max_delay_ms:<#>) (entities) // @Group world // @Required 2 - // @Maximum 11 + // @Maximum 12 // @Short Creates, loads, pastes, and saves schematics (Sets of blocks). // // @Description @@ -94,6 +94,9 @@ public void run(ReplaceableTagEvent event) { // block set, instead of actually modifying the blocks in the world. // This takes an optional duration as "fake_duration" for how long the fake blocks should remain. // + // The "create" and "paste" options allow the "entities" argument to be specified - when used, entities will be copied or pasted. + // At current time, entity types included will be: Paintings, ItemFrames. + // // The schematic command is ~waitable as an alternative to 'delayed' argument. Refer to <@link language ~waitable>. // // @Tags @@ -183,6 +186,10 @@ else if (!scriptEntry.hasObject("noair") && arg.matches("noair")) { scriptEntry.addObject("noair", new ElementTag("true")); } + else if (!scriptEntry.hasObject("entities") + && arg.matches("entities")) { + scriptEntry.addObject("entities", new ElementTag("true")); + } else if (!scriptEntry.hasObject("mask") && arg.matchesPrefix("mask") && arg.matchesArgumentList(MaterialTag.class)) { @@ -200,7 +207,7 @@ else if (!scriptEntry.hasObject("fake_duration") } else if (!scriptEntry.hasObject("location") && arg.matchesArgumentType(LocationTag.class)) { - scriptEntry.addObject("location", arg.asType(LocationTag.class)); + scriptEntry.addObject("location", arg.asType(LocationTag.class).getBlockLocation()); } else if (!scriptEntry.hasObject("cuboid") && arg.matchesArgumentType(CuboidTag.class)) { @@ -231,23 +238,15 @@ public void execute(final ScriptEntry scriptEntry) { ElementTag noair = scriptEntry.getElement("noair"); ElementTag delayed = scriptEntry.getElement("delayed"); ElementTag maxDelayMs = scriptEntry.getElement("max_delay_ms"); + ElementTag copyEntities = scriptEntry.getElement("entities"); LocationTag location = scriptEntry.getObjectTag("location"); List mask = (List) scriptEntry.getObject("mask"); List fakeTo = (List) scriptEntry.getObject("fake_to"); DurationTag fakeDuration = scriptEntry.getObjectTag("fake_duration"); CuboidTag cuboid = scriptEntry.getObjectTag("cuboid"); if (scriptEntry.dbCallShouldDebug()) { - Debug.report(scriptEntry, getName(), type.debug() - + name.debug() - + (location != null ? location.debug() : "") - + (filename != null ? filename.debug() : "") - + (cuboid != null ? cuboid.debug() : "") - + (angle != null ? angle.debug() : "") - + (noair != null ? noair.debug() : "") - + (delayed != null ? delayed.debug() + maxDelayMs.debug() : "") - + (mask != null ? ArgumentHelper.debugList("mask", mask) : "") - + (fakeTo != null ? ArgumentHelper.debugList("fake_to", fakeTo) : "") - + (fakeDuration != null ? fakeDuration.debug() : "")); + Debug.report(scriptEntry, getName(), type, name, location, filename, cuboid, angle, noair, delayed, maxDelayMs, fakeDuration, + (mask != null ? ArgumentHelper.debugList("mask", mask) : ""), (fakeTo != null ? ArgumentHelper.debugList("fake_to", fakeTo) : "")); } CuboidBlockSet set; Type ttype = Type.valueOf(type.asString()); @@ -273,6 +272,9 @@ public void execute(final ScriptEntry scriptEntry) { if (delayed != null && delayed.asBoolean()) { set = new CuboidBlockSet(); set.buildDelayed(cuboid, location, () -> { + if (copyEntities != null && copyEntities.asBoolean()) { + set.buildEntities(cuboid, location); + } schematics.put(name.asString().toUpperCase(), set); scriptEntry.setFinished(true); }, maxDelayMs.asLong()); @@ -280,6 +282,9 @@ public void execute(final ScriptEntry scriptEntry) { else { scriptEntry.setFinished(true); set = new CuboidBlockSet(cuboid, location); + if (copyEntities != null && copyEntities.asBoolean()) { + set.buildEntities(cuboid, location); + } schematics.put(name.asString().toUpperCase(), set); } } @@ -445,6 +450,11 @@ public void execute(final ScriptEntry scriptEntry) { input.centerLocation = location; input.noAir = noair != null && noair.asBoolean(); input.fakeTo = fakeTo; + if (fakeTo != null && copyEntities != null && copyEntities.asBoolean()) { + Debug.echoError(scriptEntry.getResidingQueue(), "Cannot fake paste entities currently."); + scriptEntry.setFinished(true); + return; + } if (fakeDuration == null) { fakeDuration = new DurationTag(0); } @@ -455,12 +465,21 @@ public void execute(final ScriptEntry scriptEntry) { input.mask.add(material.getMaterial()); } } + set = schematics.get(name.asString().toUpperCase()); if (delayed != null && delayed.asBoolean()) { - schematics.get(name.asString().toUpperCase()).setBlocksDelayed(() -> scriptEntry.setFinished(true), input, maxDelayMs.asLong()); + set.setBlocksDelayed(() -> { + if (copyEntities != null && copyEntities.asBoolean()) { + set.pasteEntities(location); + } + scriptEntry.setFinished(true); + }, input, maxDelayMs.asLong()); } else { + set.setBlocks(input); + if (copyEntities != null && copyEntities.asBoolean()) { + set.pasteEntities(location); + } scriptEntry.setFinished(true); - schematics.get(name.asString().toUpperCase()).setBlocks(input); } } catch (Exception ex) { diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java index e44d7db361..2ede431965 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/CuboidBlockSet.java @@ -1,14 +1,24 @@ package com.denizenscript.denizen.utilities.blocks; import com.denizenscript.denizen.Denizen; -import com.denizenscript.denizen.objects.CuboidTag; -import com.denizenscript.denizen.objects.LocationTag; -import com.denizenscript.denizen.objects.MaterialTag; +import com.denizenscript.denizen.objects.*; import com.denizenscript.denizen.scripts.commands.world.SchematicCommand; +import com.denizenscript.denizencore.objects.Mechanism; +import com.denizenscript.denizencore.objects.core.ElementTag; +import com.denizenscript.denizencore.objects.core.ListTag; +import com.denizenscript.denizencore.objects.core.MapTag; +import com.denizenscript.denizencore.utilities.CoreUtilities; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Painting; import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.util.Vector; + +import java.util.ArrayList; public class CuboidBlockSet implements BlockSet { @@ -85,6 +95,8 @@ public void run() { public int center_z; + public ListTag entities = null; + @Override public FullBlockData[] getBlocks() { return blocks; @@ -96,6 +108,97 @@ public CuboidTag getCuboid(Location loc) { return new CuboidTag(low, high); } + public static BlockFace rotateFaceOneBackwards(BlockFace face) { + switch (face) { + case NORTH: + return BlockFace.EAST; + case EAST: + return BlockFace.SOUTH; + case SOUTH: + return BlockFace.WEST; + case WEST: + return BlockFace.NORTH; + default: + return BlockFace.SELF; + } + } + + public static BlockFace rotateFaceOne(BlockFace face) { + switch (face) { + case NORTH: + return BlockFace.WEST; + case EAST: + return BlockFace.NORTH; + case SOUTH: + return BlockFace.EAST; + case WEST: + return BlockFace.SOUTH; + default: + return BlockFace.SELF; + } + } + + public void buildEntities(CuboidTag cuboid, Location center) { + entities = new ListTag(); + for (Entity ent : cuboid.getWorld().getEntities()) { + if (cuboid.isInsideCuboid(ent.getLocation())) { + EntityType type = ent.getType(); + if (type == EntityType.PAINTING || type == EntityType.ITEM_FRAME) { + MapTag data = new MapTag(); + data.putObject("entity", new EntityTag(ent).describe()); + data.putObject("rotation", new ElementTag(0)); + Vector offset = ent.getLocation().toVector().subtract(center.toVector()); + if (ent instanceof Painting) { // Compensate for painting locations being very stupid + //offset.setY(offset.getY() - 0.1); + //offset.add(rotateFaceOneBackwards(ent.getFacing()).getDirection().multiply(0.1)); + } + data.putObject("offset", new LocationTag(offset)); + entities.addObject(data); + } + } + } + } + + public void pasteEntities(LocationTag relative) { + if (entities == null) { + return; + } + for (MapTag data : entities.filter(MapTag.class, CoreUtilities.noDebugContext)) { + LocationTag offset = data.getObject("offset").asType(LocationTag.class, CoreUtilities.noDebugContext); + int rotation = data.getObject("rotation").asType(ElementTag.class, CoreUtilities.noDebugContext).asInt(); + EntityTag entity = data.getObject("entity").asType(EntityTag.class, CoreUtilities.noDebugContext); + if (entity == null || offset == null) { + continue; + } + entity = entity.duplicate(); + offset = offset.clone(); + if (rotation != 0) { + ArrayList mechs = new ArrayList<>(entity.getWaitingMechanisms().size()); + for (Mechanism mech : entity.getWaitingMechanisms()) { + if (mech.getName().equals("rotation")) { + String rotationName = mech.getValue().asString(); + BlockFace face = BlockFace.valueOf(rotationName.toUpperCase()); + for (int i = 0; i < rotation; i += 90) { + face = rotateFaceOne(face); + } + offset.add(face.getDirection().multiply(0.1)); // Compensate for hanging locations being very stupid + mechs.add(new Mechanism(new ElementTag("rotation"), new ElementTag(face.name()), CoreUtilities.noDebugContext)); + } + else { + mechs.add(new Mechanism(mech.getName(), mech.value, CoreUtilities.noDebugContext)); + } + } + entity.mechanisms = mechs; + } + else { + for (Mechanism mechanism : entity.mechanisms) { + mechanism.context = CoreUtilities.noDebugContext; + } + } + entity.spawnAt(relative.clone().add(offset)); + } + } + public void setBlockSingle(FullBlockData block, int x, int y, int z, InputParams input) { if (input.noAir && block.data.getMaterial() == Material.AIR) { return; @@ -164,7 +267,29 @@ public void setBlocks(InputParams input) { SchematicCommand.noPhys = false; } + public void rotateEntitiesOne() { + if (entities == null) { + return; + } + ListTag outEntities = new ListTag(); + for (MapTag data : entities.filter(MapTag.class, CoreUtilities.noDebugContext)) { + LocationTag offset = data.getObject("offset").asType(LocationTag.class, CoreUtilities.noDebugContext); + int rotation = data.getObject("rotation").asType(ElementTag.class, CoreUtilities.noDebugContext).asInt(); + offset = new LocationTag(null, offset.getZ(), offset.getY(), -offset.getX() + 1); + rotation += 90; + while (rotation >= 360) { + rotation -= 360; + } + data = data.duplicate(); + data.putObject("offset", offset); + data.putObject("rotation", new ElementTag(rotation)); + outEntities.addObject(data); + } + entities = outEntities; + } + public void rotateOne() { + rotateEntitiesOne(); FullBlockData[] bd = new FullBlockData[blocks.length]; int index = 0; int cx = center_x; diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/SpongeSchematicHelper.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/SpongeSchematicHelper.java index e8ddfe96f3..abb7273f1d 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/SpongeSchematicHelper.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/blocks/SpongeSchematicHelper.java @@ -5,6 +5,7 @@ import com.denizenscript.denizen.nms.util.jnbt.*; import com.denizenscript.denizen.objects.MaterialTag; import com.denizenscript.denizen.utilities.debugging.Debug; +import com.denizenscript.denizencore.objects.core.ListTag; import com.denizenscript.denizencore.utilities.CoreUtilities; import org.bukkit.Material; import org.bukkit.block.data.BlockData; @@ -23,6 +24,7 @@ public class SpongeSchematicHelper { // Referenced from WorldEdit source and Sponge schematic format v2 documentation + // Some values are custom and specific to Denizen public static CuboidBlockSet fromSpongeStream(InputStream is) { CuboidBlockSet cbs = new CuboidBlockSet(); try { @@ -34,6 +36,10 @@ public static CuboidBlockSet fromSpongeStream(InputStream is) { } CompoundTag schematicTag = (CompoundTag) rootTag.getTag(); Map schematic = schematicTag.getValue(); + if (schematic.containsKey("DenizenEntities")) { + String entities = getChildTag(schematic, "DenizenEntities", StringTag.class).getValue(); + cbs.entities = ListTag.valueOf(entities, CoreUtilities.errorButNoDebugContext); + } short width = getChildTag(schematic, "Width", ShortTag.class).getValue(); short length = getChildTag(schematic, "Length", ShortTag.class).getValue(); short height = getChildTag(schematic, "Height", ShortTag.class).getValue(); @@ -143,6 +149,9 @@ public static void saveToSpongeStream(CuboidBlockSet blockSet, OutputStream os) schematic.put("Length", new ShortTag((short) (blockSet.z_height))); schematic.put("Height", new ShortTag((short) (blockSet.y_length))); schematic.put("DenizenOffset", new IntArrayTag(new int[] {blockSet.center_x, blockSet.center_y, blockSet.center_z})); + if (blockSet.entities != null) { + schematic.put("DenizenEntities", new StringTag(blockSet.entities.toString())); + } Map palette = new HashMap<>(); ByteArrayOutputStream blocksBuffer = new ByteArrayOutputStream((blockSet.x_width) * (blockSet.y_length) * (blockSet.z_height)); ArrayList tileEntities = new ArrayList<>();