diff --git a/src/main/java/dev/sefiraat/netheopoiesis/Netheopoiesis.java b/src/main/java/dev/sefiraat/netheopoiesis/Netheopoiesis.java index df04566..52d10ca 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/Netheopoiesis.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/Netheopoiesis.java @@ -1,10 +1,11 @@ package dev.sefiraat.netheopoiesis; +import dev.sefiraat.netheopoiesis.api.plant.netheos.NetheoBalls; import dev.sefiraat.netheopoiesis.implementation.Items; import dev.sefiraat.netheopoiesis.managers.ConfigManager; import dev.sefiraat.netheopoiesis.managers.ListenerManager; import dev.sefiraat.netheopoiesis.managers.MobManager; -import dev.sefiraat.netheopoiesis.managers.RunnableManager; +import dev.sefiraat.netheopoiesis.managers.TaskManager; import dev.sefiraat.netheopoiesis.managers.SupportedPluginManager; import io.github.thebusybiscuit.slimefun4.api.SlimefunAddon; import net.guizhanss.guizhanlib.updater.GuizhanBuildsUpdater; @@ -31,10 +32,10 @@ public class Netheopoiesis extends JavaPlugin implements SlimefunAddon { private ConfigManager configManager; private SupportedPluginManager supportedPluginManager; private ListenerManager listenerManager; - private RunnableManager runnableManager; + private TaskManager taskManager; private MobManager mobManager; private Purification purification; - private PlantRegistry plantRegistry; + private Registry registry; public Netheopoiesis() { this.username = "SlimefunGuguProject"; @@ -51,17 +52,18 @@ public void onEnable() { getLogger().info("########################################"); saveDefaultConfig(); + this.configManager = new ConfigManager(); tryUpdate(); - this.configManager = new ConfigManager(); this.supportedPluginManager = new SupportedPluginManager(); this.listenerManager = new ListenerManager(); - this.runnableManager = new RunnableManager(); + this.taskManager = new TaskManager(); this.mobManager = new MobManager(); this.purification = new Purification(); - this.plantRegistry = new PlantRegistry(); + this.registry = new Registry(); Items.setup(this); + NetheoBalls.setup(); setupStats(); } @@ -73,9 +75,7 @@ public void onDisable() { } public void tryUpdate() { - if (getConfig().getBoolean("auto-update") - && getDescription().getVersion().startsWith("Build") - ) { + if (configManager.isAutoUpdate() && getDescription().getVersion().startsWith("Build")) { new GuizhanBuildsUpdater(this, getFile(), username, repo, branch, false, "zh-CN").start(); } } @@ -129,8 +129,8 @@ public static ListenerManager getListenerManager() { return Netheopoiesis.getInstance().listenerManager; } - public static RunnableManager getRunnableManager() { - return Netheopoiesis.getInstance().runnableManager; + public static TaskManager getRunnableManager() { + return Netheopoiesis.getInstance().taskManager; } public static MobManager getMobManager() { @@ -141,7 +141,7 @@ public static Purification getPurificationMemory() { return Netheopoiesis.getInstance().purification; } - public static PlantRegistry getPlantRegistry() { - return Netheopoiesis.getInstance().plantRegistry; + public static Registry getPlantRegistry() { + return Netheopoiesis.getInstance().registry; } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/Purification.java b/src/main/java/dev/sefiraat/netheopoiesis/Purification.java index 4ba0173..2c16c8e 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/Purification.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/Purification.java @@ -39,6 +39,7 @@ public class Purification { public static final int SPAWN_TROPICAL_FISH = 1000; public static final int SPAWN_AXOLOTL = 1000; public static final int WANDERING_TRADER = 1500; + public static final int WANDERING_PIGLIN = 1500; // Regeneration public static final int REGEN_1 = 500; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/PlantRegistry.java b/src/main/java/dev/sefiraat/netheopoiesis/Registry.java similarity index 92% rename from src/main/java/dev/sefiraat/netheopoiesis/PlantRegistry.java rename to src/main/java/dev/sefiraat/netheopoiesis/Registry.java index 480207c..1cef7c3 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/PlantRegistry.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/Registry.java @@ -1,9 +1,6 @@ package dev.sefiraat.netheopoiesis; import com.google.common.base.Preconditions; -import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedResult; -import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedResultType; -import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedingPair; import dev.sefiraat.netheopoiesis.api.items.BiomeSpreadingSeed; import dev.sefiraat.netheopoiesis.api.items.CruxSpreadingSeed; import dev.sefiraat.netheopoiesis.api.items.DroppingSeed; @@ -11,8 +8,13 @@ import dev.sefiraat.netheopoiesis.api.items.GenericTickingSeed; import dev.sefiraat.netheopoiesis.api.items.HarvestableSeed; import dev.sefiraat.netheopoiesis.api.items.NetherSeed; +import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedResult; +import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedResultType; +import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedingPair; +import dev.sefiraat.netheopoiesis.api.plant.netheos.Trade; import dev.sefiraat.netheopoiesis.utils.TextUtils; import org.bukkit.ChatColor; +import org.bukkit.entity.Piglin; import org.bukkit.inventory.ItemStack; import javax.annotation.Nonnull; @@ -23,18 +25,25 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Set; +import java.util.UUID; -public class PlantRegistry { +public class Registry { - private static PlantRegistry instance; + private static Registry instance; + @Nonnull private final List registeredPlants = new ArrayList<>(); + @Nonnull private final List breedingPairs = new ArrayList<>(); + @Nonnull + private final List trades = new ArrayList<>(); - public PlantRegistry() { - Preconditions.checkArgument(instance == null, "Cannot create a new instance of the PlantRegistry"); + public Registry() { + Preconditions.checkArgument(instance == null, "Cannot create a new instance of the Registry"); instance = this; } @@ -43,6 +52,10 @@ public void addPlant(@Nonnull NetherSeed netherSeed) { this.breedingPairs.addAll(netherSeed.getBreedingPairs()); } + public void addTrade(@Nonnull Trade trade) { + this.trades.add(trade); + } + @Nonnull public BreedResult getBreedResult(@Nonnull String seed1, @Nonnull String seed2) { int matches = 0; @@ -69,6 +82,11 @@ public List getBreedingPairs() { return Collections.unmodifiableList(breedingPairs); } + @Nonnull + public List getTrades() { + return trades; + } + /** * Should never be used in ac production environment - this method will output all plant details * into .md files for GitBook @@ -164,7 +182,7 @@ public void printRegistry() { } } - public static PlantRegistry getInstance() { + public static Registry getInstance() { return instance; } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/RecipeTypes.java b/src/main/java/dev/sefiraat/netheopoiesis/api/RecipeTypes.java index 4c293ff..f10d44e 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/RecipeTypes.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/RecipeTypes.java @@ -1,9 +1,14 @@ package dev.sefiraat.netheopoiesis.api; +import com.google.common.base.Preconditions; +import dev.sefiraat.netheopoiesis.api.interfaces.WorldCrushable; +import dev.sefiraat.netheopoiesis.listeners.DropListener; import dev.sefiraat.netheopoiesis.utils.Keys; import dev.sefiraat.netheopoiesis.utils.Theme; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; import javax.annotation.Nonnull; @@ -70,4 +75,74 @@ private RecipeTypes() { "通过破坏方块获得." ) ); + + @Nonnull + public static final RecipeType CRUSHING = new RecipeType( + Keys.newKey("crushing"), + Theme.themedItemStack( + Material.ANVIL, + Theme.RECIPE_TYPE, + "Tough Crushing", + "This item is made by crushing it.", + "To crush items, from an Anvil on it from", + "a height." + ) + ); + + @Nonnull + public static final RecipeType NETHEO_MIXING = new RecipeType( + Keys.newKey("netheo-mixing"), + Theme.themedItemStack( + Material.QUARTZ, + Theme.RECIPE_TYPE, + "Netheo Mixing", + "This item is made by mixing three", + "Netheo pastes together.", + "Throw the three pastes onto the ground", + "and then right click with a Mixing Quartz." + ) + ); + + /** + * This method both registers the drop and returns an ItemStack array that can be used + * for Slimefun's recipe system. {@link RecipeTypes#VANILLA_DROP} + * + * @param stackToDrop The {@link ItemStack} to drop in the world + * @param dropFrom The {@link ItemStack} to drop from (#getType() is used) and the stack is used in the recipe. + * @param dropChance The chance (0-1) for the drop to occur + * @return A {@link ItemStack[]} used for Slimefun's Recipe registration with the dropFrom item in the middle. + */ + @Nonnull + public static ItemStack[] createWorldDropRecipe(@Nonnull ItemStack stackToDrop, + @Nonnull ItemStack dropFrom, + double dropChance + ) { + final Material material = dropFrom.getType(); + DropListener.getDropMap().put(material, new DropListener.BlockDrop(stackToDrop, material, dropChance)); + return new ItemStack[]{ + null, null, null, + null, dropFrom, null, + null, null, null + }; + } + + /** + * This method returns an ItemStack array that can be used for Slimefun's recipe system. + * + * @param dropFrom The {@link SlimefunItem} (must implement WorldCrushable) to drop from. + * @return A {@link ItemStack[]} used for Slimefun's Recipe registration with the dropFrom item in the middle. + * @see RecipeTypes#CRUSHING + */ + @Nonnull + public static ItemStack[] createCrushingRecipe(@Nonnull SlimefunItem dropFrom) { + Preconditions.checkArgument( + dropFrom instanceof WorldCrushable, + "A crushing recipe item must implement WorldCrushable" + ); + return new ItemStack[]{ + null, null, null, + null, dropFrom.getItem(), null, + null, null, null + }; + } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/NetherPlant.java b/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/NetherPlant.java index 628f34f..0e6b0ca 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/NetherPlant.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/NetherPlant.java @@ -1,8 +1,8 @@ package dev.sefiraat.netheopoiesis.api.interfaces; -import dev.sefiraat.netheopoiesis.implementation.plant.GrowthStages; import dev.sefiraat.netheopoiesis.api.items.NetherCrux; import dev.sefiraat.netheopoiesis.api.items.NetherSeed; +import dev.sefiraat.netheopoiesis.implementation.plant.GrowthStages; import dev.sefiraat.netheopoiesis.utils.Skulls; import dev.sefiraat.netheopoiesis.utils.Theme; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/SeedPaste.java b/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/SeedPaste.java new file mode 100644 index 0000000..a5067db --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/SeedPaste.java @@ -0,0 +1,15 @@ +package dev.sefiraat.netheopoiesis.api.interfaces; + +import dev.sefiraat.netheopoiesis.api.plant.netheos.FlavourProfile; + +import javax.annotation.Nullable; + +/** + * A SeedPaste item a {@link WorldCrushable} item whose crushed variant can be used for making + * Malvin Cubes + */ +public interface SeedPaste extends WorldCrushable { + + @Nullable + FlavourProfile getFlavourProfile(); +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/WorldCrushable.java b/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/WorldCrushable.java new file mode 100644 index 0000000..e681c06 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/interfaces/WorldCrushable.java @@ -0,0 +1,20 @@ +package dev.sefiraat.netheopoiesis.api.interfaces; + +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nullable; + +/** + * A WorldCrushable item is one that will be destroyed when crushed by a falling block + * but drop a 'crushed' version of itself. + */ +public interface WorldCrushable { + + /** + * This is the {@link ItemStack} that should drop when this item is crushed + * + * @return The {@link ItemStack} to drop. + */ + @Nullable + ItemStack crushingDrop(); +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherCrux.java b/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherCrux.java index d42272f..ab80aa6 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherCrux.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherCrux.java @@ -1,7 +1,7 @@ package dev.sefiraat.netheopoiesis.api.items; -import dev.sefiraat.netheopoiesis.api.interfaces.PurifyingObject; import dev.sefiraat.netheopoiesis.api.RecipeTypes; +import dev.sefiraat.netheopoiesis.api.interfaces.PurifyingObject; import dev.sefiraat.netheopoiesis.implementation.Stacks; import dev.sefiraat.netheopoiesis.implementation.plant.Placements; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; @@ -49,7 +49,8 @@ public void onPlayerBreak(BlockBreakEvent event, ItemStack item, List // We do not want crux' to be able to drop and placed elsewhere thus gaming the system final Block block = event.getBlock(); final ItemStack heldItem = event.getPlayer().getInventory().getItemInMainHand(); - if (!SlimefunItem.getByItem(heldItem).getId().equals(Stacks.CRUX_GATHERER.getItemId())) { + final SlimefunItem slimefunItem = SlimefunItem.getByItem(heldItem); + if (slimefunItem == null || !slimefunItem.getId().equals(Stacks.CRUX_GATHERER.getItemId())) { event.setCancelled(true); block.setType(Material.AIR); BlockStorage.clearBlockInfo(block); diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherSeed.java b/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherSeed.java index 23b235b..42d46e6 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherSeed.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/items/NetherSeed.java @@ -2,15 +2,18 @@ import com.google.common.base.Preconditions; import dev.sefiraat.netheopoiesis.Netheopoiesis; -import dev.sefiraat.netheopoiesis.PlantRegistry; +import dev.sefiraat.netheopoiesis.Registry; +import dev.sefiraat.netheopoiesis.api.RecipeTypes; +import dev.sefiraat.netheopoiesis.api.events.PlantBeforeGrowthEvent; +import dev.sefiraat.netheopoiesis.api.interfaces.NetherPlant; +import dev.sefiraat.netheopoiesis.api.interfaces.SeedPaste; import dev.sefiraat.netheopoiesis.api.plant.Growth; import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedResult; import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedResultType; import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedingPair; -import dev.sefiraat.netheopoiesis.api.interfaces.NetherPlant; -import dev.sefiraat.netheopoiesis.api.events.PlantBeforeGrowthEvent; -import dev.sefiraat.netheopoiesis.api.RecipeTypes; +import dev.sefiraat.netheopoiesis.api.plant.netheos.FlavourProfile; import dev.sefiraat.netheopoiesis.implementation.Groups; +import dev.sefiraat.netheopoiesis.implementation.netheos.Paste; import dev.sefiraat.netheopoiesis.implementation.plant.GrowthStages; import dev.sefiraat.netheopoiesis.implementation.plant.Placements; import dev.sefiraat.netheopoiesis.utils.Keys; @@ -36,6 +39,7 @@ import me.mrCookieSlime.Slimefun.api.inventory.BlockMenuPreset; import me.mrCookieSlime.Slimefun.api.item_transport.ItemTransportFlow; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.Color; import org.bukkit.Location; import org.bukkit.Material; @@ -63,7 +67,7 @@ /** * This class is used to define a Seed item that will grow as a {@link NetherPlant} */ -public abstract class NetherSeed extends SlimefunItem implements NetherPlant { +public abstract class NetherSeed extends SlimefunItem implements NetherPlant, SeedPaste { @Nonnull public static final Set BREEDING_DIRECTIONS = Set.of( @@ -82,6 +86,11 @@ public abstract class NetherSeed extends SlimefunItem implements NetherPlant { @Nonnull protected Set breedingPairs = new HashSet<>(); + @Nullable + protected ItemStack crushingDrop; + @Nullable + protected FlavourProfile flavourProfile; + @ParametersAreNonnullByDefault protected NetherSeed(SlimefunItemStack item) { this(item, RecipeTypes.PLANT_BREEDING, new ItemStack[0], null); @@ -188,7 +197,7 @@ private void tryBreed(@Nonnull Block motherBlock, @Nonnull NetherSeed mother) { final SlimefunItem mateItem = BlockStorage.check(potentialMate); if (mateItem instanceof NetherSeed mate) { - final BreedResult result = PlantRegistry.getInstance().getBreedResult(mother.getId(), mate.getId()); + final BreedResult result = Registry.getInstance().getBreedResult(mother.getId(), mate.getId()); if (result.getResultType() == BreedResultType.NO_PAIRS) { // No matching breeding pairs, lets feedback to the player then move to the next direction @@ -369,22 +378,97 @@ public Set getBreedingPairs() { } /** - * Tries to register the seed (if it passes validation) first into the PlantRegistry, then its + * Adds a flavour profile to this seed. When registered, a NetherSeed with a FlavourProfile + * will register its paste version + * + * @param sweet The sweetness of this seed + * @param sour The sourness of this seed + * @param salty The saltiness of this seed + * @param bitter The bitterness of this seed + * @param umami The unamai(ness?) of this seed + * @return Returns self + */ + @Nonnull + @ParametersAreNonnullByDefault + public NetherSeed addFlavourProfile(int sweet, int sour, int salty, int bitter, int umami) { + this.flavourProfile = new FlavourProfile(sweet, sour, salty, bitter, umami); + return this; + } + + /** + * Adds a flavour profile to this seed. When registered, a NetherSeed with a FlavourProfile + * will register it's powdered version as well its paste version + * + * @param flavourProfile The {@link FlavourProfile} for this item + * @return Returns self + */ + @Nonnull + @ParametersAreNonnullByDefault + public NetherSeed addFlavourProfile(@Nonnull FlavourProfile flavourProfile) { + this.flavourProfile = flavourProfile; + return this; + } + + @Override + @Nullable + public FlavourProfile getFlavourProfile() { + return this.flavourProfile; + } + + /** + * Tries to register the seed (if it passes validation) first into the Registry, then its * breeding pairs and finally with Slimefun. * * @param addon The addon registering this Seed */ public void tryRegister(@Nonnull SlimefunAddon addon) { if (validateSeed()) { + if (this.flavourProfile != null) { + registerPaste(); + } if (this.description == null) { Netheopoiesis.logWarning(this.getId() + " has no Growth, it will not be registered."); } else { - PlantRegistry.getInstance().addPlant(this); + Registry.getInstance().addPlant(this); register(addon); } } } + private void registerPaste() { + final SlimefunItemStack stack = Theme.themedSlimefunItemStack( + "NPS_PASTE_" + this.getId().replace("NPS_", ""), + Material.RABBIT_STEW, + Theme.PASTE, + "Netheo Paste: " + ChatColor.stripColor(this.getItemName()), + "This paste is highly nutritious and", + "loved by Piglins. Can be formed into", + "tasty Netheo Balls.", + "", + Theme.CLICK_INFO.asTitle("Sweet", this.flavourProfile.getSweet()), + Theme.CLICK_INFO.asTitle("Sour", this.flavourProfile.getSour()), + Theme.CLICK_INFO.asTitle("Salty", this.flavourProfile.getSalty()), + Theme.CLICK_INFO.asTitle("Bitter", this.flavourProfile.getBitter()), + Theme.CLICK_INFO.asTitle("Umami", this.flavourProfile.getUmami()) + ); + this.crushingDrop = stack; + + final Paste paste = new Paste( + Groups.PASTES, + stack, + RecipeTypes.CRUSHING, + RecipeTypes.createCrushingRecipe(this), + this.flavourProfile + ); + paste.register(Netheopoiesis.getInstance()); + } + + @Nullable + @Override + public ItemStack crushingDrop() { + return this.crushingDrop; + } + @Nonnull @Override public GrowthStages getGrowthStages() { diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCap.java b/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCap.java index e6e9511..29ded81 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCap.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCap.java @@ -1,5 +1,6 @@ package dev.sefiraat.netheopoiesis.api.mobs; +import dev.sefiraat.netheopoiesis.Netheopoiesis; import org.bukkit.Bukkit; import org.bukkit.entity.Entity; import org.jetbrains.annotations.Unmodifiable; @@ -12,15 +13,55 @@ public class MobCap { - public static final MobCap WATER_AMBIENT = new MobCap(MobCapType.WATER_AMBIENT, 15); - public static final MobCap WATER_ANIMAL = new MobCap(MobCapType.WATER_ANIMAL, 5); - public static final MobCap WATER_HOSTILE = new MobCap(MobCapType.WATER_HOSTILE, 10); - public static final MobCap LAND_AMBIENT = new MobCap(MobCapType.WATER_AMBIENT, 5); - public static final MobCap LAND_ANIMAL = new MobCap(MobCapType.LAND_ANIMAL, 10); - public static final MobCap LAND_HOSTILE = new MobCap(MobCapType.LAND_HOSTILE, 10); - public static final MobCap VILLAGER = new MobCap(MobCapType.VILLAGER, 5); - public static final MobCap PIGLIN_TRADER = new MobCap(MobCapType.PIGLIN_TRADER, 1); - public static final MobCap WANDERING_TRADER = new MobCap(MobCapType.WANDERING_TRADER, 1); + public static final MobCap WATER_AMBIENT = new MobCap( + MobCapType.WATER_AMBIENT, + Netheopoiesis.getConfigManager().getPlayerMobCapWaterAmbient() + ); + + public static final MobCap WATER_ANIMAL = new MobCap( + MobCapType.WATER_ANIMAL, + Netheopoiesis.getConfigManager().getPlayerMobCapWaterAnimal() + ); + + public static final MobCap WATER_HOSTILE = new MobCap( + MobCapType.WATER_HOSTILE, + Netheopoiesis.getConfigManager().getPlayerMobCapWaterHostile() + ); + + public static final MobCap LAND_AMBIENT = new MobCap( + MobCapType.WATER_AMBIENT, + Netheopoiesis.getConfigManager().getPlayerMobCapLandAmbient() + ); + + public static final MobCap LAND_ANIMAL = new MobCap( + MobCapType.LAND_ANIMAL, + Netheopoiesis.getConfigManager().getPlayerMobCapLandAnimal() + ); + + public static final MobCap LAND_HOSTILE = new MobCap( + MobCapType.LAND_HOSTILE, + Netheopoiesis.getConfigManager().getPlayerMobCapLandHostile() + ); + + public static final MobCap VILLAGER = new MobCap( + MobCapType.VILLAGER, + Netheopoiesis.getConfigManager().getPlayerMobCapVillager() + ); + + public static final MobCap PIGLIN_TRADER = new MobCap( + MobCapType.PIGLIN_TRADER, + Netheopoiesis.getConfigManager().getPlayerMobCapPiglinTrader() + ); + + public static final MobCap WANDERING_TRADER = new MobCap( + MobCapType.WANDERING_TRADER, + Netheopoiesis.getConfigManager().getPlayerMobCapWanderingTrader() + ); + + public static final MobCap MISC = new MobCap( + MobCapType.MISC, + 0 + ); private final int amountPerPlayer; @Nonnull @@ -35,6 +76,7 @@ public MobCap(@Nonnull MobCapType type, int maxPerPlayer) { /** * Gets the number of mobs currently held in this cap + * * @return The number of mobs currently held in this cap */ public int count() { @@ -43,6 +85,7 @@ public int count() { /** * Checks if there is space in this cap for another mob + * * @return true if there is space */ public boolean hasSpace() { @@ -51,6 +94,7 @@ public boolean hasSpace() { /** * Checks if a given {@link UUID} is contained in this cap + * * @param mobUuid The {@link UUID} for the mob being checked * @return true if contained in the cap */ @@ -61,6 +105,7 @@ public boolean contains(@Nonnull UUID mobUuid) { /** * Adds a new mob to this cap. Does not check for space and can be forced if required. * Check hasSpace() first! + * * @param mobUuid The {@link UUID} of the mob to add */ public void addMob(@Nonnull UUID mobUuid) { @@ -69,6 +114,7 @@ public void addMob(@Nonnull UUID mobUuid) { /** * Removes a mob from this cap if possible + * * @param mobUuid The {@link UUID} of the mob to remove */ public void removeMob(@Nonnull UUID mobUuid) { @@ -77,6 +123,7 @@ public void removeMob(@Nonnull UUID mobUuid) { /** * Kills a mob whilst also removing them from the cap + * * @param mobUuid The {@link UUID} of the mob to kill/remove */ public void killMob(@Nonnull UUID mobUuid) { @@ -102,6 +149,7 @@ public void killAllMobs() { /** * Gets the maximum number of spawns, per player, for this cap. + * * @return The max number of mobs */ public int getMaxAmountPerPlayer() { @@ -110,6 +158,7 @@ public int getMaxAmountPerPlayer() { /** * Gets the maximum number of spawns for this cap (includes the multiplier per-player) + * * @return The max number of mobs */ public int getMaxAmount() { @@ -118,6 +167,7 @@ public int getMaxAmount() { /** * The {@link MobCapType} for this cap + * * @return The {@link MobCapType} for this cap */ @Nonnull @@ -127,6 +177,7 @@ public MobCapType getType() { /** * Gets an immutable list of all mobs contained within this cap + * * @return An immutable list of all mobs contained within this cap */ @Nonnull diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCapType.java b/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCapType.java index dcc074e..709447f 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCapType.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/MobCapType.java @@ -9,5 +9,6 @@ public enum MobCapType { LAND_HOSTILE, VILLAGER, PIGLIN_TRADER, - WANDERING_TRADER + WANDERING_TRADER, + MISC } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/RandomSpawn.java b/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/RandomSpawn.java index 75cb4ac..9f50071 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/RandomSpawn.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/mobs/RandomSpawn.java @@ -9,7 +9,9 @@ import org.bukkit.entity.LivingEntity; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -19,12 +21,17 @@ */ public class RandomSpawn { + @Nonnull private final EntityType type; + @Nonnull private final MobCapType mobCapType; private final int requiredPurification; private final double chance; + @Nonnull private final Predicate predicate; private final boolean randomize; + @Nullable + private final Consumer callback; /** * Creates a new RandomSpawn @@ -88,6 +95,28 @@ public RandomSpawn(@Nonnull EntityType type, double chance, boolean randomize, Predicate predicate + ) { + this(type, mobCapType, requiredValue, chance, randomize, predicate, null); + } + + /** + * Creates a new RandomSpawn + * + * @param type The {@link EntityType} that will spawn + * @param requiredValue The required Purification level in the chunk for the spawn to be attempted + * @param chance The chance for the mob to spawn successfully + * @param randomize Defines if the mob's data should be randomized (default = true) + * @param predicate This predicate is used to determine if the mob can spawn. Use for additional spawn + * requirements, e.g. Location being in water + * @param callback The {@link Consumer} that will be accepted after the mob has been spawned + */ + public RandomSpawn(@Nonnull EntityType type, + @Nonnull MobCapType mobCapType, + int requiredValue, + double chance, + boolean randomize, + Predicate predicate, + @Nullable Consumer callback ) { Preconditions.checkArgument(type.isAlive(), "Only LivingEntities can be RandomSpawns"); Preconditions.checkArgument(type.isSpawnable(), "Specified type is not spawnable"); @@ -97,6 +126,7 @@ public RandomSpawn(@Nonnull EntityType type, this.chance = chance; this.randomize = randomize; this.predicate = predicate; + this.callback = callback; } /** @@ -126,6 +156,25 @@ public double getChance() { return chance; } + @Nonnull + public MobCapType getMobCapType() { + return mobCapType; + } + + @Nonnull + public Predicate getPredicate() { + return predicate; + } + + public boolean isRandomize() { + return randomize; + } + + @Nullable + public Consumer getCallback() { + return callback; + } + /** * Attempts to spawn the {@link EntityType} defined. * @@ -158,6 +207,9 @@ public boolean trySpawn(@Nonnull Location location, int purificationLevel) { ); if (livingEntity != null) { + if (callback != null) { + callback.accept(livingEntity); + } livingEntity.setRemoveWhenFarAway(true); livingEntity.setNoDamageTicks(20); return true; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/Flavour.java b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/Flavour.java new file mode 100644 index 0000000..32caa86 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/Flavour.java @@ -0,0 +1,9 @@ +package dev.sefiraat.netheopoiesis.api.plant.netheos; + +public enum Flavour { + SWEET, + SOUR, + SALTY, + BITTER, + UMAMI +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/FlavourProfile.java b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/FlavourProfile.java new file mode 100644 index 0000000..89c894c --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/FlavourProfile.java @@ -0,0 +1,47 @@ +package dev.sefiraat.netheopoiesis.api.plant.netheos; + +import org.jetbrains.annotations.Unmodifiable; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; + +public class FlavourProfile { + + private final Map flavourMap = new EnumMap<>(Flavour.class); + + public FlavourProfile(int sweet, int sour, int salty, int bitter, int umami) { + flavourMap.put(Flavour.SWEET, sweet); + flavourMap.put(Flavour.SOUR, sour); + flavourMap.put(Flavour.SALTY, salty); + flavourMap.put(Flavour.BITTER, bitter); + flavourMap.put(Flavour.UMAMI, umami); + } + + public int getSweet() { + return flavourMap.getOrDefault(Flavour.SWEET, 0); + } + + public int getSour() { + return flavourMap.getOrDefault(Flavour.SOUR, 0); + } + + public int getSalty() { + return flavourMap.getOrDefault(Flavour.SALTY, 0); + } + + public int getBitter() { + return flavourMap.getOrDefault(Flavour.BITTER, 0); + } + + public int getUmami() { + return flavourMap.getOrDefault(Flavour.UMAMI, 0); + } + + @Nonnull + @Unmodifiable + public Map getFlavourMap() { + return Collections.unmodifiableMap(flavourMap); + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/NetheoBalls.java b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/NetheoBalls.java new file mode 100644 index 0000000..d04cc12 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/NetheoBalls.java @@ -0,0 +1,400 @@ +package dev.sefiraat.netheopoiesis.api.plant.netheos; + +import dev.sefiraat.netheopoiesis.Netheopoiesis; +import dev.sefiraat.netheopoiesis.api.RecipeTypes; +import dev.sefiraat.netheopoiesis.implementation.Groups; +import dev.sefiraat.netheopoiesis.implementation.netheos.NetheoBall; +import dev.sefiraat.netheopoiesis.utils.TextUtils; +import dev.sefiraat.netheopoiesis.utils.Theme; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public class NetheoBalls { + + public static final NetheoBalls SWEET = new NetheoBalls( + "SWEET", + Flavour.SWEET, + Flavour.SWEET, + Flavour.SWEET, + TradePool.SWEET + ); + public static final NetheoBalls TANGY = new NetheoBalls( + "TANGY", + Flavour.SWEET, + Flavour.SWEET, + Flavour.SOUR, + TradePool.TANGY + ); + public static final NetheoBalls POPCORN = new NetheoBalls( + "POPCORN", + Flavour.SWEET, + Flavour.SWEET, + Flavour.SALTY, + TradePool.POPCORN + ); + public static final NetheoBalls BITTERSWEET = new NetheoBalls( + "BITTERSWEET", + Flavour.SWEET, + Flavour.SWEET, + Flavour.BITTER, + TradePool.BITTERSWEET + ); + public static final NetheoBalls SWEETPEA = new NetheoBalls( + "SWEETPEA", + Flavour.SWEET, + Flavour.SWEET, + Flavour.UMAMI, + TradePool.SWEETPEA + ); + public static final NetheoBalls CANDY = new NetheoBalls( + "CANDY", + Flavour.SWEET, + Flavour.SOUR, + Flavour.SOUR, + TradePool.CANDY + ); + public static final NetheoBalls COMPLEX = new NetheoBalls( + "COMPLEX", + Flavour.SWEET, + Flavour.SOUR, + Flavour.SALTY, + TradePool.COMPLEX + ); + public static final NetheoBalls CITRUS = new NetheoBalls( + "CITRUS", + Flavour.SWEET, + Flavour.SOUR, + Flavour.BITTER, + TradePool.CITRUS + ); + public static final NetheoBalls BAD = new NetheoBalls( + "BAD", + Flavour.SWEET, + Flavour.SOUR, + Flavour.UMAMI, + TradePool.BAD + ); + public static final NetheoBalls SALTED_CARAMEL = new NetheoBalls( + "SALTED_CARAMEL", + Flavour.SWEET, + Flavour.SALTY, + Flavour.SALTY, + TradePool.SALTED_CARAMEL + ); + public static final NetheoBalls COCOA = new NetheoBalls( + "COCOA", + Flavour.SWEET, + Flavour.SALTY, + Flavour.BITTER, + TradePool.COCOA + ); + public static final NetheoBalls TOMATO = new NetheoBalls( + "TOMATO", + Flavour.SWEET, + Flavour.SALTY, + Flavour.UMAMI, + TradePool.TOMATO + ); + public static final NetheoBalls BERRY = new NetheoBalls( + "BERRY", + Flavour.SWEET, + Flavour.BITTER, + Flavour.BITTER, + TradePool.BERRY + ); + public static final NetheoBalls TERIYAKI = new NetheoBalls( + "TERIYAKI", + Flavour.SWEET, + Flavour.BITTER, + Flavour.UMAMI, + TradePool.TERIYAKI + ); + public static final NetheoBalls SOY = new NetheoBalls( + "SOY", + Flavour.SWEET, + Flavour.UMAMI, + Flavour.UMAMI, + TradePool.SOY + ); + public static final NetheoBalls TART = new NetheoBalls( + "TART", + Flavour.SOUR, + Flavour.SOUR, + Flavour.SOUR, + TradePool.TART + ); + public static final NetheoBalls GROSS = new NetheoBalls( + "GROSS", + Flavour.SOUR, + Flavour.SOUR, + Flavour.SALTY, + TradePool.GROSS + ); + public static final NetheoBalls SPOILT = new NetheoBalls( + "SPOILT", + Flavour.SOUR, + Flavour.SOUR, + Flavour.BITTER, + TradePool.SPOILT + ); + public static final NetheoBalls BONITO = new NetheoBalls( + "BONITO", + Flavour.SOUR, + Flavour.SOUR, + Flavour.UMAMI, + TradePool.BONITO + ); + public static final NetheoBalls SEAWEED = new NetheoBalls( + "SEAWEED", + Flavour.SOUR, + Flavour.SALTY, + Flavour.SALTY, + TradePool.SEAWEED + ); + public static final NetheoBalls ASTRINGENT = new NetheoBalls( + "ASTRINGENT", + Flavour.SOUR, + Flavour.SALTY, + Flavour.BITTER, + TradePool.ASTRINGENT + ); + public static final NetheoBalls TASTY = new NetheoBalls( + "TASTY", + Flavour.SOUR, + Flavour.SALTY, + Flavour.UMAMI, + TradePool.TASTY + ); + public static final NetheoBalls GREEN_TEA = new NetheoBalls( + "GREEN_TEA", + Flavour.SOUR, + Flavour.BITTER, + Flavour.BITTER, + TradePool.GREEN_TEA + ); + public static final NetheoBalls BROTHY = new NetheoBalls( + "BROTHY", + Flavour.SOUR, + Flavour.BITTER, + Flavour.UMAMI, + TradePool.BROTHY + ); + public static final NetheoBalls VEGEMITE = new NetheoBalls( + "VEGEMITE", + Flavour.SOUR, + Flavour.UMAMI, + Flavour.UMAMI, + TradePool.VEGEMITE + ); + public static final NetheoBalls SALTY = new NetheoBalls( + "SALTY", + Flavour.SALTY, + Flavour.SALTY, + Flavour.SALTY, + TradePool.SALTY + ); + public static final NetheoBalls NASTY = new NetheoBalls( + "NASTY", + Flavour.SALTY, + Flavour.SALTY, + Flavour.BITTER, + TradePool.NASTY + ); + public static final NetheoBalls ANCHOVY = new NetheoBalls( + "ANCHOVY", + Flavour.SALTY, + Flavour.SALTY, + Flavour.UMAMI, + TradePool.ANCHOVY + ); + public static final NetheoBalls GRIM = new NetheoBalls( + "GRIM", + Flavour.SALTY, + Flavour.BITTER, + Flavour.BITTER, + TradePool.GRIM + ); + public static final NetheoBalls SUSHI = new NetheoBalls( + "SUSHI", + Flavour.SALTY, + Flavour.BITTER, + Flavour.UMAMI, + TradePool.SUSHI + ); + public static final NetheoBalls FISHY = new NetheoBalls( + "FISHY", + Flavour.SALTY, + Flavour.UMAMI, + Flavour.UMAMI, + TradePool.FISHY + ); + public static final NetheoBalls CRUCIFEROUS = new NetheoBalls( + "CRUCIFEROUS", + Flavour.BITTER, + Flavour.BITTER, + Flavour.BITTER, + TradePool.CRUCIFEROUS + ); + public static final NetheoBalls HEALTHY = new NetheoBalls( + "HEALTHY", + Flavour.BITTER, + Flavour.BITTER, + Flavour.UMAMI, + TradePool.HEALTHY + ); + public static final NetheoBalls FUNGI = new NetheoBalls( + "FUNGI", + Flavour.BITTER, + Flavour.UMAMI, + Flavour.UMAMI, + TradePool.FUNGI + ); + public static final NetheoBalls SAVOURY = new NetheoBalls( + "SAVOURY", + Flavour.UMAMI, + Flavour.UMAMI, + Flavour.UMAMI, + TradePool.SAVOURY + ); + + private static final Set CACHED_VALUES = Set.of( + SWEET, + TANGY, + POPCORN, + BITTERSWEET, + SWEETPEA, + CANDY, + COMPLEX, + CITRUS, + BAD, + SALTED_CARAMEL, + COCOA, + TOMATO, + BERRY, + TERIYAKI, + SOY, + TART, + GROSS, + SPOILT, + BONITO, + SEAWEED, + ASTRINGENT, + TASTY, + GREEN_TEA, + BROTHY, + VEGEMITE, + SALTY, + NASTY, + ANCHOVY, + GRIM, + SUSHI, + FISHY, + CRUCIFEROUS, + HEALTHY, + FUNGI, + SAVOURY + ); + + @Nonnull + private final String name; + @Nonnull + private final Flavour flavour1; + @Nonnull + private final Flavour flavour2; + @Nonnull + private final Flavour flavour3; + @Nonnull + private final List flavourList; + @Nonnull + private final SlimefunItemStack slimefunItemStack; + + @ParametersAreNonnullByDefault + NetheoBalls(String name, Flavour flavour1, Flavour flavour2, Flavour flavour3, TradePool tradePool) { + this.name = name; + this.flavour1 = flavour1; + this.flavour2 = flavour2; + this.flavour3 = flavour3; + + flavourList = List.of(flavour1, flavour2, flavour3); + + this.slimefunItemStack = Theme.themedSlimefunItemStack( + "NPS_NETHEO_BALL_" + this.name, + Material.SNOWBALL, + Theme.NETHEO_BALL, + TextUtils.toTitleCase(this.name) + " Netheo Ball", + "A nutritious ball made up from", + "ground up plants from the Nether.", + "Each has a different effect when eaten.", + "Wandering Piglin Traders love these!", + "", + Theme.CLICK_INFO.asTitle("Flavour", 0) + ); + final NetheoBall ball = new NetheoBall( + Groups.BALLS, + slimefunItemStack, + RecipeTypes.NETHEO_MIXING, + new ItemStack[0], + this, + tradePool + ); + tradePool.setTradeItem(ball); + ball.register(Netheopoiesis.getInstance()); + } + + @ParametersAreNonnullByDefault + public boolean test(@Nonnull List flavours) { + final List thisList = new ArrayList<>(flavourList); + thisList.sort(Comparator.comparing(Enum::name)); + flavours.sort(Comparator.comparing(Enum::name)); + return thisList.equals(flavours); + } + + @Nonnull + public String getName() { + return name; + } + + @Nonnull + public Flavour getFlavour1() { + return flavour1; + } + + @Nonnull + public Flavour getFlavour2() { + return flavour2; + } + + @Nonnull + public Flavour getFlavour3() { + return flavour3; + } + + @Nonnull + public SlimefunItemStack getSlimefunItemStack() { + return slimefunItemStack; + } + + @ParametersAreNonnullByDefault + @Nullable + public static NetheoBalls getMatchingBall(List flavours) { + for (NetheoBalls ball : CACHED_VALUES) { + if (ball.test(flavours)) { + return ball; + } + } + return null; + } + + public static void setup() { + // todo This class needs to be instantiated, need the right way to do this + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/Trade.java b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/Trade.java new file mode 100644 index 0000000..ad6871a --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/Trade.java @@ -0,0 +1,57 @@ +package dev.sefiraat.netheopoiesis.api.plant.netheos; + +import io.github.bakedlibs.dough.collections.Pair; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class Trade { + + @Nonnull + private final Pair tradePair; + @Nonnull + private final String tradeId; + @Nullable + private TradePool tradePool; + + public Trade(@Nonnull ItemStack item, @Nonnull Integer requiredFlavour) { + this.tradePair = new Pair<>(item, requiredFlavour); + final SlimefunItem slimefunItem = SlimefunItem.getByItem(item); + this.tradeId = slimefunItem == null ? item.getType().name() : slimefunItem.getId(); + } + + @Nonnull + public ItemStack getItem() { + return tradePair.getFirstValue(); + } + + public int getRequiredFlavour() { + return tradePair.getSecondValue(); + } + + public void setTradePool(@Nonnull TradePool tradePool) { + this.tradePool = tradePool; + } + + @Nonnull + public String getTradeId() { + return tradeId; + } + + @Nullable + public TradePool getTradePool() { + return tradePool; + } + + @Nonnull + public Pair getTradePair() { + return tradePair; + } + + @Nonnull + public static Trade of(@Nonnull ItemStack item, @Nonnull Integer requiredFlavour) { + return new Trade(item, requiredFlavour); + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/TradePool.java b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/TradePool.java new file mode 100644 index 0000000..20e52cc --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/api/plant/netheos/TradePool.java @@ -0,0 +1,351 @@ +package dev.sefiraat.netheopoiesis.api.plant.netheos; + +import dev.sefiraat.netheopoiesis.Registry; +import dev.sefiraat.netheopoiesis.implementation.netheos.NetheoBall; +import io.github.thebusybiscuit.slimefun4.implementation.SlimefunItems; +import io.github.thebusybiscuit.slimefun4.libraries.dough.collections.RandomizedSet; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Set; +import java.util.stream.Collectors; + +public class TradePool { + + public static final TradePool SWEET = new TradePool() + .addTrade(new ItemStack(Material.SWEET_BERRIES, 10), 0) + .addTrade(new ItemStack(Material.COOKIE, 5), 0) + .addTrade(new ItemStack(Material.SUGAR_CANE, 10), 0) + .addTrade(new ItemStack(Material.SUGAR, 5), 0) + .addTrade(new ItemStack(Material.DIAMOND_ORE), 20) + .addTrade(new ItemStack(Material.EXPERIENCE_BOTTLE, 10), 40); + + public static final TradePool TANGY = new TradePool() + .addTrade(new ItemStack(Material.BEETROOT, 10), 0) + .addTrade(new ItemStack(Material.MELON, 5), 0) + .addTrade(new ItemStack(Material.QUARTZ, 10), 0) + .addTrade(new ItemStack(Material.GRASS_BLOCK, 5), 0) + .addTrade(new ItemStack(Material.EMERALD_ORE), 20) + .addTrade(new ItemStack(Material.MYCELIUM, 10), 40); + + public static final TradePool POPCORN = new TradePool() + .addTrade(new ItemStack(Material.WHEAT_SEEDS, 10), 0) + .addTrade(new ItemStack(Material.IRON_AXE), 0) + .addTrade(new ItemStack(Material.IRON_SWORD), 0) + .addTrade(new ItemStack(Material.FURNACE), 0) + .addTrade(new ItemStack(Material.TINTED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.TINTED_GLASS, 64), 40); + + public static final TradePool BITTERSWEET = new TradePool() + .addTrade(new ItemStack(Material.GREEN_WOOL, 10), 0) + .addTrade(new ItemStack(Material.IRON_PICKAXE), 0) + .addTrade(new ItemStack(Material.IRON_SHOVEL), 0) + .addTrade(new ItemStack(Material.GLOW_LICHEN, 5), 0) + .addTrade(new ItemStack(Material.GREEN_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.GREEN_STAINED_GLASS, 64), 40); + + public static final TradePool SWEETPEA = new TradePool() + .addTrade(new ItemStack(Material.LIME_WOOL, 10), 0) + .addTrade(new ItemStack(Material.IRON_HOE), 0) + .addTrade(new ItemStack(Material.IRON_HELMET), 0) + .addTrade(new ItemStack(Material.GLOW_INK_SAC, 5), 0) + .addTrade(new ItemStack(Material.LIME_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.LIME_STAINED_GLASS, 64), 40); + + public static final TradePool CANDY = new TradePool() + .addTrade(new ItemStack(Material.RED_WOOL, 10), 0) + .addTrade(new ItemStack(Material.IRON_LEGGINGS), 0) + .addTrade(new ItemStack(Material.IRON_CHESTPLATE), 0) + .addTrade(new ItemStack(Material.GLOW_INK_SAC, 5), 0) + .addTrade(new ItemStack(Material.RED_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.RED_STAINED_GLASS, 64), 40); + + public static final TradePool COMPLEX = new TradePool() + .addTrade(new ItemStack(Material.BLUE_WOOL, 10), 0) + .addTrade(new ItemStack(Material.IRON_HORSE_ARMOR), 0) + .addTrade(new ItemStack(Material.IRON_BOOTS), 0) + .addTrade(new ItemStack(Material.CLAY_BALL, 5), 0) + .addTrade(new ItemStack(Material.BLUE_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.BLUE_STAINED_GLASS, 64), 40); + + public static final TradePool CITRUS = new TradePool() + .addTrade(new ItemStack(Material.YELLOW_WOOL, 10), 0) + .addTrade(SlimefunItems.MAGIC_LUMP_1, 0) + .addTrade(SlimefunItems.ENDER_LUMP_1, 0) + .addTrade(new ItemStack(Material.COPPER_ORE, 5), 0) + .addTrade(new ItemStack(Material.YELLOW_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.YELLOW_STAINED_GLASS, 64), 40); + + public static final TradePool BAD = new TradePool() + .addTrade(new ItemStack(Material.DIRT, 10), 0) + .addTrade(SlimefunItems.FERTILIZER, 0) + .addTrade(new ItemStack(Material.COARSE_DIRT, 5), 0) + .addTrade(new ItemStack(Material.COAL_ORE, 5), 0) + .addTrade(new ItemStack(Material.BOOKSHELF, 5), 20) + .addTrade(new ItemStack(Material.TOTEM_OF_UNDYING, 1), 40); + + public static final TradePool SALTED_CARAMEL = new TradePool() + .addTrade(new ItemStack(Material.STONE_BRICKS, 10), 0) + .addTrade(SlimefunItems.GOLD_4K, 0) + .addTrade(new ItemStack(Material.PODZOL, 5), 0) + .addTrade(new ItemStack(Material.BUBBLE_CORAL, 5), 0) + .addTrade(new ItemStack(Material.BUBBLE_CORAL_BLOCK, 20), 20) + .addTrade(new ItemStack(Material.BUBBLE_CORAL_BLOCK, 64), 40); + + public static final TradePool COCOA = new TradePool() + .addTrade(new ItemStack(Material.COCOA_BEANS, 10), 0) + .addTrade(SlimefunItems.COPPER_INGOT, 0) + .addTrade(new ItemStack(Material.AMETHYST_SHARD, 5), 0) + .addTrade(new ItemStack(Material.BRAIN_CORAL, 5), 0) + .addTrade(new ItemStack(Material.BRAIN_CORAL_FAN, 5), 0) + .addTrade(new ItemStack(Material.BRAIN_CORAL_BLOCK, 20), 20) + .addTrade(new ItemStack(Material.BRAIN_CORAL_BLOCK, 64), 40); + + public static final TradePool TOMATO = new TradePool() + .addTrade(new ItemStack(Material.CARROT, 10), 0) + .addTrade(SlimefunItems.BILLON_INGOT, 0) + .addTrade(new ItemStack(Material.AMETHYST_SHARD, 5), 0) + .addTrade(new ItemStack(Material.FIRE_CORAL, 5), 0) + .addTrade(new ItemStack(Material.FIRE_CORAL_BLOCK, 20), 20) + .addTrade(new ItemStack(Material.FIRE_CORAL_BLOCK, 64), 40); + + public static final TradePool BERRY = new TradePool() + .addTrade(new ItemStack(Material.WHEAT, 10), 0) + .addTrade(SlimefunItems.ALUMINUM_BRASS_INGOT, 0) + .addTrade(new ItemStack(Material.KELP, 5), 0) + .addTrade(new ItemStack(Material.TUBE_CORAL, 5), 0) + .addTrade(new ItemStack(Material.TUBE_CORAL_FAN, 5), 0) + .addTrade(new ItemStack(Material.TUBE_CORAL_BLOCK, 20), 20) + .addTrade(new ItemStack(Material.TUBE_CORAL_BLOCK, 64), 40); + + public static final TradePool TERIYAKI = new TradePool() + .addTrade(new ItemStack(Material.PURPLE_WOOL, 10), 0) + .addTrade(SlimefunItems.BRASS_INGOT, 0) + .addTrade(new ItemStack(Material.REDSTONE_LAMP, 5), 0) + .addTrade(new ItemStack(Material.CACTUS, 5), 0) + .addTrade(new ItemStack(Material.SPECTRAL_ARROW, 15), 0) + .addTrade(new ItemStack(Material.PURPLE_WOOL, 20), 20) + .addTrade(new ItemStack(Material.PURPLE_WOOL, 64), 40); + + public static final TradePool SOY = new TradePool() + .addTrade(new ItemStack(Material.BLACK_WOOL, 10), 0) + .addTrade(SlimefunItems.COBALT_INGOT, 0) + .addTrade(new ItemStack(Material.REPEATER, 5), 0) + .addTrade(new ItemStack(Material.COMPARATOR, 5), 0) + .addTrade(new ItemStack(Material.SNOWBALL, 15), 0) + .addTrade(new ItemStack(Material.BLACK_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.BLACK_STAINED_GLASS, 64), 40); + + public static final TradePool TART = new TradePool() + .addTrade(new ItemStack(Material.CYAN_WOOL, 10), 0) + .addTrade(SlimefunItems.STEEL_INGOT, 0) + .addTrade(new ItemStack(Material.HONEYCOMB, 5), 0) + .addTrade(new ItemStack(Material.SHULKER_SHELL, 2), 0) + .addTrade(new ItemStack(Material.SNOWBALL, 15), 0) + .addTrade(new ItemStack(Material.CYAN_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.CYAN_STAINED_GLASS, 64), 40); + + public static final TradePool GROSS = new TradePool() + .addTrade(new ItemStack(Material.BROWN_WOOL, 10), 0) + .addTrade(SlimefunItems.TINY_URANIUM, 0) + .addTrade(new ItemStack(Material.DEEPSLATE_BRICKS, 5), 0) + .addTrade(new ItemStack(Material.SEA_PICKLE, 5), 0) + .addTrade(new ItemStack(Material.SPONGE, 2), 0) + .addTrade(new ItemStack(Material.BROWN_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.BROWN_STAINED_GLASS, 64), 40); + + public static final TradePool SPOILT = new TradePool() + .addTrade(new ItemStack(Material.GREEN_WOOL, 10), 0) + .addTrade(SlimefunItems.DURALUMIN_INGOT, 0) + .addTrade(new ItemStack(Material.REDSTONE_ORE, 5), 0) + .addTrade(new ItemStack(Material.MILK_BUCKET), 0) + .addTrade(new ItemStack(Material.GOLD_BLOCK, 1), 0) + .addTrade(new ItemStack(Material.GREEN_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.GREEN_STAINED_GLASS, 64), 40); + + public static final TradePool BONITO = new TradePool() + .addTrade(new ItemStack(Material.MAGENTA_WOOL, 10), 0) + .addTrade(SlimefunItems.DURALUMIN_INGOT, 0) + .addTrade(new ItemStack(Material.TROPICAL_FISH_BUCKET), 0) + .addTrade(new ItemStack(Material.SALMON_BUCKET), 0) + .addTrade(new ItemStack(Material.AXOLOTL_BUCKET), 10) + .addTrade(new ItemStack(Material.MAGENTA_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.MAGENTA_STAINED_GLASS, 64), 40); + + public static final TradePool SEAWEED = new TradePool() + .addTrade(new ItemStack(Material.WHITE_WOOL, 10), 0) + .addTrade(SlimefunItems.HARDENED_METAL_INGOT, 0) + .addTrade(new ItemStack(Material.POPPED_CHORUS_FRUIT, 5), 0) + .addTrade(new ItemStack(Material.POPPY, 20), 0) + .addTrade(new ItemStack(Material.CORNFLOWER, 20), 10) + .addTrade(new ItemStack(Material.WHITE_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.WHITE_STAINED_GLASS, 64), 40); + + public static final TradePool ASTRINGENT = new TradePool() + .addTrade(new ItemStack(Material.LIGHT_BLUE_WOOL, 10), 0) + .addTrade(SlimefunItems.NICKEL_INGOT, 0) + .addTrade(new ItemStack(Material.CHORUS_FRUIT, 5), 0) + .addTrade(new ItemStack(Material.DANDELION, 20), 0) + .addTrade(new ItemStack(Material.ALLIUM, 20), 10) + .addTrade(new ItemStack(Material.LIGHT_BLUE_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.LIGHT_BLUE_STAINED_GLASS, 64), 40); + + public static final TradePool TASTY = new TradePool() + .addTrade(new ItemStack(Material.LIGHT_GRAY_WOOL, 10), 0) + .addTrade(SlimefunItems.SOLAR_GENERATOR, 0) + .addTrade(new ItemStack(Material.END_ROD, 5), 0) + .addTrade(new ItemStack(Material.BLUE_ORCHID, 20), 0) + .addTrade(new ItemStack(Material.AZURE_BLUET, 20), 10) + .addTrade(new ItemStack(Material.LIGHT_GRAY_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.LIGHT_GRAY_STAINED_GLASS, 64), 40); + + public static final TradePool GREEN_TEA = new TradePool() + .addTrade(new ItemStack(Material.GRAY_WOOL, 10), 0) + .addTrade(SlimefunItems.BLANK_RUNE, 0) + .addTrade(new ItemStack(Material.LEAD, 5), 0) + .addTrade(new ItemStack(Material.ORANGE_TULIP, 20), 0) + .addTrade(new ItemStack(Material.WHITE_TULIP, 20), 10) + .addTrade(new ItemStack(Material.GRAY_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.GRAY_STAINED_GLASS, 64), 40); + + public static final TradePool BROTHY = new TradePool() + .addTrade(new ItemStack(Material.ORANGE_WOOL, 10), 0) + .addTrade(SlimefunItems.DUCT_TAPE, 0) + .addTrade(new ItemStack(Material.LIGHTNING_ROD, 5), 0) + .addTrade(new ItemStack(Material.OXEYE_DAISY, 20), 0) + .addTrade(new ItemStack(Material.LILY_OF_THE_VALLEY, 20), 10) + .addTrade(new ItemStack(Material.ORANGE_STAINED_GLASS, 20), 20) + .addTrade(new ItemStack(Material.ORANGE_STAINED_GLASS, 64), 40); + + public static final TradePool VEGEMITE = new TradePool() + .addTrade(new ItemStack(Material.MOSS_BLOCK, 10), 0) + .addTrade(SlimefunItems.ELECTRIC_MOTOR, 0) + .addTrade(new ItemStack(Material.SHROOMLIGHT, 5), 0) + .addTrade(new ItemStack(Material.RED_TULIP, 20), 0) + .addTrade(new ItemStack(Material.PINK_TULIP, 20), 10) + .addTrade(new ItemStack(Material.WITHER_ROSE, 2), 20) + .addTrade(new ItemStack(Material.BEACON), 40); + + public static final TradePool SALTY = new TradePool() + .addTrade(new ItemStack(Material.MOSS_CARPET, 10), 0) + .addTrade(SlimefunItems.ELECTRO_MAGNET, 0) + .addTrade(new ItemStack(Material.VINE, 5), 0) + .addTrade(new ItemStack(Material.BROWN_MUSHROOM, 10), 0) + .addTrade(new ItemStack(Material.RED_MUSHROOM, 10), 10) + .addTrade(new ItemStack(Material.RED_SAND, 20), 20) + .addTrade(new ItemStack(Material.ELYTRA), 40); + + public static final TradePool NASTY = new TradePool() + .addTrade(new ItemStack(Material.MUSIC_DISC_13), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_CAT), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_BLOCKS), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_CHIRP), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_FAR), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_MALL), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_MELLOHI), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_STAL), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_STRAD), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_WARD), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_11), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_WAIT), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_5), 0) + .addTrade(new ItemStack(Material.MUSIC_DISC_PIGSTEP), 40) + .addTrade(new ItemStack(Material.MUSIC_DISC_OTHERSIDE), 60); + + public static final TradePool ANCHOVY = new TradePool() + .addTrade(new ItemStack(Material.AZALEA, 5), 0) + .addTrade(SlimefunItems.GILDED_IRON, 0) + .addTrade(new ItemStack(Material.CALCITE, 15), 0) + .addTrade(new ItemStack(Material.CAVE_VINES, 10), 0) + .addTrade(new ItemStack(Material.GLOW_BERRIES, 10), 10) + .addTrade(new ItemStack(Material.SAND, 20), 20) + .addTrade(new ItemStack(Material.NAUTILUS_SHELL), 40); + + public static final TradePool GRIM = new TradePool() + .addTrade(new ItemStack(Material.DRIPSTONE_BLOCK, 5), 0) + .addTrade(SlimefunItems.CORINTHIAN_BRONZE_INGOT, 0) + .addTrade(new ItemStack(Material.BIG_DRIPLEAF, 5), 0) + .addTrade(new ItemStack(Material.POINTED_DRIPSTONE, 10), 0) + .addTrade(new ItemStack(Material.HANGING_ROOTS, 10), 10) + .addTrade(new ItemStack(Material.SNOW_BLOCK, 20), 20) + .addTrade(new ItemStack(Material.HEART_OF_THE_SEA), 60); + + public static final TradePool SUSHI = new TradePool() + .addTrade(new ItemStack(Material.COD, 5), 0) + .addTrade(new ItemStack(Material.SALMON, 5), 0) + .addTrade(new ItemStack(Material.PUFFERFISH, 5), 0) + .addTrade(new ItemStack(Material.TROPICAL_FISH, 5), 0) + .addTrade(new ItemStack(Material.COD_BUCKET), 20) + .addTrade(new ItemStack(Material.SALMON_BUCKET), 20) + .addTrade(new ItemStack(Material.AXOLOTL_BUCKET), 60); + + public static final TradePool FISHY = new TradePool() + .addTrade(new ItemStack(Material.FISHING_ROD, 5), 0) + .addTrade(SlimefunItems.FISH_JERKY, 0); + + public static final TradePool CRUCIFEROUS = new TradePool() + .addTrade(new ItemStack(Material.ACACIA_SAPLING, 5), 0) + .addTrade(new ItemStack(Material.SPRUCE_SAPLING, 5), 0) + .addTrade(new ItemStack(Material.BIRCH_SAPLING, 5), 0) + .addTrade(new ItemStack(Material.DARK_OAK_SAPLING, 5), 0) + .addTrade(new ItemStack(Material.JUNGLE_SAPLING, 5), 0) + .addTrade(new ItemStack(Material.OAK_SAPLING, 5), 0) + .addTrade(new ItemStack(Material.BAMBOO, 64), 60); + + public static final TradePool HEALTHY = new TradePool() + .addTrade(new ItemStack(Material.APPLE, 5), 0) + .addTrade(SlimefunItems.BANDAGE, 0) + .addTrade(new ItemStack(Material.GOLDEN_CARROT, 20), 20) + .addTrade(new ItemStack(Material.GOLDEN_APPLE), 60); + + public static final TradePool FUNGI = new TradePool() + .addTrade(new ItemStack(Material.MUSHROOM_STEW), 0) + .addTrade(new ItemStack(Material.RED_MUSHROOM_BLOCK, 5), 0) + .addTrade(new ItemStack(Material.BROWN_MUSHROOM_BLOCK, 5), 0) + .addTrade(new ItemStack(Material.MUSHROOM_STEM, 5), 10); + + public static final TradePool SAVOURY = new TradePool() + .addTrade(new ItemStack(Material.COOKED_BEEF, 10), 0) + .addTrade(SlimefunItems.MONSTER_JERKY, 0) + .addTrade(new ItemStack(Material.COOKED_CHICKEN, 10), 0) + .addTrade(new ItemStack(Material.COOKED_MUTTON, 10), 0) + .addTrade(new ItemStack(Material.COOKED_PORKCHOP, 10), 0) + .addTrade(new ItemStack(Material.COOKED_RABBIT, 10), 0); + + @Nonnull + private final RandomizedSet trades = new RandomizedSet<>(); + @Nullable + private NetheoBall tradeItem; + + public TradePool addTrade(@Nonnull ItemStack itemStack, int requiredFlavour) { + final Trade trade = new Trade(itemStack, requiredFlavour); + trade.setTradePool(this); + this.trades.add(trade, 1); + Registry.getInstance().addTrade(trade); + return this; + } + + @Nonnull + public RandomizedSet getAllTrades() { + return trades; + } + + @Nonnull + public Trade getRandomTrade(int flavour) { + final Set validTrades = trades.stream() + .filter(trade -> trade.getRequiredFlavour() <= flavour) + .collect(Collectors.toSet()); + return new RandomizedSet<>(validTrades).getRandom(); + } + + @Nullable + public NetheoBall getTradeItem() { + return tradeItem; + } + + public void setTradeItem(@Nonnull NetheoBall tradeItem) { + this.tradeItem = tradeItem; + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/GenericTickingMethods.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/GenericTickingMethods.java index 6532683..8ac8d0e 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/GenericTickingMethods.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/GenericTickingMethods.java @@ -1,8 +1,8 @@ package dev.sefiraat.netheopoiesis.implementation; import dev.sefiraat.netheopoiesis.api.items.GenericTickingSeed; -import dev.sefiraat.netheopoiesis.api.items.NetherSeed; import dev.sefiraat.netheopoiesis.api.items.NetherCrux; +import dev.sefiraat.netheopoiesis.api.items.NetherSeed; import dev.sefiraat.netheopoiesis.utils.Theme; import dev.sefiraat.netheopoiesis.utils.WorldUtils; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; @@ -184,9 +184,9 @@ private static void tryBroadcastRandomMessage(Location location, Theme theme, Se final World world = location.getWorld(); final Collection entities = world.getNearbyEntities(location, 10, 10, 10); final String message = set.stream() - .skip(ThreadLocalRandom.current().nextInt(set.size())) - .findFirst() - .orElse(""); + .skip(ThreadLocalRandom.current().nextInt(set.size())) + .findFirst() + .orElse(""); for (Entity entity : entities) { if (entity instanceof Player player) { diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/Groups.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/Groups.java index e7e8163..719d4b1 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/Groups.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/Groups.java @@ -5,6 +5,7 @@ import dev.sefiraat.netheopoiesis.implementation.groups.DummyItemGroup; import dev.sefiraat.netheopoiesis.implementation.groups.MainFlexGroup; import dev.sefiraat.netheopoiesis.implementation.groups.PurificationFlexGroup; +import dev.sefiraat.netheopoiesis.implementation.groups.TradesFlexGroup; import dev.sefiraat.netheopoiesis.utils.Keys; import dev.sefiraat.netheopoiesis.utils.Theme; import io.github.thebusybiscuit.slimefun4.libraries.dough.items.CustomItemStack; @@ -52,6 +53,22 @@ private Groups() { ) ); + public static final DummyItemGroup PASTES = new DummyItemGroup( + Keys.newKey("pastes"), + new CustomItemStack( + new ItemStack(Material.GLOWSTONE_DUST), + Theme.MAIN.color("下界乌托邦 - Seed Pastes") + ) + ); + + public static final DummyItemGroup BALLS = new DummyItemGroup( + Keys.newKey("netheo-balls"), + new CustomItemStack( + new ItemStack(Material.SNOWBALL), + Theme.MAIN.color("下界乌托邦 - Netheo Balls") + ) + ); + public static final DummyItemGroup CRUX = new DummyItemGroup( Keys.newKey("crux"), new CustomItemStack( @@ -68,6 +85,14 @@ private Groups() { ) ); + public static final TradesFlexGroup TRADES = new TradesFlexGroup( + Keys.newKey("trades"), + new CustomItemStack( + new ItemStack(Material.GOLD_INGOT), + Theme.MAIN.color("流浪猪灵") + ) + ); + public static final PurificationFlexGroup GUIDE = new PurificationFlexGroup( Keys.newKey("guide"), new CustomItemStack( diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/Items.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/Items.java index 5f2fbac..500fc70 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/Items.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/Items.java @@ -2,12 +2,6 @@ import dev.sefiraat.netheopoiesis.Netheopoiesis; import dev.sefiraat.netheopoiesis.api.RecipeTypes; -import dev.sefiraat.netheopoiesis.api.plant.Growth; -import dev.sefiraat.netheopoiesis.implementation.plant.GrowthStages; -import dev.sefiraat.netheopoiesis.implementation.plant.Placements; -import dev.sefiraat.netheopoiesis.listeners.DropListener; -import dev.sefiraat.netheopoiesis.implementation.flora.CrystallineCrux; -import dev.sefiraat.netheopoiesis.api.items.NetherCrux; import dev.sefiraat.netheopoiesis.api.items.BiomeSpreadingSeed; import dev.sefiraat.netheopoiesis.api.items.CruxSpreadingSeed; import dev.sefiraat.netheopoiesis.api.items.DoNothingSeed; @@ -15,11 +9,17 @@ import dev.sefiraat.netheopoiesis.api.items.EntitySpawningSeed; import dev.sefiraat.netheopoiesis.api.items.GenericTickingSeed; import dev.sefiraat.netheopoiesis.api.items.HarvestableSeed; +import dev.sefiraat.netheopoiesis.api.items.NetherCrux; +import dev.sefiraat.netheopoiesis.api.plant.Growth; +import dev.sefiraat.netheopoiesis.implementation.flora.CrystallineCrux; import dev.sefiraat.netheopoiesis.implementation.flora.PurificationSeed; import dev.sefiraat.netheopoiesis.implementation.flora.WetSeed; +import dev.sefiraat.netheopoiesis.implementation.plant.GrowthStages; +import dev.sefiraat.netheopoiesis.implementation.plant.Placements; import dev.sefiraat.netheopoiesis.implementation.tools.Analyser; import dev.sefiraat.netheopoiesis.implementation.tools.EnderCake; import dev.sefiraat.netheopoiesis.implementation.tools.HarvestingTool; +import dev.sefiraat.netheopoiesis.implementation.tools.MixingQuartz; import dev.sefiraat.netheopoiesis.implementation.tools.PurificationBarometer; import dev.sefiraat.netheopoiesis.implementation.tools.PurificationScanner; import dev.sefiraat.netheopoiesis.utils.EasterEggUtils; @@ -47,6 +47,7 @@ public static void setup(Netheopoiesis addon) { final ItemStack oakPlank = new ItemStack(Material.OAK_PLANKS); final ItemStack ironIngot = new ItemStack(Material.IRON_INGOT); final ItemStack glass = new ItemStack(Material.GLASS); + final ItemStack quartz = new ItemStack(Material.QUARTZ); final ItemStack redstone = new ItemStack(Material.REDSTONE); final ItemStack wheat = new ItemStack(Material.WHEAT); final ItemStack milkBucket = new ItemStack(Material.MILK_BUCKET); @@ -206,6 +207,18 @@ public static void setup(Netheopoiesis addon) { } ).register(addon); + new MixingQuartz( + Groups.TOOLS, + Stacks.MIXING_QUARTZ, + RecipeType.ENHANCED_CRAFTING_TABLE, + new ItemStack[]{ + null, quartz, null, + quartz, Stacks.HARVESTING_TOOL, quartz, + Stacks.SAINTLY_SEED, glass, Stacks.SAINTLY_SEED, + }, + 50 + ).register(addon); + new Analyser( Groups.TOOLS, Stacks.SEED_ANALYSER, @@ -234,7 +247,7 @@ public static void setup(Netheopoiesis addon) { new PurificationSeed( Stacks.PURIFICATION_SEED, - DropListener.createRecipe(Stacks.PURIFICATION_SEED, new ItemStack(Material.SOUL_SOIL), 0.05) + RecipeTypes.createWorldDropRecipe(Stacks.PURIFICATION_SEED, new ItemStack(Material.SOUL_SOIL), 0.05) ) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.NULL, 1, 0.30)) .tryRegister(addon); @@ -244,6 +257,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.PURIFIED_NETHERRACK) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.ALL, 2, 0.2)) .addBreedingPair(Stacks.ROTTEN_SEED.getItemId(), Stacks.PURIFICATION_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(2, 0, 0, 0, 0) .tryRegister(addon); new CruxSpreadingSeed(Stacks.SPIRIT_SEED) @@ -251,6 +265,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.VORACIOUS_DIRT) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.PURIFIED_AND_UP, 4, 0.15)) .addBreedingPair(Stacks.SOUL_SEED.getItemId(), Stacks.PERFECTION_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(4, 0, 0, 0, 0) .tryRegister(addon); new CruxSpreadingSeed(Stacks.SAINTLY_SEED) @@ -258,6 +273,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.NETHER_DIRT) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.VORACIOUS_AND_UP, 8, 0.1)) .addBreedingPair(Stacks.OAKENDRAN_SEED.getItemId(), Stacks.SPIRIT_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(8, 0, 0, 0, 0) .tryRegister(addon); new CruxSpreadingSeed(Stacks.EDEN_SEED) @@ -265,6 +281,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.NETHER_GRASS) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.NETHER_DIRT_AND_UP, 16, 0.1)) .addBreedingPair(Stacks.GATEWAY_SEED.getItemId(), Stacks.PERFECTION_SEED.getItemId(), 0.15, 0.2) + .addFlavourProfile(16, 0, 0, 0, 0) .tryRegister(addon); new BiomeSpreadingSeed(Stacks.JUNGLE_SEED) @@ -273,6 +290,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.JUNGLE_CRUX) .setGrowth(new Growth(GrowthStages.VINEY_GREEN, Placements.JUNGLE_FRINGE, 16, 0.1)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.EDEN_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(16, 0, 0, 4, 0) .tryRegister(addon); new BiomeSpreadingSeed(Stacks.BEACH_SEED) @@ -281,6 +299,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.BEACH_CRUX) .setGrowth(new Growth(GrowthStages.VINEY_YELLOW, Placements.BEACH_FRINGE, 16, 0.1)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.EDEN_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(16, 0, 4, 0, 0) .tryRegister(addon); new BiomeSpreadingSeed(Stacks.DESERT_SEED) @@ -289,6 +308,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.DESERT_CRUX) .setGrowth(new Growth(GrowthStages.VINEY_RED, Placements.DESERT_FRINGE, 16, 0.1)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.EDEN_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(16, 0, 0, 0, 4) .tryRegister(addon); new BiomeSpreadingSeed(Stacks.SNOW_SEED) @@ -297,6 +317,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.SNOW_CRUX) .setGrowth(new Growth(GrowthStages.VINEY_CYAN, Placements.SNOW_FRINGE, 16, 0.1)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.EDEN_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(20, 0, 0, 0, 0) .tryRegister(addon); new BiomeSpreadingSeed(Stacks.STONEY_SEED) @@ -305,6 +326,7 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.STONEY_CRUX) .setGrowth(new Growth(GrowthStages.VINEY_PURPLE, Placements.STONEY_FRINGE, 16, 0.1)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.EDEN_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(16, 0, 2, 2, 0) .tryRegister(addon); new BiomeSpreadingSeed(Stacks.SWAMP_SEED) @@ -313,12 +335,14 @@ public static void setup(Netheopoiesis addon) { .setCrux(Stacks.SWAMP_CRUX) .setGrowth(new Growth(GrowthStages.VINEY_GREEN, Placements.SWAMP_FRINGE, 16, 0.1)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.EDEN_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(16, 4, 0, 0, 0) .tryRegister(addon); new GenericTickingSeed(Stacks.SPINDLE_SEED) .setConsumer(GenericTickingMethods::onTickSpindleSeed) .setGrowth(new Growth(GrowthStages.SPIKEY_ORANGE, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.PURIFICATION_SEED.getItemId(), Stacks.PURIFICATION_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 1, 0, 1, 0) .tryRegister(addon); new DroppingSeed(Stacks.GRAINY_SEED) @@ -326,6 +350,7 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.05) .setGrowth(new Growth(GrowthStages.VINEY_RED, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.PURIFICATION_SEED.getItemId(), Stacks.PURIFICATION_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 0, 0, 1, 1) .tryRegister(addon); new DroppingSeed(Stacks.STRINGY_SEED) @@ -333,6 +358,7 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.05) .setGrowth(new Growth(GrowthStages.VINEY_GREEN, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.PURIFICATION_SEED.getItemId(), Stacks.PURIFICATION_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 1, 1, 0, 0) .tryRegister(addon); new DroppingSeed(Stacks.GRASS_SEED) @@ -343,6 +369,7 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.05) .setGrowth(new Growth(GrowthStages.VINEY_GREEN, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.SPINDLE_SEED.getItemId(), Stacks.STRINGY_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(2, 0, 0, 0, 0) .tryRegister(addon); new DroppingSeed(Stacks.COBBLED_SEED) @@ -350,95 +377,111 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.05) .setGrowth(new Growth(GrowthStages.VINEY_PURPLE, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.SPINDLE_SEED.getItemId(), Stacks.GRAINY_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 0, 1, 1, 0) .tryRegister(addon); new HarvestableSeed(Stacks.VOLCANIC_SEED) .setHarvestingResult(new ItemStack(Material.GRANITE)) .setGrowth(new Growth(GrowthStages.FUNGAL_RED, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.COBBLED_SEED.getItemId(), Stacks.COBBLED_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 0, 1, 1, 1) .tryRegister(addon); new HarvestableSeed(Stacks.IGNEOUS_SEED) .setHarvestingResult(new ItemStack(Material.ANDESITE)) .setGrowth(new Growth(GrowthStages.FUNGAL_CYAN, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.COBBLED_SEED.getItemId(), Stacks.COBBLED_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 0, 1, 1, 1) .tryRegister(addon); new HarvestableSeed(Stacks.FELDSPAR_SEED) .setHarvestingResult(new ItemStack(Material.DIORITE)) .setGrowth(new Growth(GrowthStages.FUNGAL_YELLOW, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.COBBLED_SEED.getItemId(), Stacks.COBBLED_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 0, 1, 1, 1) .tryRegister(addon); new HarvestableSeed(Stacks.DEEPSLATE_SEED) .setHarvestingResult(new ItemStack(Material.COBBLED_DEEPSLATE)) .setGrowth(new Growth(GrowthStages.VINEY_PURPLE, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.IGNEOUS_SEED.getItemId(), Stacks.VOLCANIC_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(0, 0, 1, 1, 1) .tryRegister(addon); new HarvestableSeed(Stacks.DUSTY_SEED) .setHarvestingResult(new ItemStack(Material.GRAVEL)) .setGrowth(new Growth(GrowthStages.SPIKEY_CYAN, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.COBBLED_SEED.getItemId(), Stacks.GRAINY_SEED.getItemId(), 0.1, 0.2) + .addFlavourProfile(1, 0, 1, 0, 1) .tryRegister(addon); new HarvestableSeed(Stacks.SEASIDE_SEED) .setHarvestingResult(new ItemStack(Material.SAND)) .setGrowth(new Growth(GrowthStages.SPIKEY_YELLOW, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.SPINDLE_SEED.getItemId(), Stacks.DUSTY_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 2, 0, 0) .tryRegister(addon); new HarvestableSeed(Stacks.NORI_SEED) .setHarvestingResult(new ItemStack(Material.KELP)) .setGrowth(new Growth(GrowthStages.SPIKEY_GREEN, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.SEASIDE_SEED.getItemId(), Stacks.GRASS_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 3, 0, 0) .tryRegister(addon); new HarvestableSeed(Stacks.MOLDABLE_SEED) .setHarvestingResult(new ItemStack(Material.CLAY_BALL)) .setGrowth(new Growth(GrowthStages.FUNGAL_PURPLE, Placements.ALL, 1, 0.09)) .addBreedingPair(Stacks.SEASIDE_SEED.getItemId(), Stacks.COBBLED_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 2, 0, 1) .tryRegister(addon); new WetSeed(Stacks.WET_SEED) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.ALL, 2, 0.11)) .addBreedingPair(Stacks.SEASIDE_SEED.getItemId(), Stacks.MOLDABLE_SEED.getItemId(), 0.1, 0.1) + .addFlavourProfile(1, 0, 0, 0, 1) .tryRegister(addon); new EntitySpawningSeed(Stacks.SPLINTERED_SEED) .setEntityType(EntityType.SKELETON) .setGrowth(new Growth(GrowthStages.SPAWNING_CYAN, Placements.ALL, 2, 0.08)) .addBreedingPair(Stacks.SPINDLE_SEED.getItemId(), Stacks.STRINGY_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 3, 0, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.ROTTEN_SEED) .setEntityType(EntityType.ZOMBIE) .setGrowth(new Growth(GrowthStages.SPAWNING_GREEN, Placements.ALL, 2, 0.08)) .addBreedingPair(Stacks.SPLINTERED_SEED.getItemId(), Stacks.DUSTY_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 2, 0, 0, 1) .tryRegister(addon); new HarvestableSeed(Stacks.METALLIC_SEED) .setHarvestingResult(new ItemStack(Material.IRON_NUGGET)) .setGrowth(new Growth(GrowthStages.VINEY_RED, Placements.ALL, 2, 0.08)) .addBreedingPair(Stacks.SOUL_SEED.getItemId(), Stacks.SOUL_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 0, 0, 4) .tryRegister(addon); new HarvestableSeed(Stacks.TARNISHED_SEED) .setHarvestingResult(new ItemStack(Material.RAW_COPPER)) .setGrowth(new Growth(GrowthStages.VINEY_ORANGE, Placements.ALL, 2, 0.08)) .addBreedingPair(Stacks.METALLIC_SEED.getItemId(), Stacks.DUSTY_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(2, 2, 0, 0, 0) .tryRegister(addon); new HarvestableSeed(Stacks.SHINY_SEED) .setHarvestingResult(new ItemStack(Material.GOLD_NUGGET)) .setGrowth(new Growth(GrowthStages.VINEY_YELLOW, Placements.ALL, 2, 0.08)) .addBreedingPair(Stacks.SOUL_SEED.getItemId(), Stacks.SOUL_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 1, 2, 0) .tryRegister(addon); new HarvestableSeed(Stacks.SMOOTH_SEED) .setHarvestingResult(new ItemStack(Material.AMETHYST_SHARD)) .setGrowth(new Growth(GrowthStages.VINEY_PURPLE, Placements.ALL, 2, 0.08)) .addBreedingPair(Stacks.SOUL_SEED.getItemId(), Stacks.SOUL_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(1, 1, 1, 1, 0) .tryRegister(addon); new DroppingSeed(Stacks.SEEDY_SEED) @@ -449,48 +492,56 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.05) .setGrowth(new Growth(GrowthStages.FUNGAL_YELLOW, Placements.PURIFIED_AND_UP, 2, 0.08)) .addBreedingPair(Stacks.SMOOTH_SEED.getItemId(), Stacks.GRASS_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(2, 1, 0, 1, 0) .tryRegister(addon); new GenericTickingSeed(Stacks.SWEET_SEED) .setConsumer(GenericTickingMethods::onTickSweetSeed) .setGrowth(new Growth(GrowthStages.SPIKEY_CYAN, Placements.PURIFIED_AND_UP, 3, 0.07)) .addBreedingPair(Stacks.SEEDY_SEED.getItemId(), Stacks.SEASIDE_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(5, 0, 0, 0, 0) .tryRegister(addon); new HarvestableSeed(Stacks.ENCHANTED_SEED) .setHarvestingResult(new ItemStack(Material.LAPIS_LAZULI)) .setGrowth(new Growth(GrowthStages.VINEY_BLUE, Placements.PURIFIED_AND_UP, 3, 0.07)) .addBreedingPair(Stacks.SHINY_SEED.getItemId(), Stacks.SOUL_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 0, 0, 4) .tryRegister(addon); new HarvestableSeed(Stacks.COMBUSTIBLE_SEED) .setHarvestingResult(new ItemStack(Material.COAL)) .setGrowth(new Growth(GrowthStages.SPIKEY_RED, Placements.PURIFIED_AND_UP, 3, 0.07)) .addBreedingPair(Stacks.SMOOTH_SEED.getItemId(), Stacks.SPLINTERED_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 0, 2, 3) .tryRegister(addon); new EntitySpawningSeed(Stacks.PROTECTIVE_SEED) .setEntityType(EntityType.IRON_GOLEM) .setGrowth(new Growth(GrowthStages.SPAWNING_PURPLE, Placements.PURIFIED_AND_UP, 5, 0.03)) .addBreedingPair(Stacks.METALLIC_SEED.getItemId(), Stacks.SOUL_SEED.getItemId(), 0.2, 0.1) + .addFlavourProfile(3, 0, 2, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.PORKY_SEED) .setEntityType(EntityType.PIG) .setGrowth(new Growth(GrowthStages.SPAWNING_RED, Placements.PURIFIED_AND_UP, 3, 0.08)) .addBreedingPair(Stacks.SPINDLE_SEED.getItemId(), Stacks.SOUL_SEED.getItemId(), 0.07, 0.1) + .addFlavourProfile(0, 0, 2, 0, 3) .tryRegister(addon); new HarvestableSeed(Stacks.VALUABLE_SEED) .setHarvestingResult(new ItemStack(Material.EMERALD)) .setGrowth(new Growth(GrowthStages.VINEY_GREEN, Placements.PURIFIED_AND_UP, 3, 0.07)) .addBreedingPair(Stacks.SHINY_SEED.getItemId(), Stacks.ENCHANTED_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 2, 2, 1) .tryRegister(addon); new HarvestableSeed(Stacks.PERFECTION_SEED) .setHarvestingResult(new ItemStack(Material.DIAMOND)) .setGrowth(new Growth(GrowthStages.FUNGAL_BLUE, Placements.PURIFIED_AND_UP, 5, 0.07)) .addBreedingPair(Stacks.SHINY_SEED.getItemId(), Stacks.VALUABLE_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 2, 0, 3, 0) .tryRegister(addon); new DroppingSeed(Stacks.RAINBOW_SEED) @@ -513,6 +564,7 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.1) .setGrowth(new Growth(GrowthStages.VINEY_CYAN, Placements.VORACIOUS_AND_UP, 8, 0.06)) .addBreedingPair(Stacks.SPIRIT_SEED.getItemId(), Stacks.SPIRIT_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(4, 2, 2, 0, 0) .tryRegister(addon); new DroppingSeed(Stacks.GLOWING_SEED) @@ -522,30 +574,35 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.08) .setGrowth(new Growth(GrowthStages.FUNGAL_RED, Placements.VORACIOUS_AND_UP, 8, 0.06)) .addBreedingPair(Stacks.SPIRIT_SEED.getItemId(), Stacks.SPIRIT_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(0, 0, 4, 0, 4) .tryRegister(addon); new HarvestableSeed(Stacks.GLISTENING_SEED) .setHarvestingResult(new ItemStack(Material.GLISTERING_MELON_SLICE)) .setGrowth(new Growth(GrowthStages.SPIKEY_ORANGE, Placements.VORACIOUS_AND_UP, 5, 0.07)) .addBreedingPair(Stacks.GLOWING_SEED.getItemId(), Stacks.METALLIC_SEED.getItemId(), 0.05, 0.2) + .addFlavourProfile(3, 3, 2, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.ETHEREAL_SEED) .setEntityType(EntityType.ENDERMAN) .setGrowth(new Growth(GrowthStages.SPAWNING_GREEN, Placements.VORACIOUS_AND_UP, 6, 0.06)) .addBreedingPair(Stacks.SPIRIT_SEED.getItemId(), Stacks.SPIRIT_SEED.getItemId(), 0.1, 0.1) + .addFlavourProfile(0, 0, 0, 2, 6) .tryRegister(addon); new EntitySpawningSeed(Stacks.IGNITED_SEED) .setEntityType(EntityType.BLAZE) .setGrowth(new Growth(GrowthStages.SPAWNING_RED, Placements.VORACIOUS_AND_UP, 8, 0.07)) .addBreedingPair(Stacks.ETHEREAL_SEED.getItemId(), Stacks.COMBUSTIBLE_SEED.getItemId(), 0.2, 0.25) + .addFlavourProfile(0, 4, 4, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.BARTERED_SEED) .setEntityType(EntityType.PIGLIN) .setGrowth(new Growth(GrowthStages.SPAWNING_CYAN, Placements.VORACIOUS_AND_UP, 8, 0.06)) .addBreedingPair(Stacks.IGNEOUS_SEED.getItemId(), Stacks.PORKY_SEED.getItemId(), 0.2, 0.25) + .addFlavourProfile(0, 0, 0, 0, 8) .tryRegister(addon); new DroppingSeed(Stacks.PRISMATIC_SEED) @@ -554,89 +611,104 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.05) .setGrowth(new Growth(GrowthStages.SPIKEY_GREEN, Placements.VORACIOUS_AND_UP, 9, 0.06)) .addBreedingPair(Stacks.RAINBOW_SEED.getItemId(), Stacks.SPLINTERED_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(0, 0, 8, 0, 0) .tryRegister(addon); new HarvestableSeed(Stacks.POROUS_SEED) .setHarvestingResult(new ItemStack(Material.SPONGE)) .setGrowth(new Growth(GrowthStages.FUNGAL_YELLOW, Placements.VORACIOUS_AND_UP, 9, 0.06)) .addBreedingPair(Stacks.PRISMATIC_SEED.getItemId(), Stacks.SEASIDE_SEED.getItemId(), 0.05, 0.05) + .addFlavourProfile(2, 2, 4, 0, 0) .tryRegister(addon); new HarvestableSeed(Stacks.LEARNED_SEED) .setHarvestingResult(new ItemStack(Material.EXPERIENCE_BOTTLE)) .setGrowth(new Growth(GrowthStages.VINEY_ORANGE, Placements.VORACIOUS_AND_UP, 9, 0.06)) .addBreedingPair(Stacks.ETHEREAL_SEED.getItemId(), Stacks.ENCHANTED_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(2, 0, 0, 6, 0) .tryRegister(addon); new HarvestableSeed(Stacks.BUSY_SEED) .setHarvestingResult(new ItemStack(Material.COOKIE)) .setGrowth(new Growth(GrowthStages.SPIKEY_RED, Placements.VORACIOUS_AND_UP, 9, 0.06)) .addBreedingPair(Stacks.LEARNED_SEED.getItemId(), Stacks.SWEET_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(2, 2, 2, 2, 2) .tryRegister(addon); new GenericTickingSeed(Stacks.OAKENDRAN_SEED) .setConsumer(GenericTickingMethods::onTickOakendranSeed) .setGrowth(new Growth(GrowthStages.VINEY_ORANGE, Placements.VORACIOUS_AND_UP, 12, 0.04)) .addBreedingPair(Stacks.ETHEREAL_SEED.getItemId(), Stacks.SPINDLE_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(3, 2, 1, 1, 1) .tryRegister(addon); new HarvestableSeed(Stacks.ADDON_BERRY_SEED) .setHarvestingResult(Stacks.ADDON_BERRY) .setGrowth(new Growth(GrowthStages.SPIKEY_RED, Placements.NETHER_DIRT_AND_UP, 10, 0.2)) .addBreedingPair(Stacks.SAINTLY_SEED.getItemId(), Stacks.SAINTLY_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(16, 0, 0, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.CUTE_SEED) .setEntityType(EntityType.AXOLOTL) .setGrowth(new Growth(GrowthStages.SPAWNING_BLUE, Placements.NETHER_DIRT_AND_UP, 15, 0.15)) .addBreedingPair(Stacks.SAINTLY_SEED.getItemId(), Stacks.SAINTLY_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(12, 2, 2, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.BEST_FRIEND_SEED) .setEntityType(EntityType.WOLF) .setGrowth(new Growth(GrowthStages.SPAWNING_CYAN, Placements.NETHER_DIRT_AND_UP, 16, 0.10)) .addBreedingPair(Stacks.CUTE_SEED.getItemId(), Stacks.SPLINTERED_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(0, 0, 8, 0, 8) .tryRegister(addon); new GenericTickingSeed(Stacks.MATH_SEED) .setConsumer(GenericTickingMethods::onAlessioTeach) .setGrowth(new Growth(GrowthStages.SPIKEY_BLUE, Placements.NETHER_DIRT_AND_UP, 16, 0.10)) .addBreedingPair(Stacks.BEST_FRIEND_SEED.getItemId(), Stacks.PERFECTION_SEED.getItemId(), 0.1, 0.15) + .addFlavourProfile(4, 4, 4, 0, 4) .tryRegister(addon); new HarvestableSeed(Stacks.BUZZING_SEED) .setHarvestingResult(new ItemStack(Material.HONEYCOMB)) .setGrowth(new Growth(GrowthStages.VINEY_YELLOW, Placements.NETHER_DIRT_AND_UP, 10, 0.2)) .addBreedingPair(Stacks.CUTE_SEED.getItemId(), Stacks.SPINDLE_SEED.getItemId(), 0.15, 0.2) + .addFlavourProfile(2, 4, 2, 6, 2) .tryRegister(addon); new EntitySpawningSeed(Stacks.TERRIFYING_SEED) .setEntityType(EntityType.WITHER_SKELETON) .setGrowth(new Growth(GrowthStages.SPAWNING_PURPLE, Placements.NETHER_DIRT_AND_UP, 15, 0.10)) .addBreedingPair(Stacks.SAINTLY_SEED.getItemId(), Stacks.SAINTLY_SEED.getItemId(), 0.10, 0.3) + .addFlavourProfile(0, 0, 16, 0, 0) .tryRegister(addon); new GenericTickingSeed(Stacks.HATE_FILLED_SEED) .setConsumer(GenericTickingMethods::onTickHateFilledSeed) .setGrowth(new Growth(GrowthStages.FUNGAL_ORANGE, Placements.NETHER_DIRT_AND_UP, 0, 0.2)) .addBreedingPair(Stacks.TERRIFYING_SEED.getItemId(), Stacks.PROTECTIVE_SEED.getItemId(), 0.2, 0.05) + .addFlavourProfile(0, 0, 10, 6, 0) .tryRegister(addon); new GenericTickingSeed(Stacks.PULSING_SEED) .setConsumer(GenericTickingMethods::onTickPulsingSeed) .setGrowth(new Growth(GrowthStages.VINEY_GREEN, Placements.NETHER_DIRT_AND_UP, 20, 0.08)) .addBreedingPair(Stacks.HATE_FILLED_SEED.getItemId(), Stacks.GLOWING_SEED.getItemId(), 0.15, 0.2) + .addFlavourProfile(10, 6, 0, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.GATEWAY_SEED) .setEntityType(EntityType.VILLAGER) .setGrowth(new Growth(GrowthStages.SPAWNING_PURPLE, Placements.NETHER_DIRT_AND_UP, 20, 0.08)) .addBreedingPair(Stacks.PULSING_SEED.getItemId(), Stacks.BARTERED_SEED.getItemId(), 0.15, 0.2) + .addFlavourProfile(0, 0, 0, 0, 16) .tryRegister(addon); new DoNothingSeed(Stacks.CRYSTALLINE_SEED) .setGrowth(new Growth(GrowthStages.SPIKEY_ORANGE, Placements.NETHER_GRASS_AND_UP, 0, 0.02)) .addBreedingPair(Stacks.EDEN_SEED.getItemId(), Stacks.WET_SEED.getItemId(), 0.15, 0.2) + .addFlavourProfile(8, 0, 0, 8, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.BLACK_AND_WHITE_SEED) @@ -650,24 +722,28 @@ public static void setup(Netheopoiesis addon) { ) .setGrowth(new Growth(GrowthStages.SPAWNING_BLUE, Placements.JUNGLE_BIOME, 25, 0.03)) .addBreedingPair(Stacks.JUNGLE_SEED.getItemId(), Stacks.PORKY_SEED.getItemId(), 0.01, 0.05) + .addFlavourProfile(0, 10, 1, 10, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.PARROT_SEED) .setEntityType(EntityType.PARROT) .setGrowth(new Growth(GrowthStages.SPAWNING_GREEN, Placements.JUNGLE_BIOME, 15, 0.10)) .addBreedingPair(Stacks.JUNGLE_SEED.getItemId(), Stacks.RAINBOW_SEED.getItemId(), 0.09, 0.15) + .addFlavourProfile(0, 5, 5, 5, 5) .tryRegister(addon); new EntitySpawningSeed(Stacks.WILD_SEED) .setEntityType(EntityType.OCELOT) .setGrowth(new Growth(GrowthStages.SPAWNING_YELLOW, Placements.JUNGLE_BIOME, 15, 0.10)) .addBreedingPair(Stacks.JUNGLE_SEED.getItemId(), Stacks.CUTE_SEED.getItemId(), 0.05, 0.15) + .addFlavourProfile(5, 5, 5, 0, 5) .tryRegister(addon); new EntitySpawningSeed(Stacks.SHELLED_SEED) .setEntityType(EntityType.TURTLE) .setGrowth(new Growth(GrowthStages.SPAWNING_GREEN, Placements.BEACH_BIOME, 15, 0.10)) .addBreedingPair(Stacks.BEACH_SEED.getItemId(), Stacks.PROTECTIVE_SEED.getItemId(), 0.05, 0.15) + .addFlavourProfile(4, 4, 12, 0, 0) .tryRegister(addon); new DroppingSeed(Stacks.TREASURED_SEED) @@ -676,30 +752,35 @@ public static void setup(Netheopoiesis addon) { .setTriggerChance(0.01) .setGrowth(new Growth(GrowthStages.SPIKEY_GREEN, Placements.BEACH_BIOME, 15, 0.06)) .addBreedingPair(Stacks.BEACH_SEED.getItemId(), Stacks.SHINY_SEED.getItemId(), 0.1, 0.5) + .addFlavourProfile(4, 4, 4, 4, 4) .tryRegister(addon); new GenericTickingSeed(Stacks.SPINEY_SEED) .setConsumer(GenericTickingMethods::onTickSpineySeed) .setGrowth(new Growth(GrowthStages.SPIKEY_GREEN, Placements.DESERT_BIOME, 10, 0.03)) .addBreedingPair(Stacks.DESERT_SEED.getItemId(), Stacks.SPINDLE_SEED.getItemId(), 0.2, 0.2) + .addFlavourProfile(0, 10, 0, 5, 5) .tryRegister(addon); new EntitySpawningSeed(Stacks.HUSKY_SEED) .setEntityType(EntityType.HUSK) .setGrowth(new Growth(GrowthStages.SPAWNING_YELLOW, Placements.DESERT_BIOME, 15, 0.06)) .addBreedingPair(Stacks.DESERT_SEED.getItemId(), Stacks.ROTTEN_SEED.getItemId(), 0.05, 0.25) + .addFlavourProfile(0, 0, 20, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.STRAY_SEED) .setEntityType(EntityType.STRAY) .setGrowth(new Growth(GrowthStages.SPAWNING_CYAN, Placements.SNOW_BIOME, 15, 0.06)) .addBreedingPair(Stacks.SNOW_SEED.getItemId(), Stacks.SPLINTERED_SEED.getItemId(), 0.1, 0.25) + .addFlavourProfile(0, 20, 0, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.POLAR_SEED) .setEntityType(EntityType.POLAR_BEAR) .setGrowth(new Growth(GrowthStages.SPAWNING_YELLOW, Placements.SNOW_BIOME, 12, 0.08)) .addBreedingPair(Stacks.SNOW_SEED.getItemId(), Stacks.PORKY_SEED.getItemId(), 0.05, 0.25) + .addFlavourProfile(0, 0, 1, 1, 1) .tryRegister(addon); new EntitySpawningSeed(Stacks.CHILLY_SEED) @@ -711,24 +792,28 @@ public static void setup(Netheopoiesis addon) { ) .setGrowth(new Growth(GrowthStages.SPAWNING_PURPLE, Placements.SNOW_BIOME, 12, 0.08)) .addBreedingPair(Stacks.SNOW_SEED.getItemId(), Stacks.BEST_FRIEND_SEED.getItemId(), 0.05, 0.1) + .addFlavourProfile(20, 0, 0, 0, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.HEXED_SEED) .setEntityType(EntityType.WITCH) .setGrowth(new Growth(GrowthStages.SPAWNING_PURPLE, Placements.SWAMP_BIOME, 12, 0.04)) .addBreedingPair(Stacks.SWAMP_SEED.getItemId(), Stacks.ENCHANTED_SEED.getItemId(), 0.03, 0.1) + .addFlavourProfile(0, 0, 0, 20, 0) .tryRegister(addon); new EntitySpawningSeed(Stacks.SLIMY_SEED) .setEntityType(EntityType.SLIME) .setGrowth(new Growth(GrowthStages.SPAWNING_GREEN, Placements.SWAMP_BIOME, 12, 0.08)) .addBreedingPair(Stacks.SWAMP_SEED.getItemId(), Stacks.GLISTENING_SEED.getItemId(), 0.05, 0.1) + .addFlavourProfile(0, 0, 0, 0, 20) .tryRegister(addon); new GenericTickingSeed(Stacks.BLOB_SEED) .setConsumer(GenericTickingMethods::onWalshyIsMad) .setGrowth(new Growth(GrowthStages.FUNGAL_YELLOW, Placements.SWAMP_BIOME, 12, 0.08)) .addBreedingPair(Stacks.SLIMY_SEED.getItemId(), Stacks.BEST_FRIEND_SEED.getItemId(), 0.05, 0.1) + .addFlavourProfile(0, 20, 0, 0, 0) .tryRegister(addon); // endregion diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/Stacks.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/Stacks.java index 9bf06c2..4b6ffbb 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/Stacks.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/Stacks.java @@ -109,6 +109,18 @@ private Stacks() { "不会改变生物群系." ); + public static final SlimefunItemStack MIXING_QUARTZ = Theme.themedSlimefunItemStack( + "NPS_MIXING_QUARTZ_1", + Material.QUARTZ, + Theme.TOOL, + "Mixing Quartz", + "Drop 3 Netheo Paste on the ground", + "and right click with this mixing", + "quartz to form a Netheo Ball", + "The type and flavour depends on the", + "pastes dropped." + ); + public static final SlimefunItemStack ENDER_CAKE = Theme.themedSlimefunItemStack( "NPS_ENDER_CAKE", Material.CAKE, diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/CrystallineCrux.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/CrystallineCrux.java index 123f882..8ff4b8e 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/CrystallineCrux.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/CrystallineCrux.java @@ -3,8 +3,8 @@ import dev.sefiraat.netheopoiesis.Netheopoiesis; import dev.sefiraat.netheopoiesis.Purification; import dev.sefiraat.netheopoiesis.api.items.NetherCrux; -import dev.sefiraat.netheopoiesis.implementation.tasks.UpdateCruxTask; import dev.sefiraat.netheopoiesis.implementation.Stacks; +import dev.sefiraat.netheopoiesis.implementation.tasks.UpdateCruxTask; import dev.sefiraat.netheopoiesis.utils.Keys; import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/PurificationSeed.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/PurificationSeed.java index 4b3fddd..bbe1dd7 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/PurificationSeed.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/flora/PurificationSeed.java @@ -2,12 +2,12 @@ import dev.sefiraat.netheopoiesis.Netheopoiesis; import dev.sefiraat.netheopoiesis.Purification; -import dev.sefiraat.netheopoiesis.api.events.CruxSpreadEvent; -import dev.sefiraat.netheopoiesis.implementation.tasks.UpdateCruxTask; import dev.sefiraat.netheopoiesis.api.RecipeTypes; -import dev.sefiraat.netheopoiesis.implementation.Stacks; +import dev.sefiraat.netheopoiesis.api.events.CruxSpreadEvent; import dev.sefiraat.netheopoiesis.api.items.NetherCrux; import dev.sefiraat.netheopoiesis.api.items.NetherSeed; +import dev.sefiraat.netheopoiesis.implementation.Stacks; +import dev.sefiraat.netheopoiesis.implementation.tasks.UpdateCruxTask; import dev.sefiraat.netheopoiesis.utils.Keys; import dev.sefiraat.netheopoiesis.utils.ProtectionUtils; import dev.sefiraat.netheopoiesis.utils.WorldUtils; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/DiscoveriesFlexGroup.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/DiscoveriesFlexGroup.java index 17313a7..f2c81c3 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/DiscoveriesFlexGroup.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/DiscoveriesFlexGroup.java @@ -1,8 +1,8 @@ package dev.sefiraat.netheopoiesis.implementation.groups; -import dev.sefiraat.netheopoiesis.PlantRegistry; -import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedingPair; +import dev.sefiraat.netheopoiesis.Registry; import dev.sefiraat.netheopoiesis.api.items.NetherSeed; +import dev.sefiraat.netheopoiesis.api.plant.breeding.BreedingPair; import dev.sefiraat.netheopoiesis.implementation.Groups; import dev.sefiraat.netheopoiesis.utils.StatisticUtils; import dev.sefiraat.netheopoiesis.utils.Theme; @@ -114,7 +114,7 @@ public void open(Player p, PlayerProfile profile, SlimefunGuideMode mode) { @ParametersAreNonnullByDefault private void setupPage(Player player, PlayerProfile profile, SlimefunGuideMode mode, ChestMenu menu, int page) { - final List breedingPairs = new ArrayList<>(PlantRegistry.getInstance().getBreedingPairs()); + final List breedingPairs = new ArrayList<>(Registry.getInstance().getBreedingPairs()); final int amount = breedingPairs.size(); final int totalPages = (int) Math.ceil(amount / (double) PAGE_SIZE); final int start = (page - 1) * PAGE_SIZE; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/MainFlexGroup.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/MainFlexGroup.java index 3f77b6e..9bb0994 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/MainFlexGroup.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/MainFlexGroup.java @@ -27,9 +27,12 @@ public class MainFlexGroup extends FlexItemGroup { private static final int CRAFTING = 9; private static final int TOOLS = 10; private static final int SEEDS = 11; - private static final int CRUX = 12; - private static final int DISCOVERIES = 13; - private static final int GUIDE = 14; + private static final int PASTES = 12; + private static final int BALLS = 13; + private static final int CRUX = 14; + private static final int DISCOVERIES = 15; + private static final int TRADES = 16; + private static final int GUIDE = 17; private static final int[] HEADER = new int[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8 @@ -108,19 +111,37 @@ private void setupPage(Player player, PlayerProfile profile, SlimefunGuideMode m openPage(profile, Groups.SEEDS, mode, 1) ); + // Pastes + menu.replaceExistingItem(PASTES, Groups.PASTES.getItem(player)); + menu.addMenuClickHandler(PASTES, (player1, i1, itemStack1, clickAction) -> + openPage(profile, Groups.PASTES, mode, 1) + ); + + // Balls + menu.replaceExistingItem(BALLS, Groups.BALLS.getItem(player)); + menu.addMenuClickHandler(BALLS, (player1, i1, itemStack1, clickAction) -> + openPage(profile, Groups.BALLS, mode, 1) + ); + // Crux' menu.replaceExistingItem(CRUX, Groups.CRUX.getItem(player)); menu.addMenuClickHandler(CRUX, (player1, i1, itemStack1, clickAction) -> openPage(profile, Groups.CRUX, mode, 1) ); - // Crux' + // Discoveries menu.replaceExistingItem(DISCOVERIES, Groups.DISCOVERIES.getItem(player)); menu.addMenuClickHandler(DISCOVERIES, (player1, i1, itemStack1, clickAction) -> openPage(profile, Groups.DISCOVERIES, mode, 1) ); - // Guide' + // Trades + menu.replaceExistingItem(TRADES, Groups.TRADES.getItem(player)); + menu.addMenuClickHandler(TRADES, (player1, i1, itemStack1, clickAction) -> + openPage(profile, Groups.TRADES, mode, 1) + ); + + // Guide menu.replaceExistingItem(GUIDE, Groups.GUIDE.getItem(player)); menu.addMenuClickHandler(GUIDE, (player1, i1, itemStack1, clickAction) -> openPage(profile, Groups.GUIDE, mode, 1) diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/PurificationFlexGroup.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/PurificationFlexGroup.java index edf1814..e4b0dae 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/PurificationFlexGroup.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/PurificationFlexGroup.java @@ -113,7 +113,8 @@ public class PurificationFlexGroup extends FlexItemGroup { Theme.CLICK_INFO.asTitle("河豚", Purification.SPAWN_PUFFER_FISH), Theme.CLICK_INFO.asTitle("热带鱼", Purification.SPAWN_TROPICAL_FISH), Theme.CLICK_INFO.asTitle("美西螈", Purification.SPAWN_AXOLOTL), - Theme.CLICK_INFO.asTitle("流浪商人", Purification.WANDERING_TRADER) + Theme.CLICK_INFO.asTitle("流浪商人", Purification.WANDERING_TRADER), + Theme.CLICK_INFO.asTitle("流浪猪灵", Purification.WANDERING_PIGLIN) ); private static final ItemStack PIGLIN_STACK = new CustomItemStack( diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/TradesFlexGroup.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/TradesFlexGroup.java new file mode 100644 index 0000000..55a2f82 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/groups/TradesFlexGroup.java @@ -0,0 +1,246 @@ +package dev.sefiraat.netheopoiesis.implementation.groups; + +import dev.sefiraat.netheopoiesis.Registry; +import dev.sefiraat.netheopoiesis.api.items.NetherSeed; +import dev.sefiraat.netheopoiesis.api.plant.netheos.Trade; +import dev.sefiraat.netheopoiesis.implementation.Groups; +import dev.sefiraat.netheopoiesis.utils.StatisticUtils; +import dev.sefiraat.netheopoiesis.utils.Theme; +import io.github.bakedlibs.dough.items.CustomItemStack; +import io.github.thebusybiscuit.slimefun4.api.items.groups.FlexItemGroup; +import io.github.thebusybiscuit.slimefun4.api.player.PlayerProfile; +import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuide; +import io.github.thebusybiscuit.slimefun4.core.guide.SlimefunGuideMode; +import io.github.thebusybiscuit.slimefun4.implementation.Slimefun; +import io.github.thebusybiscuit.slimefun4.utils.ChestMenuUtils; +import me.mrCookieSlime.CSCoreLibPlugin.general.Inventory.ChestMenu; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * This flex group is used to display trading information to the player. + * Information is locked until the player has bred the appropriate plant at least once + */ +public class TradesFlexGroup extends FlexItemGroup { + + private static final int PAGE_SIZE = 36; + + private static final int GUIDE_BACK = 1; + + private static final int PAGE_PREVIOUS = 46; + private static final int PAGE_NEXT = 52; + + private static final int[] HEADER = new int[]{ + 0, 1, 2, 3, 4, 5, 6, 7, 8 + }; + private static final int[] FOOTER = new int[]{ + 45, 46, 47, 48, 49, 50, 51, 52, 53 + }; + + private static final int TRADE_ITEM_SLOT = 21; + private static final int[] TRADE_ITEM_INFO_SLOT = new int[]{12, 30}; + + private static final int DROPPED_ITEM_SLOT = 23; + private static final int[] DROPPED_ITEM_INFO_SLOT = new int[]{14, 32}; + + private static final int[] HELD_SLOTS = new int[]{37, 38, 39, 40, 41, 42, 43}; + + private static final ItemStack TRADE_ITEM_INFO = new CustomItemStack( + Material.LIGHT_BLUE_STAINED_GLASS_PANE, + Theme.PASSIVE + "The Item Given to the Trader" + ); + + private static final ItemStack DROPPED_ITEM_INFO = new CustomItemStack( + Material.LIGHT_BLUE_STAINED_GLASS_PANE, + Theme.PASSIVE + "The Item you receive" + ); + + private static final ItemStack HELD_SLOT = new CustomItemStack( + Material.BLACK_STAINED_GLASS_PANE, + " " + ); + + private static final ItemStack NOT_FOUND = Theme.themedItemStack( + Material.BARRIER, + Theme.DISCOVEREY, + "Trade not found", + Theme.ERROR + "Not Discovered", + "You have not yet discovered how", + "to breed this plant!" + ); + + public TradesFlexGroup(NamespacedKey key, ItemStack item) { + super(key, item); + } + + @Override + @ParametersAreNonnullByDefault + public boolean isVisible(Player player, PlayerProfile playerProfile, SlimefunGuideMode guideMode) { + return true; + } + + @Override + @ParametersAreNonnullByDefault + public void open(Player p, PlayerProfile profile, SlimefunGuideMode mode) { + final ChestMenu chestMenu = new ChestMenu(Theme.MAIN.getColor() + "Trades Found"); + + for (int slot : HEADER) { + chestMenu.addItem(slot, ChestMenuUtils.getBackground(), (player1, i1, itemStack, clickAction) -> false); + } + + for (int slot : FOOTER) { + chestMenu.addItem(slot, ChestMenuUtils.getBackground(), (player1, i1, itemStack, clickAction) -> false); + } + + chestMenu.setEmptySlotsClickable(false); + setupPage(p, profile, mode, chestMenu, 1); + chestMenu.open(p); + } + + @ParametersAreNonnullByDefault + private void setupPage(Player player, PlayerProfile profile, SlimefunGuideMode mode, ChestMenu menu, int page) { + final List trades = new ArrayList<>(Registry.getInstance().getTrades()); + final int amount = trades.size(); + final int totalPages = (int) Math.ceil(amount / (double) PAGE_SIZE); + final int start = (page - 1) * PAGE_SIZE; + final int end = Math.min(start + PAGE_SIZE, trades.size()); + final List pairSubList = trades.subList(start, end); + + reapplyFooter(player, profile, mode, menu, page, totalPages); + + // Back + menu.replaceExistingItem( + GUIDE_BACK, + ChestMenuUtils.getBackButton( + player, + Slimefun.getLocalization().getMessage("guide.back.guide") + ) + ); + menu.addMenuClickHandler(GUIDE_BACK, (player1, slot, itemStack, clickAction) -> { + SlimefunGuide.openItemGroup(profile, Groups.MAIN, mode, 1); + return false; + }); + + for (int i = 0; i < 36; i++) { + final int slot = i + 9; + + if (i + 1 <= pairSubList.size()) { + final Trade trade = pairSubList.get(i); + final ItemStack itemStack = trade.getItem(); + final boolean found = StatisticUtils.isTradeFound(player, trade.getTradeId()); + + if (mode == SlimefunGuideMode.CHEAT_MODE || found) { + menu.replaceExistingItem(slot, itemStack.clone()); + menu.addMenuClickHandler(slot, (player1, i1, itemStack1, clickAction) -> { + displayDetail(player1, profile, mode, menu, page, trade); + return false; + }); + } else { + menu.replaceExistingItem(slot, NOT_FOUND); + menu.addMenuClickHandler(slot, ChestMenuUtils.getEmptyClickHandler()); + } + } else { + menu.replaceExistingItem(slot, null); + menu.addMenuClickHandler(slot, ChestMenuUtils.getEmptyClickHandler()); + } + } + } + + @ParametersAreNonnullByDefault + private void displayDetail(Player p, + PlayerProfile profile, + SlimefunGuideMode mode, + ChestMenu menu, + int returnPage, + Trade trade + ) { + // Back Button + menu.replaceExistingItem( + GUIDE_BACK, + ChestMenuUtils.getBackButton( + p, + Slimefun.getLocalization().getMessage("guide.back.guide") + ) + ); + menu.addMenuClickHandler(GUIDE_BACK, (player1, slot, itemStack, clickAction) -> { + setupPage(player1, profile, mode, menu, returnPage); + return false; + }); + + clearDisplay(menu); + + final ItemStack tradeItem = trade.getTradePool().getTradeItem().getItem(); + final ItemStack droppedItem = trade.getItem(); + + // Trade Item + menu.replaceExistingItem(TRADE_ITEM_SLOT, tradeItem); + menu.addMenuClickHandler(TRADE_ITEM_SLOT, ChestMenuUtils.getEmptyClickHandler()); + for (int i : TRADE_ITEM_INFO_SLOT) { + menu.replaceExistingItem(i, TRADE_ITEM_INFO); + menu.addMenuClickHandler(i, ChestMenuUtils.getEmptyClickHandler()); + } + + // Dropped Item + menu.replaceExistingItem(DROPPED_ITEM_SLOT, droppedItem); + menu.addMenuClickHandler(DROPPED_ITEM_SLOT, ChestMenuUtils.getEmptyClickHandler()); + for (int i : DROPPED_ITEM_INFO_SLOT) { + menu.replaceExistingItem(i, DROPPED_ITEM_INFO); + menu.addMenuClickHandler(i, ChestMenuUtils.getEmptyClickHandler()); + } + + // Held slots (for adding more information in the future) + for (int i : HELD_SLOTS) { + menu.replaceExistingItem(i, HELD_SLOT); + menu.addMenuClickHandler(i, ChestMenuUtils.getEmptyClickHandler()); + } + } + + @ParametersAreNonnullByDefault + private void clearDisplay(ChestMenu menu) { + for (int i = 0; i < 45; i++) { + final int slot = i + 9; + menu.replaceExistingItem(slot, null); + menu.addMenuClickHandler(slot, (player1, i1, itemStack1, clickAction) -> false); + } + } + + @ParametersAreNonnullByDefault + private void reapplyFooter(Player p, + PlayerProfile profile, + SlimefunGuideMode mode, + ChestMenu menu, + int page, + int totalPages + ) { + for (int slot : FOOTER) { + menu.replaceExistingItem(slot, ChestMenuUtils.getBackground()); + menu.addMenuClickHandler(slot, ChestMenuUtils.getEmptyClickHandler()); + } + + menu.replaceExistingItem(PAGE_PREVIOUS, ChestMenuUtils.getPreviousButton(p, page, totalPages)); + menu.addMenuClickHandler(PAGE_PREVIOUS, (player1, slot, itemStack, clickAction) -> { + final int previousPage = page - 1; + if (previousPage >= 1) { + setupPage(player1, profile, mode, menu, previousPage); + } + return false; + }); + + menu.replaceExistingItem(PAGE_NEXT, ChestMenuUtils.getNextButton(p, page, totalPages)); + menu.addMenuClickHandler(PAGE_NEXT, (player1, slot, itemStack, clickAction) -> { + final int nextPage = page + 1; + if (nextPage <= totalPages) { + setupPage(player1, profile, mode, menu, nextPage); + } + return false; + }); + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/netheos/NetheoBall.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/netheos/NetheoBall.java new file mode 100644 index 0000000..a4d75a5 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/netheos/NetheoBall.java @@ -0,0 +1,43 @@ +package dev.sefiraat.netheopoiesis.implementation.netheos; + +import dev.sefiraat.netheopoiesis.api.plant.netheos.NetheoBalls; +import dev.sefiraat.netheopoiesis.api.plant.netheos.TradePool; +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; + +public class NetheoBall extends SlimefunItem { + + @Nonnull + private final NetheoBalls parent; + @Nonnull + private final TradePool tradePool; + + @ParametersAreNonnullByDefault + public NetheoBall(ItemGroup itemGroup, + SlimefunItemStack item, + RecipeType recipeType, + ItemStack[] recipe, + NetheoBalls parent, + TradePool tradePool + ) { + super(itemGroup, item, recipeType, recipe); + this.parent = parent; + this.tradePool = tradePool; + } + + @Nonnull + public NetheoBalls getParent() { + return parent; + } + + @Nonnull + public TradePool getTradePool() { + return tradePool; + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/netheos/Paste.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/netheos/Paste.java new file mode 100644 index 0000000..5896ec6 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/netheos/Paste.java @@ -0,0 +1,80 @@ +package dev.sefiraat.netheopoiesis.implementation.netheos; + +import dev.sefiraat.netheopoiesis.api.plant.netheos.Flavour; +import dev.sefiraat.netheopoiesis.api.plant.netheos.FlavourProfile; +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.handlers.ItemConsumptionHandler; +import io.github.thebusybiscuit.slimefun4.libraries.dough.collections.RandomizedSet; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Set; +import java.util.stream.Collectors; + +public class Paste extends SlimefunItem { + + @Nonnull + private final FlavourProfile profile; + @Nonnull + private final RandomizedSet dominantFlavours; + private final int flavourVolume; + + @ParametersAreNonnullByDefault + public Paste(ItemGroup itemGroup, + SlimefunItemStack item, + RecipeType recipeType, + ItemStack[] recipe, + FlavourProfile profile + ) { + super(itemGroup, item, recipeType, recipe); + this.profile = profile; + + // Get the dominant flavour(s) present in this paste + final int highest = profile.getFlavourMap().values().stream() + .mapToInt(Integer::intValue) + .max() + .orElse(-1); + + final Set filtered = profile.getFlavourMap().keySet().stream() + .filter(key -> profile.getFlavourMap().get(key) == highest) + .collect(Collectors.toSet()); + + this.dominantFlavours = new RandomizedSet<>(filtered); + + // Get the total flavour volume in this paste + this.flavourVolume = profile.getFlavourMap().values().stream() + .mapToInt(Integer::intValue) + .sum(); + } + + @Override + public void preRegister() { + addItemHandler(new ItemConsumptionHandler() { + @Override + @ParametersAreNonnullByDefault + public void onConsume(PlayerItemConsumeEvent event, Player player, ItemStack item) { + event.setCancelled(true); + } + }); + } + + @Nonnull + public Flavour getDominantFlavour() { + return dominantFlavours.getRandom(); + } + + @Nonnull + public FlavourProfile getProfile() { + return profile; + } + + public int getFlavourVolume() { + return flavourVolume; + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/MobSpawnTask.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/MobSpawnTask.java index 3d50e09..0e42631 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/MobSpawnTask.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/MobSpawnTask.java @@ -3,14 +3,17 @@ import dev.sefiraat.netheopoiesis.Purification; import dev.sefiraat.netheopoiesis.api.mobs.MobCapType; import dev.sefiraat.netheopoiesis.api.mobs.RandomSpawn; +import dev.sefiraat.netheopoiesis.managers.MobManager; import dev.sefiraat.netheopoiesis.utils.WorldUtils; import io.github.bakedlibs.dough.collections.RandomizedSet; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; @@ -81,6 +84,27 @@ public class MobSpawnTask extends BukkitRunnable { MobSpawnTask::isSafeGround ); + private static final RandomSpawn WANDERING_PIGLIN = new RandomSpawn( + EntityType.PIGLIN, + MobCapType.PIGLIN_TRADER, + Purification.WANDERING_PIGLIN, + 0.1, + false, + MobSpawnTask::isSafeGround, + livingEntity -> { + final Location location = livingEntity.getLocation(); + final World world = location.getWorld(); + final LivingEntity strider1 = (LivingEntity) world.spawnEntity(location, EntityType.STRIDER, false); + final LivingEntity strider2 = (LivingEntity) world.spawnEntity(location, EntityType.STRIDER, false); + + strider1.setLeashHolder(livingEntity); + strider2.setLeashHolder(livingEntity); + + MobManager.getInstance().addMob(MobCapType.MISC, strider1, true); + MobManager.getInstance().addMob(MobCapType.MISC, strider2, true); + } + ); + private final RandomizedSet possibleSpawns = new RandomizedSet<>(); public MobSpawnTask() { @@ -91,6 +115,7 @@ public MobSpawnTask() { possibleSpawns.add(SQUID, 1); possibleSpawns.add(AXOLOTL, 1); possibleSpawns.add(WANDERING_TRADER, 1); + possibleSpawns.add(WANDERING_PIGLIN, 1); } @Override @@ -116,6 +141,6 @@ private static boolean isWater(@Nonnull Location location) { private static boolean isSafeGround(@Nonnull Location location) { final Block block = location.getBlock(); final Block blockBelow = block.getRelative(BlockFace.DOWN); - return blockBelow.getType().isAir() && blockBelow.getType().isSolid(); + return block.getType().isAir() && blockBelow.getType().isSolid(); } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/WanderingPiglinTask.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/WanderingPiglinTask.java new file mode 100644 index 0000000..847cf24 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/tasks/WanderingPiglinTask.java @@ -0,0 +1,72 @@ +package dev.sefiraat.netheopoiesis.implementation.tasks; + +import dev.sefiraat.netheopoiesis.api.mobs.MobCapType; +import dev.sefiraat.netheopoiesis.api.plant.netheos.Trade; +import dev.sefiraat.netheopoiesis.implementation.netheos.NetheoBall; +import dev.sefiraat.netheopoiesis.managers.MobManager; +import dev.sefiraat.netheopoiesis.utils.Keys; +import dev.sefiraat.netheopoiesis.utils.StatisticUtils; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.libraries.dough.data.persistent.PersistentDataAPI; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Piglin; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.UUID; + +public class WanderingPiglinTask extends BukkitRunnable { + + private static final long TRADE_COOLDOWN = 3000; + + @Override + public void run() { + for (UUID mobUuid : MobManager.getInstance().getMobCap(MobCapType.PIGLIN_TRADER).getMobs()) { + final Entity mob = Bukkit.getEntity(mobUuid); + if (mob == null || !mob.isValid() || !(mob instanceof Piglin piglin)) { + continue; + } + + final long cooldownTime = PersistentDataAPI.getLong(piglin, Keys.TRADE_COOLDOWN, 1); + final long timeNow = System.currentTimeMillis(); + + if (cooldownTime > timeNow) { + continue; + } + + final Location location = piglin.getLocation(); + final World world = location.getWorld(); + for (Entity entity : world.getNearbyEntities(location, 1.5, 1.5, 1.5, Item.class::isInstance)) { + final Item item = (Item) entity; + final ItemStack itemStack = item.getItemStack(); + final SlimefunItem slimefunItem = SlimefunItem.getByItem(itemStack); + if (slimefunItem instanceof NetheoBall ball) { + final ItemMeta itemMeta = itemStack.getItemMeta(); + final int flavour = PersistentDataAPI.getInt(itemMeta, Keys.FLAVOUR, 0); + final Trade trade = ball.getTradePool().getRandomTrade(flavour); + final ItemStack stackToDrop = trade.getItem(); + + final String owner = PersistentDataAPI.getString(item, Keys.DROPPED_PLAYER); + + if (owner != null) { + StatisticUtils.unlockTrade(UUID.fromString(owner), trade.getTradeId()); + } + + itemStack.setAmount(itemStack.getAmount() - 1); + world.dropItem(item.getLocation(), stackToDrop); + PersistentDataAPI.setLong( + piglin, + Keys.TRADE_COOLDOWN, + System.currentTimeMillis() + TRADE_COOLDOWN + ); + break; + } + } + } + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/implementation/tools/MixingQuartz.java b/src/main/java/dev/sefiraat/netheopoiesis/implementation/tools/MixingQuartz.java new file mode 100644 index 0000000..3bb761a --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/implementation/tools/MixingQuartz.java @@ -0,0 +1,124 @@ +package dev.sefiraat.netheopoiesis.implementation.tools; + +import dev.sefiraat.netheopoiesis.api.plant.netheos.Flavour; +import dev.sefiraat.netheopoiesis.api.plant.netheos.NetheoBalls; +import dev.sefiraat.netheopoiesis.implementation.netheos.Paste; +import dev.sefiraat.netheopoiesis.utils.Keys; +import dev.sefiraat.netheopoiesis.utils.Theme; +import io.github.thebusybiscuit.slimefun4.api.events.PlayerRightClickEvent; +import io.github.thebusybiscuit.slimefun4.api.items.ItemGroup; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItemStack; +import io.github.thebusybiscuit.slimefun4.api.recipes.RecipeType; +import io.github.thebusybiscuit.slimefun4.core.handlers.ItemUseHandler; +import io.github.thebusybiscuit.slimefun4.implementation.items.LimitedUseItem; +import io.github.thebusybiscuit.slimefun4.libraries.dough.data.persistent.PersistentDataAPI; +import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +/** + * The MixingQuartz is the manual method for converting {@link dev.sefiraat.netheopoiesis.implementation.netheos.Paste} + * into {@link NetheoBalls} + */ +public class MixingQuartz extends LimitedUseItem { + + private static final NamespacedKey KEY = Keys.newKey("uses"); + private static final Pattern LORE_FLAVOUR = Pattern.compile(Theme.CLICK_INFO + "Flavour: [0-9]+"); + + public MixingQuartz(ItemGroup group, + SlimefunItemStack item, + RecipeType recipeType, + ItemStack[] recipe, + int amount + ) { + super(group, item, recipeType, recipe); + setMaxUseCount(amount); + } + + @Nonnull + @Override + public ItemUseHandler getItemHandler() { + return this::onUse; + } + + // Todo cognitive + private void onUse(@Nonnull PlayerRightClickEvent event) { + event.cancel(); + final Player player = event.getPlayer(); + final World world = player.getWorld(); + final Location location = player.getLocation().add(player.getEyeLocation().getDirection().multiply(1.5)); + final Collection entities = world.getNearbyEntities(location, 1.5, 1.5, 1.5); + final List flavours = new ArrayList<>(); + final List validItems = new ArrayList<>(); + int volume = 0; + boolean isEnough = false; + + // Check all the entities and collate the ones that are Pastes. Escape when 3 are found + for (Entity entity : entities) { + if (entity instanceof Item item) { + final SlimefunItem slimefunItem = SlimefunItem.getByItem(item.getItemStack()); + if (slimefunItem instanceof Paste paste) { + validItems.add(item); + flavours.add(paste.getDominantFlavour()); + volume += paste.getFlavourVolume(); + if (flavours.size() == 3) { + isEnough = true; + break; + } + } + } + } + + // If three were found, lets remove them and spawn the ball + if (isEnough) { + final NetheoBalls ball = NetheoBalls.getMatchingBall(flavours); + + // No ball found, shouldn't be possible - but we have to escape + if (ball == null) { + return; + } + + // Get the itemstack then add the flavour volume to the PDC, line to the lore + final ItemStack stack = ball.getSlimefunItemStack().clone(); + final ItemMeta itemMeta = stack.getItemMeta(); + final List lore = itemMeta.getLore(); + // Set PDC + PersistentDataAPI.setInt(itemMeta, Keys.FLAVOUR, volume); + // Scan lore for the flavour line and replace + for (int i = 0; i < lore.size(); i++) { + final String string = lore.get(i); + if (LORE_FLAVOUR.matcher(string).matches()) { + lore.set(i, Theme.CLICK_INFO.asTitle("Flavour", volume)); + } + } + // Set meta and drop item + itemMeta.setLore(lore); + stack.setItemMeta(itemMeta); + world.dropItem(validItems.get(0).getLocation(), stack); + + // Loop through all the previously found items then remove one from each + for (Item item : validItems) { + item.getItemStack().setAmount(item.getItemStack().getAmount() - 1); + } + damageItem(player, event.getItem()); + } + } + + @Override + protected @Nonnull + NamespacedKey getStorageKey() { + return KEY; + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/listeners/CrushingListener.java b/src/main/java/dev/sefiraat/netheopoiesis/listeners/CrushingListener.java new file mode 100644 index 0000000..3481cc5 --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/listeners/CrushingListener.java @@ -0,0 +1,42 @@ +package dev.sefiraat.netheopoiesis.listeners; + +import dev.sefiraat.netheopoiesis.api.RecipeTypes; +import dev.sefiraat.netheopoiesis.api.interfaces.WorldCrushable; +import io.github.thebusybiscuit.slimefun4.api.items.SlimefunItem; +import org.bukkit.entity.Item; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.inventory.ItemStack; + +import javax.annotation.Nonnull; + +/** + * The purpose of this listener is to drop registered items when breaking the specified vanilla + * block. + * Recipes should be registered using {@link RecipeTypes#createCrushingRecipe(SlimefunItem)} + * which returns an ItemStack array used for Slimefun's recipe + * {@link RecipeTypes#CRUSHING} + */ +public class CrushingListener implements Listener { + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onCrush(@Nonnull EntityDamageEvent event) { + if (event.getEntity() instanceof Item item + && event.getCause() == EntityDamageEvent.DamageCause.FALLING_BLOCK + ) { + final SlimefunItem slimefunItem = SlimefunItem.getByItem(item.getItemStack()); + if (slimefunItem instanceof WorldCrushable crushable) { + final ItemStack stackToDrop = crushable.crushingDrop(); + if (stackToDrop == null) { + return; + } + for (int i = 0; i < item.getItemStack().getAmount(); i++) { + item.getWorld().dropItem(item.getLocation(), stackToDrop); + } + item.remove(); + } + } + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/listeners/DropListener.java b/src/main/java/dev/sefiraat/netheopoiesis/listeners/DropListener.java index efa50a1..4f0a8cf 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/listeners/DropListener.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/listeners/DropListener.java @@ -17,12 +17,13 @@ /** * The purpose of this listener is to drop registered items when breaking the specified vanilla * block. - * Recipes should be registered using {@link DropListener#createRecipe(ItemStack, ItemStack, double)} + * Recipes should be registered using {@link RecipeTypes#createWorldDropRecipe(ItemStack, ItemStack, double)} * which returns an ItemStack array used for Slimefun's recipe * {@link RecipeTypes#VANILLA_DROP} */ public class DropListener implements Listener { + @Nonnull private static final Map DROP_MAP = new EnumMap<>(Material.class); @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @@ -35,29 +36,6 @@ public void onBlockBreak(@Nonnull BlockBreakEvent event) { blockDrop.rollDrop(event); } - /** - * This method both registers the drop and returns an ItemStack array that can be used - * for Slimefun's recipe system. {@link RecipeTypes#VANILLA_DROP} - * - * @param stackToDrop The {@link ItemStack} to drop in the world - * @param dropFrom The {@link ItemStack} to drop from (#getType() is used) and the stack is used in the recipe. - * @param dropChance The chance (0-1) for the drop to occur - * @return A {@link ItemStack[]} used for Slimefun's Recipe registration with the dropFrom item in the middle. - */ - @Nonnull - public static ItemStack[] createRecipe(@Nonnull ItemStack stackToDrop, - @Nonnull ItemStack dropFrom, - double dropChance - ) { - final Material material = dropFrom.getType(); - DROP_MAP.put(material, new BlockDrop(stackToDrop, material, dropChance)); - return new ItemStack[]{ - null, null, null, - null, dropFrom, null, - null, null, null - }; - } - /** * This class represents a drop including its source, the item to drop and the chance for it to occur * Including a method to roll for and spawn the drop itself. @@ -96,4 +74,9 @@ public void rollDrop(@Nonnull BlockBreakEvent event) { } } } + + @Nonnull + public static Map getDropMap() { + return DROP_MAP; + } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/listeners/ManagedMobListener.java b/src/main/java/dev/sefiraat/netheopoiesis/listeners/ManagedMobListener.java index dc98094..be441eb 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/listeners/ManagedMobListener.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/listeners/ManagedMobListener.java @@ -1,12 +1,15 @@ package dev.sefiraat.netheopoiesis.listeners; import dev.sefiraat.netheopoiesis.managers.MobManager; +import dev.sefiraat.netheopoiesis.utils.Keys; +import io.github.thebusybiscuit.slimefun4.libraries.dough.data.persistent.PersistentDataAPI; import org.bukkit.Material; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.inventory.ItemStack; diff --git a/src/main/java/dev/sefiraat/netheopoiesis/listeners/WanderingPiglinListener.java b/src/main/java/dev/sefiraat/netheopoiesis/listeners/WanderingPiglinListener.java new file mode 100644 index 0000000..7814c9c --- /dev/null +++ b/src/main/java/dev/sefiraat/netheopoiesis/listeners/WanderingPiglinListener.java @@ -0,0 +1,34 @@ +package dev.sefiraat.netheopoiesis.listeners; + +import dev.sefiraat.netheopoiesis.api.mobs.MobCapType; +import dev.sefiraat.netheopoiesis.managers.MobManager; +import dev.sefiraat.netheopoiesis.utils.Keys; +import io.github.thebusybiscuit.slimefun4.libraries.dough.data.persistent.PersistentDataAPI; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityPickupItemEvent; +import org.bukkit.event.player.PlayerDropItemEvent; + +import javax.annotation.Nonnull; +import java.util.UUID; + +public class WanderingPiglinListener implements Listener { + + + @EventHandler + public void onPiglinPicksUpItem(@Nonnull EntityPickupItemEvent event) { + final UUID piglinUuid = event.getEntity().getUniqueId(); + if (MobManager.getInstance().getMobCap(MobCapType.PIGLIN_TRADER).contains(piglinUuid)) { + event.setCancelled(true); + } + } + + @EventHandler + public void onPlayerDropItem(@Nonnull PlayerDropItemEvent event) { + PersistentDataAPI.setString( + event.getItemDrop(), + Keys.DROPPED_PLAYER, + event.getPlayer().getUniqueId().toString() + ); + } +} diff --git a/src/main/java/dev/sefiraat/netheopoiesis/managers/ConfigManager.java b/src/main/java/dev/sefiraat/netheopoiesis/managers/ConfigManager.java index 435e154..22e1575 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/managers/ConfigManager.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/managers/ConfigManager.java @@ -1,12 +1,17 @@ package dev.sefiraat.netheopoiesis.managers; import dev.sefiraat.netheopoiesis.Netheopoiesis; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNonnullByDefault; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; /** * This class is used to create and manage/save custom configuration files @@ -17,9 +22,48 @@ public class ConfigManager { private final FileConfiguration discoveries; public ConfigManager() { + setupDefaultConfig(); this.discoveries = getConfig("discoveries.yml"); } + private void setupDefaultConfig() { + final Netheopoiesis plugin = Netheopoiesis.getInstance(); + final InputStream inputStream = plugin.getResource("config.yml"); + final File existingFile = new File(plugin.getDataFolder(), "config.yml"); + + if (inputStream == null) { + // Not sure how? Regardless cannot copy over new keys + return; + } + + final Reader reader = new InputStreamReader(inputStream); + final FileConfiguration resourceConfig = YamlConfiguration.loadConfiguration(reader); + final FileConfiguration existingConfig = YamlConfiguration.loadConfiguration(existingFile); + + for (String key : resourceConfig.getKeys(false)) { + checkKey(existingConfig, resourceConfig, key); + } + + try { + existingConfig.save(existingFile); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @ParametersAreNonnullByDefault + private void checkKey(FileConfiguration existingConfig, FileConfiguration resourceConfig, String key) { + final Object currentValue = existingConfig.get(key); + final Object newValue = resourceConfig.get(key); + if (newValue instanceof ConfigurationSection section) { + for (String sectionKey : section.getKeys(false)) { + checkKey(existingConfig, resourceConfig, key + "." + sectionKey); + } + } else if (currentValue == null) { + existingConfig.set(key, newValue); + } + } + @Nonnull @SuppressWarnings("ResultOfMethodCallIgnored") private FileConfiguration getConfig(@Nonnull String fileName) { @@ -54,4 +98,48 @@ private void saveDiscoveries() { public FileConfiguration getDiscoveries() { return discoveries; } + + public boolean isAutoUpdate() { + return Netheopoiesis.getInstance().getConfig().getBoolean("auto-update"); + } + + public boolean isDebugMessages() { + return Netheopoiesis.getInstance().getConfig().getBoolean("debug-messages"); + } + + public int getPlayerMobCapWaterAmbient() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-water-ambient"); + } + + public int getPlayerMobCapWaterAnimal() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-water-animal"); + } + + public int getPlayerMobCapWaterHostile() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-water-hostile"); + } + + public int getPlayerMobCapLandAmbient() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-land-ambient"); + } + + public int getPlayerMobCapLandAnimal() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-land-animal"); + } + + public int getPlayerMobCapLandHostile() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-land-hostile"); + } + + public int getPlayerMobCapVillager() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-villager"); + } + + public int getPlayerMobCapPiglinTrader() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-piglin-trader"); + } + + public int getPlayerMobCapWanderingTrader() { + return Netheopoiesis.getInstance().getConfig().getInt("spawning.player-cap-wandering-trader"); + } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/managers/ListenerManager.java b/src/main/java/dev/sefiraat/netheopoiesis/managers/ListenerManager.java index 6928f3f..12180ff 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/managers/ListenerManager.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/managers/ListenerManager.java @@ -3,6 +3,7 @@ import com.google.common.base.Preconditions; import dev.sefiraat.netheopoiesis.Netheopoiesis; import dev.sefiraat.netheopoiesis.listeners.BlockProtectionListener; +import dev.sefiraat.netheopoiesis.listeners.CrushingListener; import dev.sefiraat.netheopoiesis.listeners.CrystallineSeedListener; import dev.sefiraat.netheopoiesis.listeners.DropListener; import dev.sefiraat.netheopoiesis.listeners.FriendlyMobsListener; @@ -10,6 +11,7 @@ import dev.sefiraat.netheopoiesis.listeners.MobSpawnListener; import dev.sefiraat.netheopoiesis.listeners.PlayerSleepListener; import dev.sefiraat.netheopoiesis.listeners.SeedPlacementListener; +import dev.sefiraat.netheopoiesis.listeners.WanderingPiglinListener; import dev.sefiraat.netheopoiesis.listeners.WaterPlaceListener; import org.bukkit.event.Listener; @@ -34,6 +36,8 @@ public ListenerManager() { addListener(new BlockProtectionListener()); addListener(new CrystallineSeedListener()); addListener(new ManagedMobListener()); + addListener(new CrushingListener()); + addListener(new WanderingPiglinListener()); } private void addListener(@Nonnull Listener listener) { diff --git a/src/main/java/dev/sefiraat/netheopoiesis/managers/MobManager.java b/src/main/java/dev/sefiraat/netheopoiesis/managers/MobManager.java index 419d57d..d50f8de 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/managers/MobManager.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/managers/MobManager.java @@ -45,6 +45,7 @@ public MobManager() { mobCaps.put(MobCapType.VILLAGER, MobCap.VILLAGER); mobCaps.put(MobCapType.PIGLIN_TRADER, MobCap.PIGLIN_TRADER); mobCaps.put(MobCapType.WANDERING_TRADER, MobCap.WANDERING_TRADER); + mobCaps.put(MobCapType.MISC, MobCap.MISC); new MobRemovalTask().runTaskTimer(Netheopoiesis.getInstance(), 1200, 1200); } @@ -170,6 +171,17 @@ public void addMob(@Nonnull MobCapType type, @Nonnull LivingEntity livingEntity) addMob(type, livingEntity.getUniqueId()); } + /** + * Adds a specific mob manually to the specified mob cap. Should be used only if spawnMob() isn't suitable + * + * @param type The {@link MobCapType} to add the mob to + * @param livingEntity The {@link LivingEntity} being added + * @param bypassCap True if the mob should be added regardless of a cap + */ + public void addMob(@Nonnull MobCapType type, @Nonnull LivingEntity livingEntity, boolean bypassCap) { + addMob(type, livingEntity.getUniqueId(), bypassCap); + } + /** * Adds a specific mob manually to the specified mob cap. Should be used only if spawnMob() isn't suitable * @@ -177,7 +189,18 @@ public void addMob(@Nonnull MobCapType type, @Nonnull LivingEntity livingEntity) * @param mobUuid The {@link UUID} of the mob being added */ public void addMob(@Nonnull MobCapType type, @Nonnull UUID mobUuid) { - if (mobCapHasSpace(type)) { + addMob(type, mobUuid, false); + } + + /** + * Adds a specific mob manually to the specified mob cap. Should be used only if spawnMob() isn't suitable + * + * @param type The {@link MobCapType} to add the mob to + * @param mobUuid The {@link UUID} of the mob being added + * @param bypassCap True if the mob should be added regardless of a cap + */ + public void addMob(@Nonnull MobCapType type, @Nonnull UUID mobUuid, boolean bypassCap) { + if (mobCapHasSpace(type) || bypassCap) { final MobCap mobCap = mobCaps.get(type); mobCap.addMob(mobUuid); mobCaps.put(type, mobCap); diff --git a/src/main/java/dev/sefiraat/netheopoiesis/managers/RunnableManager.java b/src/main/java/dev/sefiraat/netheopoiesis/managers/TaskManager.java similarity index 78% rename from src/main/java/dev/sefiraat/netheopoiesis/managers/RunnableManager.java rename to src/main/java/dev/sefiraat/netheopoiesis/managers/TaskManager.java index 7dc5296..00f92e9 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/managers/RunnableManager.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/managers/TaskManager.java @@ -5,19 +5,21 @@ import dev.sefiraat.netheopoiesis.implementation.tasks.MobSpawnTask; import dev.sefiraat.netheopoiesis.implementation.tasks.PurificationEffectsTask; import dev.sefiraat.netheopoiesis.implementation.tasks.SaveConfigTask; +import dev.sefiraat.netheopoiesis.implementation.tasks.WanderingPiglinTask; /** * This class is used to run Runnables from one place */ -public class RunnableManager { +public class TaskManager { - private static RunnableManager instance; + private static TaskManager instance; private final PurificationEffectsTask regenerationRunnable; private final MobSpawnTask spawnsRunnable; private final SaveConfigTask saveConfigRunnable; + private final WanderingPiglinTask wanderingPiglinTask; - public RunnableManager() { + public TaskManager() { Preconditions.checkArgument(instance == null, "Cannot create a new instance of the ListenerManager"); instance = this; final Netheopoiesis plugin = Netheopoiesis.getInstance(); @@ -30,6 +32,9 @@ public RunnableManager() { saveConfigRunnable = new SaveConfigTask(); saveConfigRunnable.runTaskTimer(plugin, 0, 12000); + + wanderingPiglinTask = new WanderingPiglinTask(); + wanderingPiglinTask.runTaskTimer(plugin, 0, 20); } public PurificationEffectsTask getRegenerationRunnable() { @@ -44,7 +49,7 @@ public SaveConfigTask getSaveConfigRunnable() { return saveConfigRunnable; } - public static RunnableManager getInstance() { + public static TaskManager getInstance() { return instance; } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/utils/Keys.java b/src/main/java/dev/sefiraat/netheopoiesis/utils/Keys.java index 0aaa7aa..3f5b2fd 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/utils/Keys.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/utils/Keys.java @@ -13,6 +13,9 @@ public final class Keys { public static final String CRYSTALLINE_STEPS_REMAINING = "steps-left"; public static final NamespacedKey COOLDOWN = newKey("cooldown"); + public static final NamespacedKey TRADE_COOLDOWN = newKey("trade-cooldown"); + public static final NamespacedKey DROPPED_PLAYER = newKey("dropped-player"); + public static final NamespacedKey FLAVOUR = newKey("flavour"); private Keys() { throw new IllegalStateException("Utility class"); diff --git a/src/main/java/dev/sefiraat/netheopoiesis/utils/StatisticUtils.java b/src/main/java/dev/sefiraat/netheopoiesis/utils/StatisticUtils.java index 0aa055e..e80fad4 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/utils/StatisticUtils.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/utils/StatisticUtils.java @@ -3,7 +3,7 @@ import dev.sefiraat.netheopoiesis.Netheopoiesis; import org.bukkit.entity.Player; -import javax.annotation.ParametersAreNonnullByDefault; +import javax.annotation.Nonnull; import java.text.MessageFormat; import java.util.UUID; @@ -14,28 +14,42 @@ private StatisticUtils() { } private static final MessageFormat BREED_UNLOCK = new MessageFormat("{0}.BREEDING.{1}.UNLOCKED"); + private static final MessageFormat TRADE_UNLOCK = new MessageFormat("{0}.TRADING.{1}.UNLOCKED"); - @ParametersAreNonnullByDefault - public static void unlockDiscovery(Player player, String itemId) { + public static void unlockDiscovery(@Nonnull Player player, @Nonnull String itemId) { unlockDiscovery(player.getUniqueId(), itemId); } - @ParametersAreNonnullByDefault - public static void unlockDiscovery(UUID player, String itemId) { - Netheopoiesis.getConfigManager().getDiscoveries().set( - BREED_UNLOCK.format(new Object[]{player, itemId}), true); + public static void unlockDiscovery(@Nonnull UUID player, @Nonnull String itemId) { + Netheopoiesis.getConfigManager().getDiscoveries().set(BREED_UNLOCK.format(new Object[]{player, itemId}), true); } - @ParametersAreNonnullByDefault - public static boolean isDiscovered(Player player, String itemId) { + public static boolean isDiscovered(@Nonnull Player player, @Nonnull String itemId) { return isDiscovered(player.getUniqueId(), itemId); } - @ParametersAreNonnullByDefault - public static boolean isDiscovered(UUID player, String itemId) { + public static boolean isDiscovered(@Nonnull UUID player, @Nonnull String itemId) { return Netheopoiesis.getConfigManager().getDiscoveries().getBoolean( BREED_UNLOCK.format(new Object[]{player, itemId}) ); } + + public static void unlockTrade(@Nonnull Player player, @Nonnull String itemId) { + unlockTrade(player.getUniqueId(), itemId); + } + + public static void unlockTrade(@Nonnull UUID player, @Nonnull String itemId) { + Netheopoiesis.getConfigManager().getDiscoveries().set(TRADE_UNLOCK.format(new Object[]{player, itemId}), true); + } + + public static boolean isTradeFound(@Nonnull Player player, @Nonnull String tradeId) { + return isTradeFound(player.getUniqueId(), tradeId); + } + + public static boolean isTradeFound(@Nonnull UUID player, @Nonnull String tradeId) { + return Netheopoiesis.getConfigManager().getDiscoveries().getBoolean( + TRADE_UNLOCK.format(new Object[]{player, tradeId}) + ); + } } diff --git a/src/main/java/dev/sefiraat/netheopoiesis/utils/Theme.java b/src/main/java/dev/sefiraat/netheopoiesis/utils/Theme.java index aef488d..fd97531 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/utils/Theme.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/utils/Theme.java @@ -35,6 +35,8 @@ public enum Theme { TOOL(ChatColor.of("#3295a8"), "工具"), PURIFYING_SEED(ChatColor.of("#cde06c"), "净化之种"), SEED(ChatColor.of("#a241bf"), "种子"), + PASTE(ChatColor.of("#999343"), "Seed Powder"), + NETHEO_BALL(ChatColor.of("#6384b8"), "Netheo Ball"), CRUX(ChatColor.of("#4287f5"), "结构"), // Seeds SEED_RED(ChatColor.of("#c41d1d")), diff --git a/src/main/java/dev/sefiraat/netheopoiesis/utils/WorldUtils.java b/src/main/java/dev/sefiraat/netheopoiesis/utils/WorldUtils.java index 5e32e4b..5202edf 100644 --- a/src/main/java/dev/sefiraat/netheopoiesis/utils/WorldUtils.java +++ b/src/main/java/dev/sefiraat/netheopoiesis/utils/WorldUtils.java @@ -89,9 +89,9 @@ public static Location randomLocation(@Nonnull Location centreLocation, int rang * Picks a random location within a given range around a point * * @param centreLocation The {@link Location} which acts as the centre of the random check - * @param rangeX The range in blocks in which to spread out from on the X axis - * @param rangeY The range in blocks in which to spread out from on the Y axis - * @param rangeZ The range in blocks in which to spread out from on the Z axis + * @param rangeX The range in blocks in which to spread out from on the X axis + * @param rangeY The range in blocks in which to spread out from on the Y axis + * @param rangeZ The range in blocks in which to spread out from on the Z axis * @return The {@link Location} randomly selected */ @Nonnull diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index f6e6442..cbfa8ac 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,2 +1,16 @@ auto-update: true -debug-messages: false \ No newline at end of file +debug-messages: false +spawning: + player-cap-water-ambient: 15 + player-cap-water-animal: 5 + player-cap-water-hostile: 10 + player-cap-land-ambient: 5 + player-cap-land-animal: 10 + player-cap-land-hostile: 10 + player-cap-villager: 5 + player-cap-piglin-trader: 1 + player-cap-wandering-trader: 1 +speed: + speed-multiplier-crux-spread: 1 + speed-crystalline-crux-spread: 1 + speed-plant-growth-rate: 1