From 65b52e76ecc41d210ebb533a052ef363e0eb8fcb Mon Sep 17 00:00:00 2001 From: dfsek Date: Sun, 17 Oct 2021 15:23:38 -0700 Subject: [PATCH] Initial commit --- common/addons/config-structure/README.md | 3 + .../addons/config-structure/build.gradle.kts | 3 + .../addons/structure/BiomeStructures.java | 19 +++ .../structure/BiomeStructuresTemplate.java | 23 ++++ .../addons/structure/StructureAddon.java | 29 +++++ .../addons/structure/StructureFactory.java | 13 ++ .../addons/structure/StructurePopulator.java | 53 +++++++++ .../addons/structure/StructureTemplate.java | 44 +++++++ .../addons/structure/TerraStructure.java | 40 +++++++ .../command/AsyncStructureFinder.java | 111 ++++++++++++++++++ .../command/structure/StructureCommand.java | 34 ++++++ .../structure/StructureExportCommand.java | 106 +++++++++++++++++ .../structure/StructureLoadCommand.java | 85 ++++++++++++++ .../structure/StructureLocateCommand.java | 75 ++++++++++++ .../argument/ScriptArgumentParser.java | 19 +++ .../argument/StructureArgumentParser.java | 19 +++ .../completer/RotationCompleter.java | 15 +++ .../structure/completer/ScriptCompleter.java | 23 ++++ .../completer/StructureCompleter.java | 23 ++++ .../structure/structures/loot/Entry.java | 103 ++++++++++++++++ .../structures/loot/LootTableImpl.java | 73 ++++++++++++ .../structure/structures/loot/Pool.java | 63 ++++++++++ .../loot/functions/AmountFunction.java | 40 +++++++ .../loot/functions/DamageFunction.java | 47 ++++++++ .../loot/functions/EnchantFunction.java | 72 ++++++++++++ .../loot/functions/LootFunction.java | 22 ++++ 26 files changed, 1157 insertions(+) create mode 100644 common/addons/config-structure/README.md create mode 100644 common/addons/config-structure/build.gradle.kts create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructures.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructuresTemplate.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureAddon.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureFactory.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructurePopulator.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureTemplate.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/TerraStructure.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/AsyncStructureFinder.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureCommand.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureExportCommand.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLoadCommand.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLocateCommand.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/ScriptArgumentParser.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/StructureArgumentParser.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/RotationCompleter.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/ScriptCompleter.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/StructureCompleter.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Entry.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/LootTableImpl.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Pool.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/AmountFunction.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/DamageFunction.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/EnchantFunction.java create mode 100644 common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/LootFunction.java diff --git a/common/addons/config-structure/README.md b/common/addons/config-structure/README.md new file mode 100644 index 000000000..60e62972a --- /dev/null +++ b/common/addons/config-structure/README.md @@ -0,0 +1,3 @@ +# config-structure + +Registers the default configuration for Terra Structures, `STRUCTURE`. \ No newline at end of file diff --git a/common/addons/config-structure/build.gradle.kts b/common/addons/config-structure/build.gradle.kts new file mode 100644 index 000000000..a5227add5 --- /dev/null +++ b/common/addons/config-structure/build.gradle.kts @@ -0,0 +1,3 @@ +dependencies { + "shadedApi"("com.googlecode.json-simple:json-simple:1.1.1") +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructures.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructures.java new file mode 100644 index 000000000..69c7ae792 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructures.java @@ -0,0 +1,19 @@ +package com.dfsek.terra.addons.structure; + +import java.util.Set; + +import com.dfsek.terra.api.properties.Properties; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; + + +public class BiomeStructures implements Properties { + private final Set structures; + + public BiomeStructures(Set structures) { + this.structures = structures; + } + + public Set getStructures() { + return structures; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructuresTemplate.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructuresTemplate.java new file mode 100644 index 000000000..0af5a5e5b --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/BiomeStructuresTemplate.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.structure; + +import com.dfsek.tectonic.annotations.Default; +import com.dfsek.tectonic.annotations.Value; +import com.dfsek.tectonic.loading.object.ObjectTemplate; + +import java.util.Collections; +import java.util.Set; + +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; + + +public class BiomeStructuresTemplate implements ObjectTemplate { + @Value("structures") + @Default + private @Meta Set<@Meta ConfiguredStructure> structures = Collections.emptySet(); + + @Override + public BiomeStructures get() { + return new BiomeStructures(structures); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureAddon.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureAddon.java new file mode 100644 index 000000000..0e1f46aca --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureAddon.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.addons.structure; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.addon.TerraAddon; +import com.dfsek.terra.api.addon.annotations.Addon; +import com.dfsek.terra.api.addon.annotations.Author; +import com.dfsek.terra.api.addon.annotations.Version; +import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; +import com.dfsek.terra.api.event.functional.FunctionalEventHandler; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; + + +@Addon("config-structure") +@Version("1.0.0") +@Author("Terra") +public class StructureAddon extends TerraAddon { + @Inject + private Platform platform; + + @Override + public void initialize() { + platform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigPackPreLoadEvent.class) + .then(event -> event.getPack().applyLoader(ConfiguredStructure.class, (t, o, l) -> null)) + .failThrough(); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureFactory.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureFactory.java new file mode 100644 index 000000000..6989429dc --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureFactory.java @@ -0,0 +1,13 @@ +package com.dfsek.terra.addons.structure; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.config.ConfigFactory; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; + + +public class StructureFactory implements ConfigFactory { + @Override + public ConfiguredStructure build(StructureTemplate config, Platform platform) { + return new TerraStructure(config.getStructures(), config.getY(), config.getSpawn()); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructurePopulator.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructurePopulator.java new file mode 100644 index 000000000..ca0ff0b65 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructurePopulator.java @@ -0,0 +1,53 @@ +package com.dfsek.terra.addons.structure; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; +import org.jetbrains.annotations.NotNull; + +import java.util.Random; + +import com.dfsek.terra.api.config.WorldConfig; +import com.dfsek.terra.api.profiler.ProfileFrame; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; +import com.dfsek.terra.api.structure.rotation.Rotation; +import com.dfsek.terra.api.util.PopulationUtil; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.Chunk; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.generator.Chunkified; +import com.dfsek.terra.api.world.generator.GenerationStage; + + +public class StructurePopulator implements GenerationStage, Chunkified { + private final Platform platform; + + public StructurePopulator(Platform platform) { + this.platform = platform; + } + + @SuppressWarnings("try") + @Override + public void populate(@NotNull World world, @NotNull Chunk chunk) { + try(ProfileFrame ignore = platform.getProfiler().profile("structure")) { + if(world.getConfig().disableStructures()) return; + + int cx = (chunk.getX() << 4); + int cz = (chunk.getZ() << 4); + BiomeProvider provider = world.getBiomeProvider(); + WorldConfig config = world.getConfig(); + for(ConfiguredStructure conf : config.getRegistry(TerraStructure.class).entries()) { + Vector3 spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()); + + if(!provider.getBiome(spawn, world.getSeed()).getContext().get(BiomeStructures.class).getStructures().contains(conf)) { + continue; + } + Random random = new Random(PopulationUtil.getCarverChunkSeed(FastMath.floorDiv(spawn.getBlockX(), 16), + FastMath.floorDiv(spawn.getBlockZ(), 16), world.getSeed())); + conf.getStructure().get(random).generate(spawn.setY(conf.getSpawnStart().get(random)), world, chunk, random, + Rotation.fromDegrees(90 * random.nextInt(4))); + } + } + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureTemplate.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureTemplate.java new file mode 100644 index 000000000..b9e86647c --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/StructureTemplate.java @@ -0,0 +1,44 @@ +package com.dfsek.terra.addons.structure; + +import com.dfsek.tectonic.annotations.Final; +import com.dfsek.tectonic.annotations.Value; + +import com.dfsek.terra.api.config.AbstractableTemplate; +import com.dfsek.terra.api.config.meta.Meta; +import com.dfsek.terra.api.structure.Structure; +import com.dfsek.terra.api.structure.StructureSpawn; +import com.dfsek.terra.api.util.Range; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +@SuppressWarnings({ "unused", "FieldMayBeFinal" }) +public class StructureTemplate implements AbstractableTemplate { + @Value("id") + @Final + private String id; + + @Value("scripts") + private @Meta ProbabilityCollection<@Meta Structure> structure; + + @Value("spawn.start") + private @Meta Range y; + + @Value("spawn") + private @Meta StructureSpawn spawn; + + public String getID() { + return id; + } + + public ProbabilityCollection getStructures() { + return structure; + } + + public Range getY() { + return y; + } + + public StructureSpawn getSpawn() { + return spawn; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/TerraStructure.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/TerraStructure.java new file mode 100644 index 000000000..283a45268 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/TerraStructure.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.addons.structure; + +import com.dfsek.terra.api.structure.Structure; +import com.dfsek.terra.api.structure.StructureSpawn; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; +import com.dfsek.terra.api.util.Range; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +public class TerraStructure implements ConfiguredStructure { + private final ProbabilityCollection structure; + private final Range spawnStart; + private final StructureSpawn spawn; + + public TerraStructure(ProbabilityCollection structures, Range spawnStart, StructureSpawn spawn) { + this.structure = structures; + this.spawnStart = spawnStart; + this.spawn = spawn; + } + + @Override + public ProbabilityCollection getStructure() { + return structure; + } + + @Override + public Range getSpawnStart() { + return spawnStart; + } + + @Override + public StructureSpawn getSpawn() { + return spawn; + } + + @Override + public String getID() { + return null; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/AsyncStructureFinder.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/AsyncStructureFinder.java new file mode 100644 index 000000000..9cf44879e --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/AsyncStructureFinder.java @@ -0,0 +1,111 @@ +package com.dfsek.terra.addons.structure.command; + +import com.dfsek.terra.api.Platform; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; +import com.dfsek.terra.api.util.vector.Vector3; +import com.dfsek.terra.api.world.World; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; + + +public class AsyncStructureFinder implements Runnable { + protected final BiomeProvider provider; + protected final ConfiguredStructure target; + protected final int startRadius; + protected final int maxRadius; + protected final int centerX; + protected final int centerZ; + protected final World world; + protected final Platform platform; + private final Consumer callback; + protected int searchSize = 1; + + public AsyncStructureFinder(BiomeProvider provider, ConfiguredStructure target, @NotNull Vector3 origin, World world, int startRadius, + int maxRadius, Consumer callback, Platform platform) { + //setSearchSize(target.getSpawn().getWidth() + 2 * target.getSpawn().getSeparation()); + this.provider = provider; + this.target = target; + this.platform = platform; + this.startRadius = startRadius; + this.maxRadius = maxRadius; + this.centerX = origin.getBlockX(); + this.centerZ = origin.getBlockZ(); + this.world = world; + this.callback = callback; + } + + public Vector3 finalizeVector(Vector3 orig) { + return orig;//target.getSpawn().getChunkSpawn(orig.getBlockX(), orig.getBlockZ(), world.getSeed()); + } + + @Override + public void run() { + int x = centerX; + int z = centerZ; + + x /= searchSize; + z /= searchSize; + + int run = 1; + boolean toggle = true; + boolean found = false; + + main: + for(int i = startRadius; i < maxRadius; i++) { + for(int j = 0; j < run; j++) { + if(isValid(x, z, target)) { + found = true; + break main; + } + if(toggle) x += 1; + else x -= 1; + } + for(int j = 0; j < run; j++) { + if(isValid(x, z, target)) { + found = true; + break main; + } + if(toggle) z += 1; + else z -= 1; + } + run++; + toggle = !toggle; + } + Vector3 finalSpawn = found ? finalizeVector(new Vector3(x, 0, z)) : null; + callback.accept(finalSpawn); + } + + public boolean isValid(int x, int z, ConfiguredStructure target) { + //Vector3 spawn = target.getSpawn().getChunkSpawn(x, z, world.getSeed()); + //if(!((UserDefinedBiome) provider.getBiome(spawn)).getConfig().getStructures().contains(target)) return false; + //Random random = new Random(PopulationUtil.getCarverChunkSeed(FastMath.floorDiv(spawn.getBlockX(), 16), FastMath.floorDiv(spawn + // .getBlockZ(), 16), world.getSeed())); + //return target.getStructure().get(random).test(spawn.setY(target.getSpawnStart().get(random)), world, random, Rotation + // .fromDegrees(90 * random.nextInt(4))); + return false; + } + + public ConfiguredStructure getTarget() { + return target; + } + + public World getWorld() { + return world; + } + + public BiomeProvider getProvider() { + return provider; + } + + public int getSearchSize() { + return searchSize; + } + + public void setSearchSize(int searchSize) { + this.searchSize = searchSize; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureCommand.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureCommand.java new file mode 100644 index 000000000..9b5665688 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureCommand.java @@ -0,0 +1,34 @@ +package com.dfsek.terra.addons.structure.command.structure; + +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.Subcommand; +import com.dfsek.terra.api.entity.CommandSender; + + +@Command( + subcommands = { + @Subcommand( + clazz = StructureExportCommand.class, + value = "export", + aliases = "ex" + ), + @Subcommand( + clazz = StructureLoadCommand.class, + value = "load", + aliases = "ld" + ), + @Subcommand( + clazz = StructureLocateCommand.class, + value = "locate", + aliases = "l" + ) + }, + usage = "/te structure" +) +public class StructureCommand implements CommandTemplate { + @Override + public void execute(CommandSender sender) { + //LangUtil.send("command.structure.main-menu", sender); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureExportCommand.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureExportCommand.java new file mode 100644 index 000000000..5b67a9575 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureExportCommand.java @@ -0,0 +1,106 @@ +package com.dfsek.terra.addons.structure.command.structure; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.entity.Sign; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.annotation.Argument; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget; +import com.dfsek.terra.api.command.annotation.type.DebugCommand; +import com.dfsek.terra.api.command.annotation.type.PlayerCommand; +import com.dfsek.terra.api.command.annotation.type.WorldCommand; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.util.generic.pair.Pair; +import com.dfsek.terra.api.util.vector.Vector3; + + +@PlayerCommand +@WorldCommand +@DebugCommand +@Command(arguments = @Argument("id"), usage = "/terra structure export ") +public class StructureExportCommand implements CommandTemplate { + @Inject + private Platform platform; + + @ArgumentTarget("id") + private String id; + + @Override + public void execute(CommandSender sender) { + Player player = (Player) sender; + + Pair l = platform.getWorldHandle().getSelectedLocation(player); + + Vector3 l1 = l.getLeft(); + Vector3 l2 = l.getRight(); + + StringBuilder scriptBuilder = new StringBuilder("id \"" + id + "\";\nnum y = 0;\n"); + + int centerX = 0; + int centerY = 0; + int centerZ = 0; + + for(int x = l1.getBlockX(); x <= l2.getBlockX(); x++) { + for(int y = l1.getBlockY(); y <= l2.getBlockY(); y++) { + for(int z = l1.getBlockZ(); z <= l2.getBlockZ(); z++) { + BlockEntity state = player.world().getBlockState(x, y, z); + if(state instanceof Sign) { + Sign sign = (Sign) state; + if(sign.getLine(0).equals("[TERRA]") && sign.getLine(1).equals("[CENTER]")) { + centerX = x - l1.getBlockX(); + centerY = y - l1.getBlockY(); + centerZ = z - l1.getBlockZ(); + } + } + } + } + } + + for(int x = l1.getBlockX(); x <= l2.getBlockX(); x++) { + for(int y = l1.getBlockY(); y <= l2.getBlockY(); y++) { + for(int z = l1.getBlockZ(); z <= l2.getBlockZ(); z++) { + + BlockState data = player.world().getBlockData(x, y, z); + if(data.isStructureVoid()) continue; + BlockEntity state = player.world().getBlockState(x, y, z); + if(state instanceof Sign) { + Sign sign = (Sign) state; + if(sign.getLine(0).equals("[TERRA]")) { + data = platform.getWorldHandle().createBlockData(sign.getLine(2) + sign.getLine(3)); + } + } + if(!data.isStructureVoid()) { + scriptBuilder.append("block(").append(x - l1.getBlockX() - centerX).append(", y + ").append( + y - l1.getBlockY() - centerY).append(", ").append(z - l1.getBlockZ() - centerZ).append(", ") + .append("\""); + scriptBuilder.append(data.getAsString()).append("\");\n"); + } + } + } + } + + File file = new File(platform.getDataFolder() + File.separator + "export" + File.separator + "structures", id + ".tesf"); + try { + file.getParentFile().mkdirs(); + file.createNewFile(); + } catch(IOException e) { + e.printStackTrace(); + } + try(BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + writer.write(scriptBuilder.toString()); + } catch(IOException e) { + e.printStackTrace(); + } + + sender.sendMessage("Exported structure to " + file.getAbsolutePath()); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLoadCommand.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLoadCommand.java new file mode 100644 index 000000000..8b2c6e3f1 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLoadCommand.java @@ -0,0 +1,85 @@ +package com.dfsek.terra.addons.structure.command.structure; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +import com.dfsek.terra.addons.structure.command.structure.argument.ScriptArgumentParser; +import com.dfsek.terra.addons.structure.command.structure.completer.RotationCompleter; +import com.dfsek.terra.addons.structure.command.structure.completer.ScriptCompleter; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.annotation.Argument; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.Switch; +import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget; +import com.dfsek.terra.api.command.annotation.inject.SwitchTarget; +import com.dfsek.terra.api.command.annotation.type.DebugCommand; +import com.dfsek.terra.api.command.annotation.type.PlayerCommand; +import com.dfsek.terra.api.command.annotation.type.WorldCommand; +import com.dfsek.terra.api.command.arg.IntegerArgumentParser; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.Structure; +import com.dfsek.terra.api.structure.rotation.Rotation; + + +@PlayerCommand +@DebugCommand +@WorldCommand +@Command(arguments = { + @Argument( + value = "structure", + tabCompleter = ScriptCompleter.class, + argumentParser = ScriptArgumentParser.class + ), + @Argument( + value = "rotation", + required = false, + tabCompleter = RotationCompleter.class, + argumentParser = IntegerArgumentParser.class, + defaultValue = "0" + ) +}, switches = @Switch(value = "chunk", + aliases = "c" +), usage = "/terra structure load [ROTATION] [-c]") +public class StructureLoadCommand implements CommandTemplate { + @ArgumentTarget("rotation") + private final Integer rotation = 0; + + @SwitchTarget("chunk") + private boolean chunk; + + @ArgumentTarget("structure") + private Structure script; + + @Inject + private Platform platform; + + @Override + public void execute(CommandSender sender) { + Player player = (Player) sender; + + long t = System.nanoTime(); + Random random = new Random(ThreadLocalRandom.current().nextLong()); + Rotation r; + try { + r = Rotation.fromDegrees(rotation); + } catch(Exception e) { + sender.sendMessage("Invalid rotation: " + rotation); + return; + } + if(script == null) { + sender.sendMessage("Invalid structure."); + return; + } + if(this.chunk) { + script.generate(player.position(), player.world(), player.world().getChunkAt(player.position()), random, r); + } else { + script.generate(player.position(), player.world(), random, r); + } + long l = System.nanoTime() - t; + + sender.sendMessage("Took " + ((double) l) / 1000000 + "ms"); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLocateCommand.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLocateCommand.java new file mode 100644 index 000000000..cede50303 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/StructureLocateCommand.java @@ -0,0 +1,75 @@ +package com.dfsek.terra.addons.structure.command.structure; + +import java.util.Locale; + +import com.dfsek.terra.addons.structure.command.AsyncStructureFinder; +import com.dfsek.terra.addons.structure.command.structure.argument.StructureArgumentParser; +import com.dfsek.terra.addons.structure.command.structure.completer.StructureCompleter; +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.annotation.Argument; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.Switch; +import com.dfsek.terra.api.command.annotation.inject.ArgumentTarget; +import com.dfsek.terra.api.command.annotation.inject.SwitchTarget; +import com.dfsek.terra.api.command.annotation.type.PlayerCommand; +import com.dfsek.terra.api.command.annotation.type.WorldCommand; +import com.dfsek.terra.api.command.arg.IntegerArgumentParser; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; +import com.dfsek.terra.api.util.vector.Vector3; + + +@PlayerCommand +@WorldCommand +@Command(arguments = { + @Argument( + value = "structure", + tabCompleter = StructureCompleter.class, + argumentParser = StructureArgumentParser.class + ), + @Argument( + value = "radius", + required = false, + defaultValue = "100", + argumentParser = IntegerArgumentParser.class + ) +}, switches = @Switch( + value = "teleport", + aliases = { "t", "tp" } +)) +public class StructureLocateCommand implements CommandTemplate { + @Inject + private Platform platform; + + @ArgumentTarget("structure") + private ConfiguredStructure structure; + + @ArgumentTarget("radius") + private Integer radius; + + @SwitchTarget("teleport") + private boolean teleport; + + @Override + public void execute(CommandSender sender) { + Player player = (Player) sender; + + new Thread(new AsyncStructureFinder(player.world().getBiomeProvider(), structure, + player.position().clone().multiply((1D / platform.getTerraConfig().getBiomeSearchResolution())), + player.world(), 0, radius, location -> { + if(location != null) { + sender.sendMessage( + String.format("The nearest %s is at [%d, ~, %d] (%.1f blocks away)", structure.getID().toLowerCase(Locale.ROOT), + location.getBlockX(), location.getBlockZ(), + location.add(new Vector3(0, player.position().getY(), 0)).distance(player.position()))); + if(teleport) { + platform.runPossiblyUnsafeTask( + () -> player.position(new Vector3(location.getX(), player.position().getY(), location.getZ()))); + } + } //else LangUtil.send("command.biome.unable-to-locate", sender); + }, platform), "Biome Location Thread").start(); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/ScriptArgumentParser.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/ScriptArgumentParser.java new file mode 100644 index 000000000..b1ca7e2e7 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/ScriptArgumentParser.java @@ -0,0 +1,19 @@ +package com.dfsek.terra.addons.structure.command.structure.argument; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.command.arg.ArgumentParser; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.Structure; + + +public class ScriptArgumentParser implements ArgumentParser { + @Inject + private Platform platform; + + @Override + public Structure parse(CommandSender sender, String arg) { + return ((Player) sender).world().getConfig().getRegistry(Structure.class).get(arg); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/StructureArgumentParser.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/StructureArgumentParser.java new file mode 100644 index 000000000..5c53464e7 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/argument/StructureArgumentParser.java @@ -0,0 +1,19 @@ +package com.dfsek.terra.addons.structure.command.structure.argument; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.command.arg.ArgumentParser; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; + + +public class StructureArgumentParser implements ArgumentParser { + @Inject + private Platform platform; + + @Override + public ConfiguredStructure parse(CommandSender sender, String arg) { + return ((Player) sender).world().getConfig().getRegistry(ConfiguredStructure.class).get(arg); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/RotationCompleter.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/RotationCompleter.java new file mode 100644 index 000000000..01d5ed287 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/RotationCompleter.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.addons.structure.command.structure.completer; + +import java.util.Arrays; +import java.util.List; + +import com.dfsek.terra.api.command.tab.TabCompleter; +import com.dfsek.terra.api.entity.CommandSender; + + +public class RotationCompleter implements TabCompleter { + @Override + public List complete(CommandSender sender) { + return Arrays.asList("0", "90", "180", "270"); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/ScriptCompleter.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/ScriptCompleter.java new file mode 100644 index 000000000..d48daf7a3 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/ScriptCompleter.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.structure.command.structure.completer; + +import java.util.List; +import java.util.stream.Collectors; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.command.tab.TabCompleter; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.Structure; + + +public class ScriptCompleter implements TabCompleter { + @Inject + private Platform platform; + + @Override + public List complete(CommandSender sender) { + return ((Player) sender).world().getConfig().getRegistry(Structure.class).entries().stream().map(Structure::getID).collect( + Collectors.toList()); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/StructureCompleter.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/StructureCompleter.java new file mode 100644 index 000000000..d5b1feb7e --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/command/structure/completer/StructureCompleter.java @@ -0,0 +1,23 @@ +package com.dfsek.terra.addons.structure.command.structure.completer; + +import java.util.ArrayList; +import java.util.List; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.command.tab.TabCompleter; +import com.dfsek.terra.api.entity.CommandSender; +import com.dfsek.terra.api.entity.Player; +import com.dfsek.terra.api.inject.annotations.Inject; +import com.dfsek.terra.api.structure.configured.ConfiguredStructure; + + +public class StructureCompleter implements TabCompleter { + @Inject + private Platform platform; + + @Override + public List complete(CommandSender sender) { + Player player = (Player) sender; + return new ArrayList<>(player.world().getConfig().getRegistry(ConfiguredStructure.class).keys()); + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Entry.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Entry.java new file mode 100644 index 000000000..c86959588 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Entry.java @@ -0,0 +1,103 @@ +package com.dfsek.terra.addons.structure.structures.loot; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.dfsek.terra.addons.structure.structures.loot.functions.AmountFunction; +import com.dfsek.terra.addons.structure.structures.loot.functions.DamageFunction; +import com.dfsek.terra.addons.structure.structures.loot.functions.EnchantFunction; +import com.dfsek.terra.addons.structure.structures.loot.functions.LootFunction; +import com.dfsek.terra.api.inventory.Item; +import com.dfsek.terra.api.inventory.ItemStack; + + +/** + * Representation of a single item entry within a Loot Table pool. + */ +public class Entry { + private final Item item; + private final long weight; + private final List functions = new ArrayList<>(); + + /** + * Instantiates an Entry from a JSON representation. + * + * @param entry The JSON Object to instantiate from. + */ + public Entry(JSONObject entry, Platform platform) { + String id = entry.get("name").toString(); + this.item = platform.getItemHandle().createItem(id); + + long weight1; + try { + weight1 = (long) entry.get("weight"); + } catch(NullPointerException e) { + weight1 = 1; + } + + this.weight = weight1; + if(entry.containsKey("functions")) { + for(Object function : (JSONArray) entry.get("functions")) { + switch(((String) ((JSONObject) function).get("function"))) { + case "minecraft:set_count", "set_count" -> { + Object loot = ((JSONObject) function).get("count"); + long max, min; + if(loot instanceof Long) { + max = (Long) loot; + min = (Long) loot; + } else { + max = (long) ((JSONObject) loot).get("max"); + min = (long) ((JSONObject) loot).get("min"); + } + functions.add(new AmountFunction(FastMath.toIntExact(min), FastMath.toIntExact(max))); + } + case "minecraft:set_damage", "set_damage" -> { + long maxDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("max"); + long minDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("min"); + functions.add(new DamageFunction(FastMath.toIntExact(minDamage), FastMath.toIntExact(maxDamage))); + } + case "minecraft:enchant_with_levels", "enchant_with_levels" -> { + long maxEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("max"); + long minEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("min"); + JSONArray disabled = null; + if(((JSONObject) function).containsKey("disabled_enchants")) + disabled = (JSONArray) ((JSONObject) function).get("disabled_enchants"); + functions.add( + new EnchantFunction(FastMath.toIntExact(minEnchant), FastMath.toIntExact(maxEnchant), disabled, platform)); + } + } + } + } + } + + /** + * Fetches a single ItemStack from the Entry, applying all functions to it. + * + * @param r The Random instance to apply functions with + * + * @return ItemStack - The ItemStack with all functions applied. + */ + public ItemStack getItem(Random r) { + ItemStack item = this.item.newItemStack(1); + for(LootFunction f : functions) { + item = f.apply(item, r); + } + return item; + } + + /** + * Gets the weight attribute of the Entry. + * + * @return long - The weight of the Entry. + */ + public long getWeight() { + return this.weight; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/LootTableImpl.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/LootTableImpl.java new file mode 100644 index 000000000..f32349a43 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/LootTableImpl.java @@ -0,0 +1,73 @@ +package com.dfsek.terra.addons.structure.structures.loot; + +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.dfsek.terra.api.Platform; +import com.dfsek.terra.api.inventory.Inventory; +import com.dfsek.terra.api.inventory.ItemStack; + + +/** + * Class representation of a Loot Table to populate chest loot. + */ +public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable { + private final List pools = new ArrayList<>(); + + /** + * Instantiates a LootTable from a JSON String. + * + * @param json The JSON String representing the loot table. + * + * @throws ParseException if malformed JSON is passed. + */ + public LootTableImpl(String json, Platform platform) throws ParseException { + JSONParser jsonParser = new JSONParser(); + Object tableJSON = jsonParser.parse(json); + JSONArray poolArray = (JSONArray) ((JSONObject) tableJSON).get("pools"); + for(Object pool : poolArray) { + pools.add(new Pool((JSONObject) pool, platform)); + } + } + + @Override + public void fillInventory(Inventory i, Random r) { + List loot = getLoot(r); + for(ItemStack stack : loot) { + int attempts = 0; + while(stack.getAmount() != 0 && attempts < 10) { + ItemStack newStack = stack.getType().newItemStack(stack.getAmount()); + newStack.setItemMeta(stack.getItemMeta()); + newStack.setAmount(1); + int slot = r.nextInt(i.getSize()); + ItemStack slotItem = i.getItem(slot); + if(slotItem == null) { + i.setItem(slot, newStack); + stack.setAmount(stack.getAmount() - 1); + } else if(slotItem.getType().equals(newStack.getType())) { + ItemStack dep = newStack.getType().newItemStack(newStack.getAmount()); + dep.setItemMeta(newStack.getItemMeta()); + dep.setAmount(newStack.getAmount() + slotItem.getAmount()); + i.setItem(slot, dep); + stack.setAmount(stack.getAmount() - 1); + } + attempts++; + } + } + } + + @Override + public List getLoot(Random r) { + List itemList = new ArrayList<>(); + for(Pool pool : pools) { + itemList.addAll(pool.getItems(r)); + } + return itemList; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Pool.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Pool.java new file mode 100644 index 000000000..22473a722 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/Pool.java @@ -0,0 +1,63 @@ +package com.dfsek.terra.addons.structure.structures.loot; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.dfsek.terra.api.inventory.ItemStack; +import com.dfsek.terra.api.util.collection.ProbabilityCollection; + + +/** + * Representation of a Loot Table pool, or a set of items to be fetched independently. + */ +public class Pool { + private final int max; + private final int min; + private final ProbabilityCollection entries; + + /** + * Instantiates a Pool from a JSON representation. + * + * @param pool The JSON Object to instantiate from. + */ + public Pool(JSONObject pool, Platform platform) { + entries = new ProbabilityCollection<>(); + Object amount = pool.get("rolls"); + if(amount instanceof Long) { + max = FastMath.toIntExact((Long) amount); + min = FastMath.toIntExact((Long) amount); + } else { + max = FastMath.toIntExact((Long) ((JSONObject) amount).get("max")); + min = FastMath.toIntExact((Long) ((JSONObject) amount).get("min")); + } + + for(Object entryJSON : (JSONArray) pool.get("entries")) { + Entry entry = new Entry((JSONObject) entryJSON, platform); + entries.add(entry, FastMath.toIntExact(entry.getWeight())); + } + } + + /** + * Fetches a list of items from the pool using the provided Random instance. + * + * @param r The Random instance to use. + * + * @return List<ItemStack> - The list of items fetched. + */ + public List getItems(Random r) { + + int rolls = r.nextInt(max - min + 1) + min; + List items = new ArrayList<>(); + for(int i = 0; i < rolls; i++) { + items.add(entries.get(r).getItem(r)); + } + return items; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/AmountFunction.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/AmountFunction.java new file mode 100644 index 000000000..c97ea4eaa --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/AmountFunction.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.addons.structure.structures.loot.functions; + + +import java.util.Random; + +import com.dfsek.terra.api.inventory.ItemStack; + + +/** + * Loot LootFunction fot setting the amount of an item. + */ +public class AmountFunction implements LootFunction { + private final int max; + private final int min; + + /** + * Instantiates an AmountFunction. + * + * @param min Minimum amount. + * @param max Maximum amount. + */ + public AmountFunction(int min, int max) { + this.min = min; + this.max = max; + } + + /** + * Applies the function to an ItemStack. + * + * @param original The ItemStack on which to apply the function. + * @param r The Random instance to use. + * + * @return - ItemStack - The mutated ItemStack. + */ + @Override + public ItemStack apply(ItemStack original, Random r) { + original.setAmount(r.nextInt(max - min + 1) + min); + return original; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/DamageFunction.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/DamageFunction.java new file mode 100644 index 000000000..d778f13c9 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/DamageFunction.java @@ -0,0 +1,47 @@ +package com.dfsek.terra.addons.structure.structures.loot.functions; + +import java.util.Random; + +import com.dfsek.terra.api.inventory.ItemStack; +import com.dfsek.terra.api.inventory.item.Damageable; +import com.dfsek.terra.api.inventory.item.ItemMeta; + + +/** + * Loot LootFunction for setting the damage on items in Loot Tables + */ +public class DamageFunction implements LootFunction { + private final int max; + private final int min; + + /** + * Instantiates a DamageFunction. + * + * @param min Minimum amount of damage (percentage, out of 100) + * @param max Maximum amount of damage (percentage, out of 100) + */ + public DamageFunction(int min, int max) { + this.min = min; + this.max = max; + } + + /** + * Applies the function to an ItemStack. + * + * @param original The ItemStack on which to apply the function. + * @param r The Random instance to use. + * + * @return - ItemStack - The mutated ItemStack. + */ + @Override + public ItemStack apply(ItemStack original, Random r) { + if(original == null) return null; + if(!original.isDamageable()) return original; + ItemMeta meta = original.getItemMeta(); + double itemDurability = (r.nextDouble() * (max - min)) + min; + Damageable damage = (Damageable) meta; + damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability())); + original.setItemMeta((ItemMeta) damage); + return original; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/EnchantFunction.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/EnchantFunction.java new file mode 100644 index 000000000..bf3c07a1d --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/EnchantFunction.java @@ -0,0 +1,72 @@ +package com.dfsek.terra.addons.structure.structures.loot.functions; + +import com.dfsek.terra.api.Platform; + +import net.jafama.FastMath; +import org.json.simple.JSONArray; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import com.dfsek.terra.api.inventory.ItemStack; +import com.dfsek.terra.api.inventory.item.Enchantment; +import com.dfsek.terra.api.inventory.item.ItemMeta; + + +public class EnchantFunction implements LootFunction { + private final int min; + private final int max; + private final JSONArray disabled; + private final Platform platform; + + + public EnchantFunction(int min, int max, JSONArray disabled, Platform platform) { + this.max = max; + this.min = min; + this.disabled = disabled; + this.platform = platform; + } + + /** + * Applies the function to an ItemStack. + * + * @param original The ItemStack on which to apply the function. + * @param r The Random instance to use. + * + * @return - ItemStack - The mutated ItemStack. + */ + @Override + public ItemStack apply(ItemStack original, Random r) { + if(original.getItemMeta() == null) return original; + + double enchant = (r.nextDouble() * (max - min)) + min; + List possible = new ArrayList<>(); + for(Enchantment ench : platform.getItemHandle().getEnchantments()) { + if(ench.canEnchantItem(original) && (disabled == null || !this.disabled.contains(ench.getID()))) { + possible.add(ench); + } + } + int numEnchant = (r.nextInt((int) FastMath.abs(enchant)) / 10 + 1); + Collections.shuffle(possible); + ItemMeta meta = original.getItemMeta(); + iter: + for(int i = 0; i < numEnchant && i < possible.size(); i++) { + Enchantment chosen = possible.get(i); + for(Enchantment ench : meta.getEnchantments().keySet()) { + if(chosen.conflictsWith(ench)) continue iter; + } + int lvl = r.nextInt(1 + (int) (((enchant / 40 > 1) ? 1 : enchant / 40) * (chosen.getMaxLevel()))); + try { + meta.addEnchantment(chosen, FastMath.max(lvl, 1)); + } catch(IllegalArgumentException e) { + platform.logger().warning( + "Attempted to enchant " + original.getType() + " with " + chosen + " at level " + FastMath.max(lvl, 1) + + ", but an unexpected exception occurred! Usually this is caused by a misbehaving enchantment plugin."); + } + } + original.setItemMeta(meta); + return original; + } +} diff --git a/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/LootFunction.java b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/LootFunction.java new file mode 100644 index 000000000..eaeebae71 --- /dev/null +++ b/common/addons/config-structure/src/main/java/com/dfsek/terra/addons/structure/structures/loot/functions/LootFunction.java @@ -0,0 +1,22 @@ +package com.dfsek.terra.addons.structure.structures.loot.functions; + + +import java.util.Random; + +import com.dfsek.terra.api.inventory.ItemStack; + + +/** + * Interface for mutating items in Loot Tables. + */ +public interface LootFunction { + /** + * Applies the function to an ItemStack. + * + * @param original The ItemStack on which to apply the function. + * @param r The Random instance to use. + * + * @return - ItemStack - The mutated ItemStack. + */ + ItemStack apply(ItemStack original, Random r); +}