move modules to better directory structure

This commit is contained in:
dfsek
2021-07-08 23:08:56 -07:00
parent b609a0ba63
commit 40e28c5e4b
298 changed files with 19 additions and 14 deletions

View File

@@ -0,0 +1,59 @@
import com.dfsek.terra.configureCompilation
import com.dfsek.terra.configureDependencies
plugins {
`java-library`
`maven-publish`
idea
}
configureCompilation()
configureDependencies()
group = "com.dfsek.terra.common"
dependencies {
"shadedApi"(project(":common:api"))
"shadedApi"("org.apache.commons:commons-rng-core:1.3")
"shadedApi"("commons-io:commons-io:2.4")
"shadedApi"("com.dfsek:Paralithic:0.3.2")
"shadedApi"("com.dfsek:Tectonic:1.4.0")
"shadedApi"("net.jafama:jafama:2.3.2")
"shadedApi"("org.yaml:snakeyaml:1.27")
"shadedApi"("org.ow2.asm:asm:9.0")
"shadedApi"("commons-io:commons-io:2.6")
"shadedApi"("com.googlecode.json-simple:json-simple:1.1.1")
"shadedApi"("org.yaml:snakeyaml:1.27")
"compileOnly"("com.google.guava:guava:30.0-jre")
"testImplementation"("com.google.guava:guava:30.0-jre")
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
artifact(tasks["sourcesJar"])
artifact(tasks["jar"])
}
}
repositories {
val mavenUrl = "https://repo.codemc.io/repository/maven-releases/"
val mavenSnapshotUrl = "https://repo.codemc.io/repository/maven-snapshots/"
maven(mavenUrl) {
val mavenUsername: String? by project
val mavenPassword: String? by project
if (mavenUsername != null && mavenPassword != null) {
credentials {
username = mavenUsername
password = mavenPassword
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
package com.dfsek.terra.addons.structure;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.config.ConfigFactory;
import com.dfsek.terra.api.structure.ConfiguredStructure;
public class StructureFactory implements ConfigFactory<StructureTemplate, ConfiguredStructure> {
@Override
public ConfiguredStructure build(StructureTemplate config, TerraPlugin main) {
return new TerraStructure(config.getStructures(), config.getY(), config.getSpawn());
}
}

View File

@@ -0,0 +1,50 @@
package com.dfsek.terra.addons.structure;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.config.WorldConfig;
import com.dfsek.terra.api.profiler.ProfileFrame;
import com.dfsek.terra.api.structure.ConfiguredStructure;
import com.dfsek.terra.api.structure.rotation.Rotation;
import com.dfsek.terra.api.util.PopulationUtil;
import com.dfsek.terra.api.vector.Vector3;
import com.dfsek.terra.api.world.Chunk;
import com.dfsek.terra.api.world.TerraWorld;
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.TerraGenerationStage;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
public class StructurePopulator implements TerraGenerationStage, Chunkified {
private final TerraPlugin main;
public StructurePopulator(TerraPlugin main) {
this.main = main;
}
@SuppressWarnings("try")
@Override
public void populate(@NotNull World world, @NotNull Chunk chunk) {
TerraWorld tw = main.getWorld(world);
try(ProfileFrame ignore = main.getProfiler().profile("structure")) {
if(tw.getConfig().disableStructures()) return;
int cx = (chunk.getX() << 4);
int cz = (chunk.getZ() << 4);
if(!tw.isSafe()) return;
BiomeProvider provider = tw.getBiomeProvider();
WorldConfig config = tw.getConfig();
for(ConfiguredStructure conf : config.getRegistry(TerraStructure.class).entries()) {
Vector3 spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed());
//if(!((UserDefinedBiome) provider.getBiome(spawn)).getConfig().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)));
}
}
}
}

View File

@@ -0,0 +1,44 @@
package com.dfsek.terra.addons.structure;
import com.dfsek.tectonic.annotations.Abstractable;
import com.dfsek.tectonic.annotations.Value;
import com.dfsek.tectonic.config.ConfigTemplate;
import com.dfsek.terra.api.config.AbstractableTemplate;
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, ConfigTemplate {
@Value("id")
private String id;
@Value("scripts")
@Abstractable
private ProbabilityCollection<Structure> structure;
@Value("spawn.start")
@Abstractable
private Range y;
@Value("spawn")
@Abstractable
private StructureSpawn spawn;
public String getID() {
return id;
}
public ProbabilityCollection<Structure> getStructures() {
return structure;
}
public Range getY() {
return y;
}
public StructureSpawn getSpawn() {
return spawn;
}
}

View File

@@ -0,0 +1,39 @@
package com.dfsek.terra.addons.structure;
import com.dfsek.terra.api.structure.ConfiguredStructure;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.structure.StructureSpawn;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
import com.dfsek.terra.api.util.Range;
public class TerraStructure implements ConfiguredStructure {
private final ProbabilityCollection<Structure> structure;
private final Range spawnStart;
private final StructureSpawn spawn;
public TerraStructure(ProbabilityCollection<Structure> structures, Range spawnStart, StructureSpawn spawn) {
this.structure = structures;
this.spawnStart = spawnStart;
this.spawn = spawn;
}
@Override
public ProbabilityCollection<Structure> getStructure() {
return structure;
}
@Override
public Range getSpawnStart() {
return spawnStart;
}
@Override
public StructureSpawn getSpawn() {
return spawn;
}
@Override
public String getID() {
return null;
}
}

View File

@@ -0,0 +1,35 @@
package com.dfsek.terra.addons.structure.command;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.structure.ConfiguredStructure;
import com.dfsek.terra.api.structure.rotation.Rotation;
import com.dfsek.terra.api.util.PopulationUtil;
import com.dfsek.terra.api.vector.Vector3;
import com.dfsek.terra.api.world.World;
import com.dfsek.terra.api.world.biome.UserDefinedBiome;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
import java.util.function.Consumer;
public class AsyncStructureFinder extends AsyncFeatureFinder<ConfiguredStructure> {
public AsyncStructureFinder(BiomeProvider provider, ConfiguredStructure target, @NotNull Vector3 origin, World world, int startRadius, int maxRadius, Consumer<Vector3> callback, TerraPlugin main) {
super(provider, target, origin, world, startRadius, maxRadius, callback, main);
setSearchSize(target.getSpawn().getWidth() + 2 * target.getSpawn().getSeparation());
}
@Override
public Vector3 finalizeVector(Vector3 orig) {
return target.getSpawn().getChunkSpawn(orig.getBlockX(), orig.getBlockZ(), world.getSeed());
}
@Override
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)));
}
}

