diff --git a/common/src/main/java/com/dfsek/terra/api/platform/block/state/BlockState.java b/common/src/main/java/com/dfsek/terra/api/platform/block/state/BlockState.java index 74fdd2be9..47f5f51b7 100644 --- a/common/src/main/java/com/dfsek/terra/api/platform/block/state/BlockState.java +++ b/common/src/main/java/com/dfsek/terra/api/platform/block/state/BlockState.java @@ -16,4 +16,8 @@ public interface BlockState extends Handle { BlockData getBlockData(); boolean update(boolean applyPhysics); + + default void applyState(String state) { + // Do nothing by default. + } } diff --git a/common/src/main/java/com/dfsek/terra/api/platform/block/state/MobSpawner.java b/common/src/main/java/com/dfsek/terra/api/platform/block/state/MobSpawner.java new file mode 100644 index 000000000..6f2772135 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/platform/block/state/MobSpawner.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.api.platform.block.state; + +import com.dfsek.terra.api.platform.world.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +public interface MobSpawner extends BlockState { + EntityType getSpawnedType(); + + void setSpawnedType(@NotNull EntityType creatureType); + + int getDelay(); + + void setDelay(int delay); + + int getMinSpawnDelay(); + + void setMinSpawnDelay(int delay); + + int getMaxSpawnDelay(); + + void setMaxSpawnDelay(int delay); + + int getSpawnCount(); + + void setSpawnCount(int spawnCount); + + int getMaxNearbyEntities(); + + void setMaxNearbyEntities(int maxNearbyEntities); + + int getRequiredPlayerRange(); + + void setRequiredPlayerRange(int requiredPlayerRange); + + int getSpawnRange(); + + void setSpawnRange(int spawnRange); +} diff --git a/common/src/main/java/com/dfsek/terra/api/platform/block/state/SerialState.java b/common/src/main/java/com/dfsek/terra/api/platform/block/state/SerialState.java new file mode 100644 index 000000000..7c05c1253 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/platform/block/state/SerialState.java @@ -0,0 +1,94 @@ +package com.dfsek.terra.api.platform.block.state; + +import java.util.HashMap; +import java.util.Map; + +public class SerialState { + protected final Map> properties = new HashMap<>(); + + public SerialState() { + } + + public static Map parse(String props) { + String[] sep = props.split(","); + Map map = new HashMap<>(); + for(String item : sep) { + map.put(item.substring(0, item.indexOf('=')), item.substring(item.indexOf('=') + 1)); + } + return map; + } + + private void checkExists(String prop) { + if(!properties.containsKey(prop)) throw new IllegalArgumentException("No such property \"" + prop + "\""); + } + + private void checkType(Class clazz, Object o, String id) { + if(!clazz.isInstance(o)) + throw new IllegalArgumentException("Invalid data for property " + id + ": " + o); + } + + public void setProperty(String id, Object value) { + checkExists(id); + Property prop = properties.get(id); + checkType(prop.getValueClass(), value, id); + prop.setValue(value); + } + + public int getInteger(String id) { + checkExists(id); + Property prop = properties.get(id); + checkType(Integer.class, prop.getValue(), id); + return (Integer) prop.getValue(); + } + + public String getString(String id) { + checkExists(id); + Property prop = properties.get(id); + checkType(String.class, prop.getValue(), id); + return (String) prop.getValue(); + } + + public long getLong(String id) { + checkExists(id); + Property prop = properties.get(id); + checkType(Long.class, prop.getValue(), id); + return (Long) prop.getValue(); + } + + public boolean getBoolean(String id) { + checkExists(id); + Property prop = properties.get(id); + checkType(Boolean.class, prop.getValue(), id); + return (Boolean) prop.getValue(); + } + + @SuppressWarnings("unchecked") + public T get(String id, Class clazz) { + checkExists(id); + Property prop = properties.get(id); + checkType(clazz, prop.getValue(), id); + return (T) prop.getValue(); + } + + protected static class Property { + private final Class clazz; + private Object value; + + public Property(Class clazz) { + this.clazz = clazz; + } + + public Class getValueClass() { + return clazz; + } + + @SuppressWarnings("unchecked") + public T getValue() { + return (T) value; + } + + public void setValue(Object value) { + this.value = value; + } + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/platform/block/state/Sign.java b/common/src/main/java/com/dfsek/terra/api/platform/block/state/Sign.java new file mode 100644 index 000000000..0cc901816 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/platform/block/state/Sign.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.api.platform.block.state; + +import org.jetbrains.annotations.NotNull; + +public interface Sign extends BlockState { + @NotNull String[] getLines(); + + @NotNull String getLine(int index) throws IndexOutOfBoundsException; + + void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException; +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java b/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java index 2498a125e..c5bdc61ab 100644 --- a/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/StructureScript.java @@ -18,6 +18,7 @@ import com.dfsek.terra.api.structures.script.builders.MarkFunctionBuilder; import com.dfsek.terra.api.structures.script.builders.PullFunctionBuilder; import com.dfsek.terra.api.structures.script.builders.RandomFunctionBuilder; import com.dfsek.terra.api.structures.script.builders.RecursionsFunctionBuilder; +import com.dfsek.terra.api.structures.script.builders.StateFunctionBuilder; import com.dfsek.terra.api.structures.script.builders.StructureFunctionBuilder; import com.dfsek.terra.api.structures.script.builders.UnaryNumberFunctionBuilder; import com.dfsek.terra.api.structures.script.builders.UnaryStringFunctionBuilder; @@ -63,6 +64,7 @@ public class StructureScript { .registerFunction("entity", new EntityFunctionBuilder(main)) .registerFunction("getBiome", new BiomeFunctionBuilder(main)) .registerFunction("getBlock", new CheckBlockFunctionBuilder()) + .registerFunction("state", new StateFunctionBuilder(main)) .registerFunction("print", new UnaryStringFunctionBuilder(string -> Debug.info("[" + tempID + "] " + string))) .registerFunction("abs", new UnaryNumberFunctionBuilder(number -> FastMath.abs(number.doubleValue()))) .registerFunction("pow", new BinaryNumberFunctionBuilder((number, number2) -> FastMath.pow(number.doubleValue(), number2.doubleValue()))) diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StateFunctionBuilder.java b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StateFunctionBuilder.java new file mode 100644 index 000000000..0ebf71c9b --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/builders/StateFunctionBuilder.java @@ -0,0 +1,44 @@ +package com.dfsek.terra.api.structures.script.builders; + +import com.dfsek.terra.api.platform.TerraPlugin; +import com.dfsek.terra.api.structures.parser.exceptions.ParseException; +import com.dfsek.terra.api.structures.parser.lang.Returnable; +import com.dfsek.terra.api.structures.parser.lang.functions.FunctionBuilder; +import com.dfsek.terra.api.structures.script.functions.StateFunction; +import com.dfsek.terra.api.structures.tokenizer.Position; + +import java.util.List; + +public class StateFunctionBuilder implements FunctionBuilder { + private final TerraPlugin main; + + public StateFunctionBuilder(TerraPlugin main) { + this.main = main; + } + + @SuppressWarnings("unchecked") + @Override + public StateFunction build(List> argumentList, Position position) throws ParseException { + if(argumentList.size() < 4) throw new ParseException("Expected data", position); + return new StateFunction((Returnable) argumentList.get(0), (Returnable) argumentList.get(1), (Returnable) argumentList.get(2), (Returnable) argumentList.get(3), main, position); + } + + @Override + public int argNumber() { + return 4; + } + + @Override + public Returnable.ReturnType getArgument(int position) { + switch(position) { + case 0: + case 1: + case 2: + return Returnable.ReturnType.NUMBER; + case 3: + return Returnable.ReturnType.STRING; + default: + return null; + } + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/script/functions/StateFunction.java b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/StateFunction.java new file mode 100644 index 000000000..54f40462e --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/script/functions/StateFunction.java @@ -0,0 +1,49 @@ +package com.dfsek.terra.api.structures.script.functions; + +import com.dfsek.terra.api.math.vector.Vector2; +import com.dfsek.terra.api.math.vector.Vector3; +import com.dfsek.terra.api.platform.TerraPlugin; +import com.dfsek.terra.api.structures.parser.lang.ImplementationArguments; +import com.dfsek.terra.api.structures.parser.lang.Returnable; +import com.dfsek.terra.api.structures.parser.lang.functions.Function; +import com.dfsek.terra.api.structures.script.TerraImplementationArguments; +import com.dfsek.terra.api.structures.structure.RotationUtil; +import com.dfsek.terra.api.structures.structure.buffer.items.BufferedStateManipulator; +import com.dfsek.terra.api.structures.tokenizer.Position; +import net.jafama.FastMath; + +public class StateFunction implements Function { + private final Returnable data; + private final Returnable x, y, z; + private final Position position; + private final TerraPlugin main; + + public StateFunction(Returnable x, Returnable y, Returnable z, Returnable data, TerraPlugin main, Position position) { + this.position = position; + this.main = main; + this.data = data; + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public Void apply(ImplementationArguments implementationArguments) { + TerraImplementationArguments arguments = (TerraImplementationArguments) implementationArguments; + Vector2 xz = new Vector2(x.apply(implementationArguments).doubleValue(), z.apply(implementationArguments).doubleValue()); + RotationUtil.rotateVector(xz, arguments.getRotation()); + + arguments.getBuffer().addItem(new BufferedStateManipulator(main, data.apply(implementationArguments)), new Vector3(FastMath.roundToInt(xz.getX()), y.apply(implementationArguments).intValue(), FastMath.roundToInt(xz.getZ()))); + return null; + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public ReturnType returnType() { + return ReturnType.VOID; + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java new file mode 100644 index 000000000..c091c151c --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/BufferedStateManipulator.java @@ -0,0 +1,26 @@ +package com.dfsek.terra.api.structures.structure.buffer.items; + +import com.dfsek.terra.api.math.vector.Location; +import com.dfsek.terra.api.platform.TerraPlugin; +import com.dfsek.terra.api.platform.block.state.BlockState; + +public class BufferedStateManipulator implements BufferedItem { + private final TerraPlugin main; + private final String data; + + public BufferedStateManipulator(TerraPlugin main, String state) { + this.main = main; + this.data = state; + } + + @Override + public void paste(Location origin) { + BlockState state = origin.getBlock().getState(); + try { + state.applyState(data); + state.update(false); + } catch(ClassCastException e) { + main.getLogger().warning("Could not apply BlockState at " + origin + ": " + e.getMessage()); + } + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/state/BufferedStateManipulator.java b/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/state/BufferedStateManipulator.java deleted file mode 100644 index ec8f72c41..000000000 --- a/common/src/main/java/com/dfsek/terra/api/structures/structure/buffer/items/state/BufferedStateManipulator.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dfsek.terra.api.structures.structure.buffer.items.state; - -import com.dfsek.terra.api.math.vector.Location; -import com.dfsek.terra.api.platform.TerraPlugin; -import com.dfsek.terra.api.platform.block.state.BlockState; -import com.dfsek.terra.api.structures.structure.buffer.items.BufferedItem; - -@SuppressWarnings("unchecked") -public abstract class BufferedStateManipulator implements BufferedItem { - private final TerraPlugin main; - - protected BufferedStateManipulator(TerraPlugin main) { - this.main = main; - } - - @Override - public void paste(Location origin) { - BlockState state = origin.getBlock().getState(); - try { - apply((T) state); - } catch(ClassCastException e) { - main.getLogger().warning("Could not find expected BlockState at " + origin + "; found " + origin.getBlock().getBlockData().getAsString()); - } - } - - public abstract void apply(T state); -} diff --git a/common/src/main/java/com/dfsek/terra/generation/items/tree/TerraTree.java b/common/src/main/java/com/dfsek/terra/generation/items/tree/TerraTree.java index 9fc9e09b0..c01875d76 100644 --- a/common/src/main/java/com/dfsek/terra/generation/items/tree/TerraTree.java +++ b/common/src/main/java/com/dfsek/terra/generation/items/tree/TerraTree.java @@ -22,13 +22,11 @@ public class TerraTree implements Tree { @Override public synchronized boolean plant(Location location, Random random) { - structure.get(random).execute(location.clone().add(0, yOffset, 0), random, Rotation.fromDegrees(90 * random.nextInt(4))); - return true; + return structure.get(random).execute(location.clone().add(0, yOffset, 0), random, Rotation.fromDegrees(90 * random.nextInt(4))); } @Override public MaterialSet getSpawnable() { return spawnable; } - } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/EventListener.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/EventListener.java index 91ad5b7cb..0c05fdc93 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/EventListener.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/listeners/EventListener.java @@ -55,8 +55,7 @@ public class EventListener implements Listener { TreeRegistry registry = c.getTreeRegistry(); Tree tree = registry.get(TREE_TYPE_STRING_TRANSFORMER.translate(e.getSpecies())); Debug.info("Overrode tree type: " + e.getSpecies()); - org.bukkit.Location location = e.getLocation().subtract(0, 1, 0); + org.bukkit.Location location = e.getLocation(); if(!tree.plant(new Location(bukkit, location.getX(), location.getY(), location.getZ()), new FastRandom())) block.setBlockData(data); - } } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitBlockState.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitBlockState.java index c20b6182b..bff9d1a67 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitBlockState.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitBlockState.java @@ -6,6 +6,7 @@ import com.dfsek.terra.api.platform.block.state.BlockState; import com.dfsek.terra.bukkit.world.block.BukkitBlock; import com.dfsek.terra.bukkit.world.block.data.BukkitBlockData; import org.bukkit.block.Container; +import org.bukkit.block.Sign; public class BukkitBlockState implements BlockState { private final org.bukkit.block.BlockState delegate; @@ -16,6 +17,7 @@ public class BukkitBlockState implements BlockState { public static BukkitBlockState newInstance(org.bukkit.block.BlockState block) { if(block instanceof Container) return new BukkitContainer((Container) block); + if(block instanceof Sign) return new BukkitSign((Sign) block); return new BukkitBlockState(block); } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitContainer.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitContainer.java index d68254b36..c95040f8c 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitContainer.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitContainer.java @@ -5,6 +5,7 @@ import com.dfsek.terra.api.platform.inventory.Inventory; import com.dfsek.terra.bukkit.world.inventory.BukkitInventory; public class BukkitContainer extends BukkitBlockState implements Container { + protected BukkitContainer(org.bukkit.block.Container block) { super(block); } @@ -13,4 +14,5 @@ public class BukkitContainer extends BukkitBlockState implements Container { public Inventory getInventory() { return new BukkitInventory(((org.bukkit.block.Container) getHandle()).getInventory()); } + } diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java new file mode 100644 index 000000000..f14eacf1f --- /dev/null +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitSign.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.bukkit.world.block.state; + +import com.dfsek.terra.api.platform.block.state.SerialState; +import com.dfsek.terra.api.platform.block.state.Sign; +import org.jetbrains.annotations.NotNull; + +public class BukkitSign extends BukkitBlockState implements Sign { + protected BukkitSign(org.bukkit.block.Sign block) { + super(block); + } + + @Override + public @NotNull String[] getLines() { + return ((org.bukkit.block.Sign) getHandle()).getLines(); + } + + @Override + public @NotNull String getLine(int index) throws IndexOutOfBoundsException { + return ((org.bukkit.block.Sign) getHandle()).getLine(index); + } + + @Override + public void setLine(int index, @NotNull String line) throws IndexOutOfBoundsException { + ((org.bukkit.block.Sign) getHandle()).setLine(index, line); + } + + + @Override + public void applyState(String state) { + SerialState.parse(state).forEach((k, v) -> setLine(Integer.parseInt(k), v)); + } +}