View File

@@ -0,0 +1,33 @@
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);
}
}

View File

@@ -0,0 +1,111 @@
package com.dfsek.terra.addons.structure.command.structure;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.entity.BlockEntity;
import com.dfsek.terra.api.block.entity.Sign;
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.injection.annotations.Inject;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.vector.Vector3;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
@PlayerCommand
@WorldCommand
@DebugCommand
@Command(
arguments = {
@Argument(
value = "id"
)
},
usage = "/terra structure export <ID>"
)
public class StructureExportCommand implements CommandTemplate {
@Inject
private TerraPlugin main;
@ArgumentTarget("id")
private String id;
@Override
public void execute(CommandSender sender) {
Player player = (Player) sender;
Pair<Vector3, Vector3> l = main.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 = main.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(main.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());
}
}

View File

@@ -0,0 +1,90 @@
package com.dfsek.terra.addons.structure.command.structure;
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.TerraPlugin;
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.injection.annotations.Inject;
import com.dfsek.terra.api.structure.Structure;
import com.dfsek.terra.api.structure.rotation.Rotation;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
@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 Integer rotation = 0;
@SwitchTarget("chunk")
private boolean chunk;
@ArgumentTarget("structure")
private Structure script;
@Inject
private TerraPlugin main;
@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");
}
}

View File

@@ -0,0 +1,73 @@
package com.dfsek.terra.addons.structure.command.structure;
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.TerraPlugin;
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.injection.annotations.Inject;
import com.dfsek.terra.api.structure.ConfiguredStructure;
import com.dfsek.terra.api.vector.Vector3;
import java.util.Locale;
@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 TerraPlugin main;
@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(main.getWorld(player.world()).getBiomeProvider(), structure, player.position().clone().multiply((1D / main.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) {
main.runPossiblyUnsafeTask(() -> player.position(new Vector3(location.getX(), player.position().getY(), location.getZ())));
}
} //else LangUtil.send("command.biome.unable-to-locate", sender);
}, main), "Biome Location Thread").start();
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.addons.structure.command.structure.argument;
import com.dfsek.terra.api.TerraPlugin;
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.injection.annotations.Inject;
import com.dfsek.terra.api.structure.Structure;
public class ScriptArgumentParser implements ArgumentParser<Structure> {
@Inject
private TerraPlugin main;
@Override
public Structure parse(CommandSender sender, String arg) {
return main.getWorld(((Player) sender).world()).getConfig().getRegistry(Structure.class).get(arg);
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.addons.structure.command.structure.argument;
import com.dfsek.terra.api.TerraPlugin;
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.injection.annotations.Inject;
import com.dfsek.terra.api.structure.ConfiguredStructure;
public class StructureArgumentParser implements ArgumentParser<ConfiguredStructure> {
@Inject
private TerraPlugin main;
@Override
public ConfiguredStructure parse(CommandSender sender, String arg) {
return main.getWorld(((Player) sender).world()).getConfig().getRegistry(ConfiguredStructure.class).get(arg);
}
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.addons.structure.command.structure.completer;
import com.dfsek.terra.api.command.tab.TabCompleter;
import com.dfsek.terra.api.entity.CommandSender;
import java.util.Arrays;
import java.util.List;
public class RotationCompleter implements TabCompleter {
@Override
public List<String> complete(CommandSender sender) {
return Arrays.asList("0", "90", "180", "270");
}
}

View File

@@ -0,0 +1,21 @@
package com.dfsek.terra.addons.structure.command.structure.completer;
import com.dfsek.terra.api.TerraPlugin;
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.injection.annotations.Inject;
import com.dfsek.terra.api.structure.Structure;
import java.util.List;
import java.util.stream.Collectors;
public class ScriptCompleter implements TabCompleter {
@Inject
private TerraPlugin main;
@Override
public List<String> complete(CommandSender sender) {
return main.getWorld(((Player) sender).world()).getConfig().getRegistry(Structure.class).entries().stream().map(Structure::getId).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.addons.structure.command.structure.completer;
import com.dfsek.terra.api.TerraPlugin;
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.injection.annotations.Inject;
import com.dfsek.terra.api.structure.ConfiguredStructure;
import java.util.ArrayList;
import java.util.List;
public class StructureCompleter implements TabCompleter {
@Inject
private TerraPlugin main;
@Override
public List<String> complete(CommandSender sender) {
Player player = (Player) sender;
return new ArrayList<>(main.getWorld(player.world()).getConfig().getRegistry(ConfiguredStructure.class).keys());
}
}

View File

@@ -0,0 +1,101 @@
package com.dfsek.terra.addons.structure.structures.loot;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.ItemStack;
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 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;
/**
* 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<LootFunction> functions = new ArrayList<>();
/**
* Instantiates an Entry from a JSON representation.
*
* @param entry The JSON Object to instantiate from.
*/
public Entry(JSONObject entry, TerraPlugin main) {
String id = entry.get("name").toString();
this.item = main.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":
case "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)));
break;
case "minecraft:set_damage":
case "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)));
break;
case "minecraft:enchant_with_levels":
case "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, main));
break;
}
}
}
}
/**
* 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;
}
}

View File

@@ -0,0 +1,70 @@
package com.dfsek.terra.addons.structure.structures.loot;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.inventory.Inventory;
import com.dfsek.terra.api.inventory.ItemStack;
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;
/**
* Class representation of a Loot Table to populate chest loot.
*/
public class LootTableImpl implements com.dfsek.terra.api.structure.LootTable {
private final List<Pool> 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, TerraPlugin main) 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, main));
}
}
@Override
public List<ItemStack> getLoot(Random r) {
List<ItemStack> itemList = new ArrayList<>();
for(Pool pool : pools) {
itemList.addAll(pool.getItems(r));
}
return itemList;
}
@Override
public void fillInventory(Inventory i, Random r) {
List<ItemStack> 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++;
}
}
}
}

View File

@@ -0,0 +1,59 @@
package com.dfsek.terra.addons.structure.structures.loot;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.util.collection.ProbabilityCollection;
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;
/**
* 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<Entry> entries;
/**
* Instantiates a Pool from a JSON representation.
*
* @param pool The JSON Object to instantiate from.
*/
public Pool(JSONObject pool, TerraPlugin main) {
entries = new ProbabilityCollectionImpl<>();
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, main);
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&lt;ItemStack&gt; - The list of items fetched.
*/
public List<ItemStack> getItems(Random r) {
int rolls = r.nextInt(max - min + 1) + min;
List<ItemStack> items = new ArrayList<>();
for(int i = 0; i < rolls; i++) {
items.add(entries.get(r).getItem(r));
}
return items;
}
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import com.dfsek.terra.api.inventory.ItemStack;
import java.util.Random;
/**
* 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;
}
}

View File

@@ -0,0 +1,45 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Damageable;
import com.dfsek.terra.api.inventory.item.ItemMeta;
import java.util.Random;
/**
* 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;
}
}

View File

@@ -0,0 +1,66 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import com.dfsek.terra.api.TerraPlugin;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Enchantment;
import com.dfsek.terra.api.inventory.item.ItemMeta;
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;
public class EnchantFunction implements LootFunction {
private final int min;
private final int max;
private final JSONArray disabled;
private final TerraPlugin main;
public EnchantFunction(int min, int max, JSONArray disabled, TerraPlugin main) {
this.max = max;
this.min = min;
this.disabled = disabled;
this.main = main;
}
/**
* 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<Enchantment> possible = new ArrayList<>();
for(Enchantment ench : main.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) {
main.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;
}
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.addons.structure.structures.loot.functions;
import com.dfsek.terra.api.inventory.ItemStack;
import java.util.Random;
/**
* 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);
}