From 075c9bcc78ae0fe0c6dbe3bb73e90d9601cc4f83 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 19 Jan 2019 07:52:04 -0800 Subject: [PATCH] WIP commit --- README.md | 114 +- pom.xml | 205 +++ .../bentobox/greenhouses/Greenhouses.java | 1590 +++++++++++++++++ .../bentobox/greenhouses/PlayerCache.java | 193 ++ .../world/bentobox/greenhouses/Players.java | 70 + .../world/bentobox/greenhouses/Settings.java | 25 + .../greenhouses/greenhouse/BiomeRecipe.java | 434 +++++ .../greenhouses/greenhouse/Ecosystem.java | 144 ++ .../greenhouses/greenhouse/Greenhouse.java | 606 +++++++ .../GreenhouseBlockConversions.java | 42 + .../greenhouses/greenhouse/GreenhouseMob.java | 31 + .../greenhouse/GreenhousePlant.java | 32 + .../bentobox/greenhouses/greenhouse/Roof.java | 234 +++ .../greenhouses/greenhouse/Walls.java | 274 +++ .../listeners/GreenhouseEvents.java | 319 ++++ .../listeners/GreenhouseGuard.java | 57 + .../listeners/JoinLeaveEvents.java | 54 + .../bentobox/greenhouses/ui/ControlPanel.java | 195 ++ .../greenhouses/ui/GreenhouseCmd.java | 248 +++ .../world/bentobox/greenhouses/ui/Locale.java | 90 + .../greenhouses/ui/admin/AdminCmd.java | 110 ++ .../ui/admin/GreenhousesAdminInfoCommand.java | 45 + .../admin/GreenhousesAdminReloadCommand.java | 44 + .../greenhouses/ui/user/GhCommand.java | 62 + .../greenhouses/ui/user/InfoCommand.java | 45 + .../greenhouses/ui/user/ListCommand.java | 62 + .../greenhouses/ui/user/MakeCommand.java | 105 ++ .../greenhouses/ui/user/RecipeCommand.java | 95 + .../greenhouses/ui/user/RemoveCommand.java | 61 + src/main/resources/addon.yml | 22 + src/main/resources/biomes.yml | 198 ++ src/main/resources/config.yml | 52 + 32 files changed, 5856 insertions(+), 2 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/world/bentobox/greenhouses/Greenhouses.java create mode 100644 src/main/java/world/bentobox/greenhouses/PlayerCache.java create mode 100644 src/main/java/world/bentobox/greenhouses/Players.java create mode 100644 src/main/java/world/bentobox/greenhouses/Settings.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/Ecosystem.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/Greenhouse.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseBlockConversions.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseMob.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/GreenhousePlant.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/Roof.java create mode 100644 src/main/java/world/bentobox/greenhouses/greenhouse/Walls.java create mode 100644 src/main/java/world/bentobox/greenhouses/listeners/GreenhouseEvents.java create mode 100644 src/main/java/world/bentobox/greenhouses/listeners/GreenhouseGuard.java create mode 100644 src/main/java/world/bentobox/greenhouses/listeners/JoinLeaveEvents.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/ControlPanel.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/GreenhouseCmd.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/Locale.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/admin/AdminCmd.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminInfoCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminReloadCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/user/GhCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/user/InfoCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/user/ListCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/user/MakeCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/user/RecipeCommand.java create mode 100644 src/main/java/world/bentobox/greenhouses/ui/user/RemoveCommand.java create mode 100755 src/main/resources/addon.yml create mode 100644 src/main/resources/biomes.yml create mode 100644 src/main/resources/config.yml diff --git a/README.md b/README.md index b14a9c3..56db548 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,112 @@ -# Greenhouses -BentoBox Add-on to enable personal biomes in a glass greenhouse +# Greenhouses - an add-on for BentoBox + + +Greenhouses is a BentoBox add-on to power-up your island world! It enables players to build their own biome greenhouses complete with weather, friendly mob spawning, unique plant growth, and even block erosion! + +Greenhouses are made out of glass and must contain the blocks found in the Biome Recipe to be valid. There is a recipe GUI. Once built, the greenhouse can be used to grow plants with bonemeal, and it may spawn biome-specific mobs. If you include a hopper with water in it, snow will form inside the greenhouse when it rains. If you put bonemeal in the hopper, biome-specific plants will grow. Some blocks can also transform over time due to "erosion". + +## Features + +* Craft your own self-contained biome greenhouse on an island (or elsewhere if you like) +* Greenhouses can grow plants that cannot normally be grown, like sunflowers +* Friendly mobs can spawn if your greenhouse is well designed - need slimes? Build a swamp greenhouse! +* Blocks change in biomes - dirt becomes sand in a desert, dirt becomes clay in a river, for example. +* Greenhouses can run in multiple worlds. +* Easy to use GUI shows greenhouse recipes (/g) +* Admins can fully customize biomes and recipes + +## How to Build A Greenhouse (Simple version) + +1. Make glass blocks and build a rectangular set of walls with a flat roof. +2. Put a hopper in the wall or roof. +3. Put a door in the wall so you can get in and out. +4. Type /g and read the rules for the greenhouse you want. +5. Exit the GUI and place blocks, water, lava, and ice so that you make your desired biome. +6. Type /g again and click on the biome to make it. + +### Once made: + +* Use bonemeal to grow small plants on grass blocks immediately in the greenhouse. +* Or place bonemeal in the hopper to have the greenhouse sprinkle bonemeal automatically. Come back later to see what grows! +* Place a bucket of water (or more) in the hopper to cause snow to fall in cold biomes. Snow will fall when it rains in the world. Each snowfall empties one bucket of water. +* Friendly biome-specific mobs may spawn in your greenhouse - the usual rules apply (be more than 24 blocks away). + +## FAQ + +* Can I use stained glass? Yes, you can. It's pretty. +* Can I fill my greenhouse full of water? Yes. That's an ocean. +* Will a squid spawn there? Maybe... okay, yes it will if it's a big enough ocean. +* How do I place a door high up in the wall if the wall is all glass? Place it on a hopper. +* How do I place a door on a hopper? Crouch and then place it. +* Can I use metal doors? Yes. +* Can I use a trap door? Yes. +* Can I grow swamp flowers with this? Yes. Make a swamp biome and use bonemeal. +* How much bonemeal is used to grow plants? One per successful plant. +* How much water do I need to put into the hopper to make it snow? One bucket of water (just the water) is used up every time it rains. This only happens in cold biomes. +* Can I build a Nether greenhouse? Try it and see... (Actually, you may need permission) +* Can I build greenhouses in the Nether? Yes. You can colonize the nether with them. +* What kind of mobs spawn in the biomes? It's what you would expect, wolves in Cold Taiga, horses on plains, etc. + + +## Required Plugin + +This version of Greenhouses is an add-on for BentoBox and will not run stand-alone! + +1. BentoBox - make sure you use the latest version! + +## Installation and Configuration + +1. Download and install BentoBox if you haven't done so already +2. Download the add-on +3. Place into the BentoBox addon's folder +4. Restart your server +5. The addon will make a data folder called greenhouses. Open that folder. +6. Check **config.yml** and edit to be what you want, note the list of world names. +7. Configure the **biomes.yml** if you wish (advanced). +8. Type **/gadmin reload** in the game to reload the config or restart the server. +9. Done! + +To make your first greenhouse, build a glass box and type **/g make** to see what kind of greenhouse you get. Type **/g** to see the recipes. + +## Upgrading + +Read the file release notes for changes and instructions on how to upgrade. + +## Player Commands + +* **/greenhouse** or **/g** can be used for short. +* **/greenhouse help** - lists these commands +* **/greenhouse make**: Tries to make a greenhouse by finding the first valid recipe +* **/greenhouse remove**: Removes a greenhouse that you are standing in if you are the owner +* **/greenhouse list**: Lists all the recipes available +* **/greenhouse recipe**: Displays the recipe GUI - clicking on a recipe will try to make a greenhouse + +## Admin Commands + +* **/gadmin reload** : Reloads config files +* **/gadmin info **: provides info on the player +* **/gadmin info**: provides info on the greenhouse you are in + +## Permissions + +Permission to use specific biomes can be added in biomes.yml. + +For example, the permission for the Nether (Hell) biome is **greenhouses.biome.nether** and is set here: + + HELL: + + permission: greenhouses.biome.nether + +The permission can be anything you like, e.g., a rank permission, **myserver.VIP**. + +### General permissions are: + + greenhouses.player: + + description: Gives access to player commands + default: true + + greenhouses.admin: + + description: Gives access to admin commands + default: op \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..8e5ed8f --- /dev/null +++ b/pom.xml @@ -0,0 +1,205 @@ + + + 4.0.0 + + world.bentobox + Greenhouses + 0.0.1-SNAPSHOT + + Greenhouses + Greenhouses is an add-on for BentoBox, an expandable Minecraft Bukkit plugin for island-type games like ASkyBlock or AcidIsland. + https://github.com/BentoBoxWorld/Greenhouses + 2019 + + + scm:git:https://github.com/BentoBoxWorld/Greenhouses.git + scm:git:git@github.com:BentoBoxWorld/Greenhouses.git + https://github.com/BentoBoxWorld/Greenhouses + + + + jenkins + http://ci.codemc.org/job/BentoBoxWorld/job/Greenhouses + + + + GitHub + https://github.com/BentoBoxWorld/Greenhouses/issues + + + + + codemc-snapshots + https://repo.codemc.org/repository/maven-snapshots + + + codemc-releases + https://repo.codemc.org/repository/maven-releases + + + + + UTF-8 + UTF-8 + 1.8 + 1.7.4 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots + + + + + + org.spigotmc + spigot-api + 1.13.2-R0.1-SNAPSHOT + provided + + + org.mockito + mockito-all + 1.10.19 + test + + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito + ${powermock.version} + test + + + world.bentobox + bentobox + 1.1-SNAPSHOT + + + + + clean package + + + src/main/resources + true + + + src/main/resources/locales + ./locales + false + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-resources-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.7.0 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.0 + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.jacoco + jacoco-maven-plugin + 0.8.1 + + true + + + + pre-unit-test + + prepare-agent + + + + post-unit-test + + report + + + + + + + + + + sonar + + https://sonarcloud.io + bentobox-world + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + 5.1 + + + verify + + sonar + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/world/bentobox/greenhouses/Greenhouses.java b/src/main/java/world/bentobox/greenhouses/Greenhouses.java new file mode 100644 index 0000000..5292f8a --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/Greenhouses.java @@ -0,0 +1,1590 @@ +package world.bentobox.greenhouses; + +import java.awt.geom.Rectangle2D; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitTask; +import org.bukkit.util.Vector; + +import world.bentobox.bentobox.api.addons.Addon; +import world.bentobox.greenhouses.greenhouse.BiomeRecipe; +import world.bentobox.greenhouses.greenhouse.Ecosystem; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.greenhouse.Roof; +import world.bentobox.greenhouses.greenhouse.Walls; +import world.bentobox.greenhouses.listeners.GreenhouseEvents; +import world.bentobox.greenhouses.listeners.GreenhouseGuard; +import world.bentobox.greenhouses.listeners.JoinLeaveEvents; +import world.bentobox.greenhouses.ui.ControlPanel; +import world.bentobox.greenhouses.ui.GreenhouseCmd; +import world.bentobox.greenhouses.ui.Locale; +import world.bentobox.greenhouses.ui.admin.AdminCmd; +import world.bentobox.greenhouses.util.MetricsLite; +import world.bentobox.greenhouses.util.Util; +import world.bentobox.greenhouses.util.VaultHelper; + +/** + * This plugin simulates greenhouses in Minecraft. It enables players to build biomes inside + * glass houses. Each biome is different and can spawn plants and animals. The recipe for each + * biome is determined by a configuration file.b + * @author tastybento + */ +public class Greenhouses extends Addon { + // Maximum size that the Minecraft inventory can be in items before going weird + private static final int MAXIMUM_INVENTORY_SIZE = 49; + + // Players object + public PlayerCache players; + + // Greenhouses + private HashSet greenhouses = new HashSet(); + private HashMap> playerhouses = new HashMap>(); + + // Offline Messages + private HashMap> messages = new HashMap>(); + private YamlConfiguration messageStore; + + // Ecosystem object and random number generator + private Ecosystem eco = new Ecosystem(this); + // Tasks + private BukkitTask plantTask; + private BukkitTask mobTask; + private BukkitTask blockTask; + private BukkitTask ecoTask; + + // Biomes + private List biomeRecipes = new ArrayList(); + private ControlPanel biomeInv; + // Debug level (0 = none, 1 = important ones, 2 = level 2, 3 = level 3 + private List debug = new ArrayList(); + + /** + * Loads all the biome recipes from the file biomes.yml. + */ + public void loadBiomeRecipes() { + biomeRecipes.clear(); + YamlConfiguration biomes = loadYamlFile("biomes.yml"); + ConfigurationSection biomeSection = biomes.getConfigurationSection("biomes"); + if (biomeSection == null) { + getLogger().severe("biomes.yml file is missing, empty or corrupted. Delete and reload plugin again!"); + return; + } + + // Loop through all the entries + for (String type: biomeSection.getValues(false).keySet()) { + logger(1,"Loading "+type + " biome recipe:"); + try { + ConfigurationSection biomeRecipe = biomeSection.getConfigurationSection(type); + Biome thisBiome = null; + if (biomeRecipe.contains("biome")) { + // Try and get the biome via the biome setting + thisBiome = Biome.valueOf(biomeRecipe.getString("biome").toUpperCase()); + } else { + // Old style, where type was the biome name + thisBiome = Biome.valueOf(type); + } + if (thisBiome != null) { + int priority = biomeRecipe.getInt("priority", 0); + BiomeRecipe b = new BiomeRecipe(this, thisBiome,priority); + // Set the name + b.setName(type); + // Set the permission + b.setPermission(biomeRecipe.getString("permission","")); + // Set the icon + b.setIcon(Material.valueOf(biomeRecipe.getString("icon", "SAPLING"))); + b.setFriendlyName(ChatColor.translateAlternateColorCodes('&', biomeRecipe.getString("friendlyname", ""))); + // A value of zero on these means that there must be NO coverage, e.g., desert. If the value is not present, then the default is -1 + b.setWatercoverage(biomeRecipe.getInt("watercoverage",-1)); + b.setLavacoverage(biomeRecipe.getInt("lavacoverage",-1)); + b.setIcecoverage(biomeRecipe.getInt("icecoverage",-1)); + b.setMobLimit(biomeRecipe.getInt("moblimit", 9)); + // Set the needed blocks + String contents = biomeRecipe.getString("contents", ""); + logger(3,"contents = '" + contents + "'"); + if (!contents.isEmpty()) { + String[] split = contents.split(" "); + // Format is MATERIAL: Qty or MATERIAL: Type:Quantity + for (String s : split) { + // Split it again + String[] subSplit = s.split(":"); + if (subSplit.length > 1) { + Material blockMaterial = Material.valueOf(subSplit[0]); + // TODO: Need to parse these inputs better. INTS and Strings + //logger(3,"subsplit = " + subSplit); + //logger(3,"subsplit length = " + subSplit.length); + int blockType = 0; + int blockQty = 0; + if (subSplit.length == 2) { + blockQty = Integer.valueOf(subSplit[1]); + blockType = -1; // anything okay + } else if (subSplit.length == 3) { + //logger(3,"subsplit[1] = " + subSplit[1]); + //logger(3,"subsplit[2] = " + subSplit[2]); + //logger(3,"subsplit[1] value = " + Integer.valueOf(subSplit[1])); + //logger(3,"subsplit[2] value = " + Integer.valueOf(subSplit[2])); + blockType = Integer.valueOf(subSplit[1]); + blockQty = Integer.valueOf(subSplit[2]); + } + b.addReqBlocks(blockMaterial, blockType, blockQty); + } else { + getLogger().warning("Block material " + s + " has no associated qty in biomes.yml " + type); + } + } + } + + // Load plants + // # Plant Material: Probability in %:Block Material on what they grow:Plant Type(optional):Block Type(Optional) + ConfigurationSection temp = biomes.getConfigurationSection("biomes." + type + ".plants"); + if (temp != null) { + HashMap plants = (HashMap)temp.getValues(false); + if (plants != null) { + for (String s: plants.keySet()) { + //logger(1, "Plant = " + s); + Material plantMaterial = null; + int plantType = 0; + if (s.contains(":")) { + String[] split = s.split(":"); + if (split.length == 2) { + plantMaterial = Material.valueOf(split[0]); + plantType = Integer.valueOf(split[1]); + } + } else { + plantMaterial = Material.valueOf(s); + } + //logger(1, "Plant = " + plantMaterial); + String[] split = ((String)plants.get(s)).split(":"); + //logger(1, "Split length = " + split.length); + int plantProbability = Integer.valueOf(split[0]); + Material plantGrowOn = Material.valueOf(split[1]); + if (split.length == 3) { + //logger(1, "Split legth is ==3"); + plantType = Integer.valueOf(split[2]); + //logger(1, "plant type = " + plantType); + } + b.addPlants(plantMaterial, plantType, plantProbability, plantGrowOn); + } + } + } + // Load mobs! + // Mob EntityType: Probability:Spawn on Material + temp = biomes.getConfigurationSection("biomes." + type + ".mobs"); + if (temp != null) { + HashMap mobs = (HashMap)temp.getValues(false); + if (mobs != null) { + for (String s: mobs.keySet()) { + EntityType mobType = EntityType.valueOf(s); + String[] split = ((String)mobs.get(s)).split(":"); + int mobProbability = Integer.valueOf(split[0]); + Material mobSpawnOn = Material.valueOf(split[1]); + // TODO: Currently not used + int mobSpawnOnType = 0; + if (split.length == 3) { + mobSpawnOnType = Integer.valueOf(split[2]); + } + b.addMobs(mobType, mobProbability, mobSpawnOn); + } + } + } + // Load block conversions + String conversions = biomeSection.getString(type + ".conversions", ""); + logger(3,"conversions = '" + conversions + "'"); + if (!conversions.isEmpty()) { + String[] split = conversions.split(" "); + for (String s : split) { + // Split it again + String[] subSplit = s.split(":"); + // After this is split, there must be 5 entries! + Material oldMaterial = null; + int oldType = 0; + Material newMaterial = null; + int newType = 0; + Material localMaterial = null; + int localType = 0; + int convChance; + oldMaterial = Material.valueOf(subSplit[0]); + oldType = Integer.valueOf(subSplit[1]); + convChance = Integer.valueOf(subSplit[2]); + newMaterial = Material.valueOf(subSplit[3]); + newType = Integer.valueOf(subSplit[4]); + if (subSplit.length == 7) { + localMaterial = Material.valueOf(subSplit[5]); + localType = Integer.valueOf(subSplit[6]); + } + b.addConvBlocks(oldMaterial, oldType, newMaterial, newType, convChance, localMaterial, localType); + } + } + // Add the recipe to the list + biomeRecipes.add(b); + } + } catch (Exception e) { + logger(1,"Problem loading biome recipe - skipping!"); + String validBiomes = ""; + for (Biome biome : Biome.values()) { + validBiomes = validBiomes + " " + biome.name(); + } + logger(1,"Valid biomes are " + validBiomes); + e.printStackTrace(); + } + + // Check maximum number + if (biomeRecipes.size() == MAXIMUM_INVENTORY_SIZE) { + getLogger().warning("Cannot load any more biome recipies - limit is 49!"); + break; + } + + } + logger(1,"Loaded " + biomeRecipes.size() + " biome recipes."); + } + + + /** + * @return the biomeRecipes + */ + public List getBiomeRecipes() { + return biomeRecipes; + } + + + /** + * Loads the various settings from the config.yml file into the plugin + */ + public void loadPluginConfig() { + try { + getConfig(); + } catch (final Exception e) { + e.printStackTrace(); + } + // Get the localization strings + getLocale(); + Locale.generalnotavailable = ChatColor.translateAlternateColorCodes('&', getLocale().getString("general.notavailable", "Greenhouses are not available in this world")); + Locale.generalgreenhouses = ChatColor.translateAlternateColorCodes('&', getLocale().getString("general.greenhouses", "Greenhouses")); + Locale.generalbiome = ChatColor.translateAlternateColorCodes('&', getLocale().getString("general.biome", "Biome")); + Locale.generalowner = ChatColor.translateAlternateColorCodes('&', getLocale().getString("general.owner", "Owner")); + Locale.helphelp = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.help", "help")); + Locale.helpmake = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.make", "Tries to make a greenhouse")); + Locale.helpremove = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.remove", "Removes a greenhouse that you are standing in if you are the owner")); + Locale.helpinfo = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.info", "Shows info on the greenhouse you and general info")); + Locale.helplist = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.list", "Lists all the greenhouse biomes that can be made")); + Locale.helpopengui = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.opengui", "Opens the Greenhouse GUI")); + Locale.helprecipe = ChatColor.translateAlternateColorCodes('&', getLocale().getString("help.recipe", "Tells you how to make greenhouse biome")); + Locale.listtitle = ChatColor.translateAlternateColorCodes('&', getLocale().getString("list.title", "[Greenhouse Biome Recipes]")); + Locale.listinfo = ChatColor.translateAlternateColorCodes('&', getLocale().getString("list.info", "Use /greenhouse recipe to see details on how to make each greenhouse")); + Locale.errorunknownPlayer = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.unknownPlayer", "That player is unknown.")); + Locale.errornoPermission = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.noPermission", "You don't have permission to use that command!")); + Locale.errorcommandNotReady = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.commandNotReady", "You can't use that command right now.")); + Locale.errorofflinePlayer = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.offlinePlayer", "That player is offline or doesn't exist.")); + Locale.errorunknownCommand = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.unknownCommand", "Unknown command.")); + Locale.errormove = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.move", "Move to a greenhouse you own first.")); + Locale.errornotowner = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.notowner", "You must be the owner of this greenhouse to do that.")); + Locale.errorremoving = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.removing", "Removing greenhouse!")); + Locale.errornotyours = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.notyours", "This is not your greenhouse!")); + Locale.errornotinside = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.notinside", "You are not in a greenhouse!")); + Locale.errortooexpensive = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.tooexpensive", "You cannot afford [price]" )); + Locale.erroralreadyexists = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.alreadyexists", "Greenhouse already exists!")); + Locale.errornorecipe = ChatColor.translateAlternateColorCodes('&', getLocale().getString("error.norecipe", "This does not meet any greenhouse recipe!")); + Locale.messagesenter = ChatColor.translateAlternateColorCodes('&', getLocale().getString("messages.enter", "Entering [owner]'s [biome] greenhouse!")); + Locale.messagesleave = ChatColor.translateAlternateColorCodes('&', getLocale().getString("messages.leave", "Now leaving [owner]'s greenhouse.")); + Locale.messagesyouarein = ChatColor.translateAlternateColorCodes('&', getLocale().getString("messages.youarein", "You are now in [owner]'s [biome] greenhouse!")); + Locale.messagesremoved = ChatColor.translateAlternateColorCodes('&', getLocale().getString("messages.removed", "This greenhouse is no more...")); + Locale.messagesremovedmessage = ChatColor.translateAlternateColorCodes('&', getLocale().getString("messages.removedmessage", "A [biome] greenhouse of yours is no more!")); + Locale.messagesecolost = ChatColor.translateAlternateColorCodes('&', getLocale().getString("messages.ecolost", "Your greenhouse at [location] lost its eco system and was removed.")); + Locale.infotitle = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.title", "&A[Greenhouse Construction]")); + Locale.infoinstructions = getLocale().getStringList("info.instructions"); + Locale.infoinfo = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.info", "[Greenhouse Info]")); + Locale.infonone = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.none", "None")); + Locale.infowelcome = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.welcome","&BWelcome! Click here for instructions")); + Locale.infonomore = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.nomore", "&4You cannot build any more greenhouses!")); + Locale.infoonemore = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.onemore","&6You can build one more greenhouse.")); + Locale.infoyoucanbuild = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.youcanbuild","&AYou can builds up to [number] more greenhouses!")); + Locale.infounlimited = ChatColor.translateAlternateColorCodes('&', getLocale().getString("info.unlimited","&AYou can build an unlimited number of greenhouses!")); + Locale.recipehint = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.hint", "Use /greenhouse list to see a list of recipe numbers!")); + Locale.recipewrongnumber = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.wrongnumber", "Recipe number must be between 1 and [size]")); + Locale.recipetitle = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.title", "[[biome] recipe]")); + Locale.recipenowater = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.nowater", "No water allowed.")); + Locale.recipenoice = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.noice", "No ice allowed.")); + Locale.recipenolava = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.nolava", "No lava allowed.")); + Locale.recipewatermustbe = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.watermustbe", "Water > [coverage]% of floor area.")); + Locale.recipeicemustbe = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.icemustbe", "Ice blocks > [coverage]% of floor area.")); + Locale.recipelavamustbe = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.lavamustbe", "Lava > [coverage]% of floor area.")); + Locale.recipeminimumblockstitle = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.minimumblockstitle", "[Minimum blocks required]")); + Locale.lineColor = ChatColor.translateAlternateColorCodes('&', ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.linecolor", "&f"))); + Locale.recipenootherblocks = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.nootherblocks", "No other blocks required.")); + Locale.recipemissing = ChatColor.translateAlternateColorCodes('&', getLocale().getString("recipe.missing", "Greenhouse is missing")); + Locale.eventbroke = ChatColor.translateAlternateColorCodes('&', getLocale().getString("event.broke", "You broke this greenhouse! Reverting biome to [biome]!")); + Locale.eventfix = ChatColor.translateAlternateColorCodes('&', getLocale().getString("event.fix", "Fix the greenhouse and then make it again.")); + Locale.eventcannotplace = ChatColor.translateAlternateColorCodes('&', getLocale().getString("event.cannotplace", "Blocks cannot be placed above a greenhouse!")); + Locale.eventpistonerror = ChatColor.translateAlternateColorCodes('&', getLocale().getString("event.pistonerror", "Pistons cannot push blocks over a greenhouse!")); + Locale.createnoroof = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.noroof", "There seems to be no roof!")); + Locale.createmissingwall = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.missingwall", "A wall is missing!")); + Locale.createnothingabove = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.nothingabove", "There can be no blocks above the greenhouse!")); + Locale.createholeinroof = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.holeinroof", "There is a hole in the roof or it is not flat!")); + Locale.createholeinwall = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.holeinwall", "There is a hole in the wall or they are not the same height all the way around!")); + Locale.createhoppererror = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.hoppererror", "Only one hopper is allowed in the walls or roof.")); + Locale.createdoorerror = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.doorerror", "You cannot have more than 4 doors in the greenhouse!")); + Locale.createsuccess = ChatColor.translateAlternateColorCodes('&', getLocale().getString("create.success", "You successfully made a [biome] biome greenhouse!")); + Locale.adminHelpreload = ChatColor.translateAlternateColorCodes('&', getLocale().getString("adminHelp.reload", "reload configuration from file.")); + Locale.adminHelpinfo = ChatColor.translateAlternateColorCodes('&', getLocale().getString("adminHelp.info", "provides info on the greenhouse you are in")); + Locale.reloadconfigReloaded = ChatColor.translateAlternateColorCodes('&', getLocale().getString("reload.configReloaded", "Configuration reloaded from file.")); + Locale.admininfoerror = ChatColor.translateAlternateColorCodes('&', getLocale().getString("admininfo.error", "Greenhouse info only available in-game")); + Locale.admininfoerror2 = ChatColor.translateAlternateColorCodes('&', getLocale().getString("admininfo.error2", "Put yourself in a greenhouse to see info.")); + Locale.admininfoflags = ChatColor.translateAlternateColorCodes('&', getLocale().getString("admininfo.flags", "[Greenhouse Flags]")); + Locale.newsheadline = ChatColor.translateAlternateColorCodes('&', getLocale().getString("news.headline", "[Greenhouse News]")); + Locale.controlpaneltitle = ChatColor.translateAlternateColorCodes('&', getLocale().getString("controlpanel.title", "&AGreenhouses")); + Locale.limitslimitedto = ChatColor.translateAlternateColorCodes('&', getLocale().getString("limits.limitedto","Permissions limit you to [limit] greenhouses so [number] were removed.")); + Locale.limitsnoneallowed = ChatColor.translateAlternateColorCodes('&', getLocale().getString("limits.noneallowed", "Permissions do not allow you any greenhouses so [number] were removed.")); + + + // Assign settings + this.debug = getConfig().getStringList("greenhouses.debug"); + Settings.allowFlowIn = getConfig().getBoolean("greenhouses.allowflowin", false); + Settings.allowFlowOut = getConfig().getBoolean("greenhouses.allowflowout", false); + // Other settings + Settings.worldName = getConfig().getStringList("greenhouses.worldName"); + if (Settings.worldName.isEmpty()) { + Settings.worldName.add("world"); + } + logger(1,"Greenhouse worlds are: " + Settings.worldName ); + Settings.snowChanceGlobal = getConfig().getDouble("greenhouses.snowchance", 0.5D); + Settings.snowDensity = getConfig().getDouble("greenhouses.snowdensity", 0.1D); + Settings.snowSpeed = getConfig().getLong("greenhouses.snowspeed", 30L); + Settings.iceInfluence = getConfig().getInt("greenhouses.iceinfluence", 125); + Settings.ecoTick = getConfig().getInt("greenhouses.ecotick", 30); + Settings.mobTick = getConfig().getInt("greenhouses.mobtick", 20); + Settings.plantTick = getConfig().getInt("greenhouses.planttick", 5); + Settings.blockTick = getConfig().getInt("greenhouses.blocktick", 10); + + logger(3,"Snowchance " + Settings.snowChanceGlobal); + logger(3,"Snowdensity " + Settings.snowDensity); + logger(3,"Snowspeed " + Settings.snowSpeed); + + // Max greenhouse settings + Settings.maxGreenhouses = getConfig().getInt("greenhouses.maxgreenhouses",-1); + Settings.deleteExtras = getConfig().getBoolean("greenhouses.deleteextras", false); + + } + + /* + * (non-Javadoc) + * + * @see org.bukkit.plugin.java.JavaPlugin#onDisable() + */ + @Override + public void onDisable() { + saveGreenhouses(); + // Reset biomes back + /* + for (Greenhouse g: plugin.getGreenhouses()) { + try { + g.endBiome(); + } catch (Exception e) {} + }*/ + try { + // Remove players from memory + greenhouses.clear(); + playerhouses.clear(); + players.removeAllPlayers(); + saveMessages(); + } catch (final Exception e) { + addon.getLogger().severe("Something went wrong saving files!"); + e.printStackTrace(); + } + } + + /* + * (non-Javadoc) + * + * @see org.bukkit.plugin.java.JavaPlugin#onEnable() + */ + @Override + public void onEnable() { + // instance of this plugin + addon = this; + saveDefaultConfig(); + saveDefaultLocale(); + // Metrics + new MetricsLite(this); + // Economy + if (!VaultHelper.setupEconomy()) { + getLogger().severe("Could not set up economy!"); + } + // Set up player's cache + players = new PlayerCache(this); + loadPluginConfig(); + loadBiomeRecipes(); + biomeInv = new ControlPanel(this); + // Set up commands for this plugin + getCommand("greenhouse").setExecutor(new GreenhouseCmd(this,players)); + getCommand("gadmin").setExecutor(new AdminCmd(this,players)); + // Register events that this plugin uses + registerEvents(); + // Load messages + loadMessages(); + + // Kick off a few tasks on the next tick + getServer().getScheduler().runTask(addon, new Runnable() { + + @Override + public void run() { + final PluginManager manager = Bukkit.getServer().getPluginManager(); + if (manager.isPluginEnabled("Vault")) { + Greenhouses.getAddon().logger(1,"Trying to use Vault for permissions..."); + if (!VaultHelper.setupPermissions()) { + getLogger().severe("Cannot link with Vault for permissions! Disabling plugin!"); + manager.disablePlugin(Greenhouses.getAddon()); + } else { + logger(1,"Success!"); + }; + } + // Load greenhouses + loadGreenhouses(); + } + }); + ecoTick(); + } + + public void ecoTick() { + // Cancel any old schedulers + if (plantTask != null) + plantTask.cancel(); + if (blockTask != null) + blockTask.cancel(); + if (mobTask != null) + mobTask.cancel(); + if (ecoTask != null) + ecoTask.cancel(); + + // Kick off flower growing + long plantTick = Settings.plantTick * 60 * 20; // In minutes + if (plantTick > 0) { + logger(1,"Kicking off flower growing scheduler every " + Settings.plantTick + " minutes"); + plantTask = getServer().getScheduler().runTaskTimer(addon, new Runnable() { + + @Override + public void run() { + for (Greenhouse g : getGreenhouses()) { + logger(3,"Servicing greenhouse biome : " + g.getBiome().toString()); + //checkEco(); + try { + g.growFlowers(); + } catch (Exception e) { + getLogger().severe("Problem found with greenhouse during growing flowers. Skipping..."); + if (addon.getDebug().contains("3")) { + e.printStackTrace(); + } + } + //g.populateGreenhouse(); + } + } + }, 80L, plantTick); + + } else { + logger(1,"Flower growth disabled."); + } + + // Kick off flower growing + long blockTick = Settings.blockTick * 60 * 20; // In minutes + + if (blockTick > 0) { + logger(1,"Kicking off block conversion scheduler every " + Settings.blockTick + " minutes"); + blockTask = getServer().getScheduler().runTaskTimer(addon, new Runnable() { + + @Override + public void run() { + for (Greenhouse g : getGreenhouses()) { + try { + g.convertBlocks(); + } catch (Exception e) { + getLogger().severe("Problem found with greenhouse during block conversion. Skipping..."); + getLogger().severe("[Greenhouse info]"); + getLogger().severe("Owner: " + g.getOwner()); + getLogger().severe("Location " + g.getPos1().toString() + " to " + g.getPos2().toString()); + e.printStackTrace(); + } + + logger(3,"Servicing greenhouse biome : " + g.getBiome().toString()); + } + } + }, 60L, blockTick); + } else { + logger(1,"Block conversion disabled."); + } + // Kick off g/h verification + long ecoTick = Settings.plantTick * 60 * 20; // In minutes + if (ecoTick > 0) { + logger(1,"Kicking off greenhouse verify scheduler every " + Settings.ecoTick + " minutes"); + ecoTask = getServer().getScheduler().runTaskTimer(addon, new Runnable() { + + @Override + public void run() { + try { + checkEco(); + } catch (Exception e) { + getLogger().severe("Problem found with greenhouse during eco check. Skipping..."); + if (addon.getDebug().contains("3")) { + e.printStackTrace(); + } + } + + //} + } + }, ecoTick, ecoTick); + + } else { + logger(1,"Greenhouse verification disabled."); + } + // Kick off mob population + long mobTick = Settings.mobTick * 60 * 20; // In minutes + if (mobTick > 0) { + logger(1,"Kicking off mob populator scheduler every " + Settings.plantTick + " minutes"); + mobTask = getServer().getScheduler().runTaskTimer(addon, new Runnable() { + + @Override + public void run() { + for (Greenhouse g : getGreenhouses()) { + g.populateGreenhouse(); + } + } + }, 120L, mobTick); + + } else { + logger(1,"Mob disabled."); + } + } + + + /** + * Returns a pseudo-random number between min and max, inclusive. + * The difference between min and max can be at most + * Integer.MAX_VALUE - 1. + * + * @param min Minimum value + * @param max Maximum value. Must be greater than min. + * @return Integer between min and max, inclusive. + * @see java.util.Random#nextInt(int) + */ + public static int randInt(int min, int max) { + // nextInt is normally exclusive of the top value, + // so add 1 to make it inclusive + Random rand = new Random(); + int randomNum = rand.nextInt((max - min) + 1) + min; + //Bukkit.logger(1,"Random number = " + randomNum); + return randomNum; + } + + + /** + * Load all known greenhouses + */ + protected void loadGreenhouses() { + // Load all known greenhouses + // Clear them first + greenhouses.clear(); + // Check for updated file + greenhouseFile = new File(this.getDataFolder(),"greenhouses.yml"); + greenhouseConfig = new YamlConfiguration(); + File playersFolder = new File(this.getDataFolder(),"players"); + // See if the new file exists or not, if not make it + if (!greenhouseFile.exists() && !playersFolder.exists()) { + // Brand new install + logger(1,"Creating new greenhouse.yml file"); + greenhouseConfig.createSection("greenhouses"); + try { + greenhouseConfig.save(greenhouseFile); + } catch (IOException e) { + logger(1,"Could not save greenhouse.yml file!"); + // Could not save + e.printStackTrace(); + } + } else if (!greenhouseFile.exists() && playersFolder.exists()) { + logger(1,"Converting from old greenhouse storage to new greenhouse storage"); + ConfigurationSection greenhouseSection = greenhouseConfig.createSection("greenhouses"); + int greenhouseNum = 0; + // Load all the players + for (final File f : playersFolder.listFiles()) { + // Need to remove the .yml suffix + String fileName = f.getName(); + if (fileName.endsWith(".yml")) { + try { + logger(1,"Converting " + fileName.substring(0, fileName.length() - 4)); + final UUID playerUUID = UUID.fromString(fileName.substring(0, fileName.length() - 4)); + if (playerUUID == null) { + getLogger().warning("Player file contains erroneous UUID data."); + getLogger().warning("Looking at " + fileName.substring(0, fileName.length() - 4)); + } + //new Players(this, playerUUID); + YamlConfiguration playerInfo = new YamlConfiguration(); + playerInfo.load(f); + // Copy over greenhouses + ConfigurationSection myHouses = playerInfo.getConfigurationSection("greenhouses"); + if (myHouses != null) { + // Get a list of all the greenhouses + for (String key : myHouses.getKeys(false)) { + try { + // Copy over the info + greenhouseSection.set(greenhouseNum + ".owner", playerUUID.toString()); + greenhouseSection.set(greenhouseNum + ".playerName", playerInfo.getString("playerName","")); + greenhouseSection.set(greenhouseNum + ".pos-one", playerInfo.getString("greenhouses." + key + ".pos-one","")); + greenhouseSection.set(greenhouseNum + ".pos-two", playerInfo.getString("greenhouses." + key + ".pos-two","")); + greenhouseSection.set(greenhouseNum + ".originalBiome", playerInfo.getString("greenhouses." + key + ".originalBiome", "PLAINS")); + greenhouseSection.set(greenhouseNum + ".greenhouseBiome", playerInfo.getString("greenhouses." + key + ".greenhouseBiome", "PLAINS")); + greenhouseSection.set(greenhouseNum + ".roofHopperLocation", playerInfo.getString("greenhouses." + key + ".roofHopperLocation")); + greenhouseSection.set(greenhouseNum + ".farewellMessage", playerInfo.getString("greenhouses." + key + ".flags.farewellMessage","")); + greenhouseSection.set(greenhouseNum + ".enterMessage", playerInfo.getString("greenhouses." + key + ".flags.enterMessage","")); + } catch (Exception e) { + addon.getLogger().severe("Problem copying player files"); + e.printStackTrace(); + } + greenhouseNum++; + } + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + } + // Save the greenhouse file + try { + greenhouseConfig.save(greenhouseFile); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else if (greenhouseFile.exists()){ + // Load greenhouses from new file + try { + greenhouseConfig.load(greenhouseFile); + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvalidConfigurationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + if (greenhouseConfig.isConfigurationSection("greenhouses")) { + ConfigurationSection myHouses = greenhouseConfig.getConfigurationSection("greenhouses"); + if (myHouses != null) { + // Get a list of all the greenhouses + for (String key : myHouses.getKeys(false)) { + logger(3,"Loading greenhouse #" + key); + try { + String playerName = myHouses.getString(key + ".playerName", ""); + // Load all the values + Location pos1 = getLocationString(myHouses.getString(key + ".pos-one")); + Location pos2 = getLocationString(myHouses.getString(key + ".pos-two")); + UUID owner = UUID.fromString(myHouses.getString(key + ".owner")); + logger(3,"File pos1: " + pos1.toString()); + logger(3,"File pos1: " + pos2.toString()); + if (pos1 != null && pos2 !=null) { + // Check if this greenhouse already exists + if (!checkGreenhouseIntersection(pos1, pos2)) { + Greenhouse g = new Greenhouse(this, pos1, pos2, owner); + logger(3,"Greenhouse pos1: " + g.getPos1().toString()); + logger(3,"Greenhouse pos2: " + g.getPos2().toString()); + // Set owner name + g.setPlayerName(playerName); + logger(3,"Owner is " + playerName); + // Set biome + String oBiome = myHouses.getString(key + ".originalBiome", "PLAINS"); + // Do some conversions + Biome originalBiome = convertBiome(oBiome); + g.setOriginalBiome(originalBiome); + logger(3,"original biome = " + oBiome + " converted = " + originalBiome); + String gBiome = myHouses.getString(key + ".greenhouseBiome", "PLAINS"); + Biome greenhouseBiome = convertBiome(gBiome); + if (greenhouseBiome == null) { + greenhouseBiome = Biome.PLAINS; + } + logger(3,"greenhouse biome = " + gBiome + " converted = " + greenhouseBiome); + String recipeName = myHouses.getString(key + ".greenhouseRecipe", ""); + boolean success = false; + // Check to see if this biome has a recipe + // Try by name first + for (BiomeRecipe br : getBiomeRecipes()) { + if (br.getName().equalsIgnoreCase(recipeName)) { + success = true; + g.setBiomeRecipe(br); + break; + } + } + // Fall back to biome + if (!success) { + for (BiomeRecipe br : getBiomeRecipes()) { + if (br.getBiome().equals(greenhouseBiome)) { + success = true; + g.setBiomeRecipe(br); + break; + } + } + } + // Check to see if it was set properly + if (!success) { + getLogger().warning("*****************************************"); + getLogger().warning("WARNING: No known recipe for biome " + greenhouseBiome.toString()); + getLogger().warning("[Greenhouse info]"); + getLogger().warning("Owner: " + playerName + " UUID:" + g.getOwner()); + getLogger().warning("Location :" + g.getPos1().getWorld().getName() + " " + g.getPos1().getBlockX() + "," + g.getPos1().getBlockZ()); + getLogger().warning("Greenhouse will be removed next eco-tick!"); + getLogger().warning("*****************************************"); + } + // Start the biome + g.startBiome(false); + Location hopperLoc = getLocationString(myHouses.getString(key + ".roofHopperLocation")); + if (hopperLoc != null) { + g.setRoofHopperLocation(hopperLoc); + } + // Load farewell and hello messages + g.setEnterMessage(myHouses.getString(key +".enterMessage",(Locale.messagesenter.replace("[owner]", playerName )).replace("[biome]", Util.prettifyText(gBiome)))); + g.setFarewellMessage(myHouses.getString(key +".farewellMessage",Locale.messagesleave.replace("[owner]", playerName))); + // Add to the cache + addGHToPlayer(owner, g); + } + } else { + getLogger().severe("Problem loading greenhouse with locations " + myHouses.getString(key + ".pos-one") + " and " + myHouses.getString(key + ".pos-two") + " skipping."); + getLogger().severe("Has this world been deleted?"); + } + } catch (Exception e) { + getLogger().severe("Problem loading greenhouse file"); + e.printStackTrace(); + } + + } + logger(3,"Loaded " + addon.getGreenhouses().size() + " greenhouses."); + } + } + + logger(1,"Loaded " + getGreenhouses().size() + " greenhouses."); + } + + public void addGHToPlayer(UUID owner, Greenhouse g) { + HashSet storedhouses = null; + + if (playerhouses.get(owner) != null) { + storedhouses = playerhouses.get(owner); + playerhouses.remove(owner); + } else { + storedhouses = new HashSet(); + } + + storedhouses.add(g); + greenhouses.add(g); + playerhouses.put(owner, storedhouses); + } + + public void removeGHFromPlayer(UUID owner, Greenhouse g) { + HashSet storedhouses = null; + + if (playerhouses.get(owner) != null) { + storedhouses = playerhouses.get(owner); + playerhouses.remove(owner); + } else { + storedhouses = new HashSet(); + } + + storedhouses.remove(g); + greenhouses.remove(g); + playerhouses.put(owner, storedhouses); + } + + + /** + * Converts biomes to known biomes if required + * @param oBiome + * @return + */ + private Biome convertBiome(String oBiome) { + if (addon.getServer().getVersion().contains("(MC: 1.8") || addon.getServer().getVersion().contains("(MC: 1.7")) { + try { + return Biome.valueOf(oBiome); + } catch (Exception e) { + getLogger().severe("Could not identify Biome " + oBiome + " setting to PLAINS - may destroy greenhouse"); + return Biome.PLAINS; + } + } else { + // May need to convert Biome + if (oBiome.equalsIgnoreCase("COLD_TAIGA")) { + getLogger().warning("Converting Cold Taiga biome to 1.9 Taiga Cold"); + return Biome.TAIGA_COLD; + } + if (oBiome.equalsIgnoreCase("FLOWER_FOREST")) { + getLogger().warning("Converting Flower Forest biome to 1.9 Forest"); + return Biome.FOREST; + } + if (oBiome.equalsIgnoreCase("BEACH")) { + getLogger().warning("Converting Beach biome to 1.9 Beaches"); + return Biome.BEACHES; + } + String test = oBiome; + + while (!test.isEmpty()) { + for (Biome biome: Biome.values()) { + if (biome.name().contains(test)) { + if (!biome.name().equals(test)) { + getLogger().warning("Converting " + oBiome + " biome to 1.9 " + biome.name() + " - may destroy greenhouse."); + } + return biome; + } + } + test = test.substring(0, test.length() - 1); + } + } + getLogger().severe("Could not identify Biome " + oBiome + " setting to PLAINS - may destroy greenhouse"); + return Biome.PLAINS; + } + + + /** + * Registers events + */ + public void registerEvents() { + final PluginManager manager = getServer().getPluginManager(); + // Greenhouse Protection events + manager.registerEvents(new GreenhouseGuard(this), this); + // Listen to greenhouse change events + manager.registerEvents(new GreenhouseEvents(this), this); + // Events for when a player joins or leaves the server + manager.registerEvents(new JoinLeaveEvents(this, players), this); + // Biome CP + manager.registerEvents(biomeInv, this); + // Weather event + manager.registerEvents(eco, this); + } + + + // Localization + /** + * Saves the locale.yml file if it does not exist + */ + public void saveDefaultLocale() { + if (localeFile == null) { + localeFile = new File(getDataFolder(), "locale.yml"); + } + if (!localeFile.exists()) { + saveResource("locale.yml", false); + } + } + + /** + * Reloads the locale file + */ + public void reloadLocale() { + if (localeFile == null) { + saveDefaultLocale(); + } + locale = YamlConfiguration.loadConfiguration(localeFile); + } + + /** + * @return locale FileConfiguration object + */ + public FileConfiguration getLocale() { + if (locale == null) { + reloadLocale(); + } + return locale; + } + + + + /* + public void saveLocale() { + if (locale == null || localeFile == null) { + return; + } + try { + getLocale().save(localeFile); + } catch (IOException ex) { + getLogger().severe("Could not save config to " + localeFile); + } + } + */ + /** + * Sets a message for the player to receive next time they login + * @param player + * @param message + * @return true if player is offline, false if online + */ + public boolean setMessage(UUID playerUUID, String message) { + logger(3,"received message - " + message); + Player player = getServer().getPlayer(playerUUID); + // Check if player is online + if (player != null) { + if (player.isOnline()) { + //player.sendMessage(message); + return false; + } + } + // Player is offline so store the message + + List playerMessages = messages.get(playerUUID); + if (playerMessages != null) { + playerMessages.add(message); + } else { + playerMessages = new ArrayList(Arrays.asList(message)); + } + messages.put(playerUUID, playerMessages); + return true; + } + + public List getMessages(UUID playerUUID) { + List playerMessages = messages.get(playerUUID); + if (playerMessages != null) { + // Remove the messages + messages.remove(playerUUID); + } else { + // No messages + playerMessages = new ArrayList(); + } + return playerMessages; + } + + public boolean saveMessages() { + logger(1,"Saving offline messages..."); + try { + // Convert to a serialized string + final HashMap offlineMessages = new HashMap(); + for (UUID p : messages.keySet()) { + offlineMessages.put(p.toString(),messages.get(p)); + } + // Convert to YAML + messageStore.set("messages", offlineMessages); + saveYamlFile(messageStore, "messages.yml"); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + public boolean loadMessages() { + logger(1,"Loading offline messages..."); + try { + messageStore = loadYamlFile("messages.yml"); + if (messageStore.getConfigurationSection("messages") == null) { + messageStore.createSection("messages"); // This is only used to create + } + HashMap temp = (HashMap) messageStore.getConfigurationSection("messages").getValues(true); + for (String s : temp.keySet()) { + List messageList = messageStore.getStringList("messages." + s); + if (!messageList.isEmpty()) { + messages.put(UUID.fromString(s), messageList); + } + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * @return the greenhouses + */ + public HashSet getGreenhouses() { + return greenhouses; + } + + /** + * + * @param uuid + * @return Cached player greenhouses + */ + /* + public HashSet getPlayerGHouse(UUID uuid) { + if (playerhouses.containsKey(uuid)) { + return playerhouses.get(uuid); + } + return null; + } + */ + + /** + * @param greenhouses the greenhouses to set + */ + public void setGreenhouses(HashSet greenhouses) { + this.greenhouses = greenhouses; + } + + /** + * @return the playerhouses + */ + public HashMap> getPlayerhouses() { + return playerhouses; + } + + + /** + * Clears the greenhouses list + */ + public void clearGreenhouses() { + this.greenhouses.clear(); + } + + /** + * Checks if a greenhouse defined by the corner points pos1 and pos2 overlaps any known greenhouses + * @param pos1 + * @param pos2 + * @return + */ + public boolean checkGreenhouseIntersection(Location pos1, Location pos2) { + // Create a 2D rectangle of this + Rectangle2D.Double rect = new Rectangle2D.Double(); + rect.setFrameFromDiagonal(pos1.getX(), pos1.getZ(), pos2.getX(), pos2.getZ()); + Rectangle2D.Double testRect = new Rectangle2D.Double(); + // Create a set of rectangles of current greenhouses + for (Greenhouse d: greenhouses) { + testRect.setFrameFromDiagonal(d.getPos1().getX(), d.getPos1().getZ(),d.getPos2().getX(),d.getPos2().getZ()); + if (rect.intersects(testRect)) { + return true; + } + } + return false; + } + + /** + * Checks if a location is inside a greenhouse (3D space) + * @param location + * @return Greenhouse or null if none + */ + public Greenhouse getInGreenhouse(Location location) { + for (Greenhouse g : greenhouses) { + //logger(3,"greenhouse check"); + if (g.insideGreenhouse(location)) { + return g; + } + } + // This location is not in a greenhouse + return null; + } + + /** + * Checks if the location is on the greenhouse + * @param location + * @return the greenhouse that this is above + */ + public Greenhouse aboveAGreenhouse(Location location) { + for (Greenhouse g : greenhouses) { + //logger(3,"greenhouse check"); + if (g.aboveGreenhouse(location)) { + return g; + } + } + // This location is above in a greenhouse + return null; + } + + /** + * Removes the greenhouse from the world and resets biomes + * @param g + */ + public void removeGreenhouse(Greenhouse g) { + //players.get(g.getOwner()); + // Remove the greenhouse + greenhouses.remove(g); + // Remove the greenhouse from the owner's count (only does anything if they are online) + players.decGreenhouseCount(g.getOwner()); + // Stop any eco action + eco.remove(g); + logger(3,"Returning biome to original state: " + g.getOriginalBiome().toString()); + //g.setBiome(g.getOriginalBiome()); // just in case + if (g.getOriginalBiome().equals(Biome.HELL) || g.getOriginalBiome().equals(Biome.DESERT) + || g.getOriginalBiome().equals(Biome.DESERT_HILLS)) { + // Remove any water + for (int y = g.getPos1().getBlockY(); y< g.getPos2().getBlockY();y++) { + for (int x = g.getPos1().getBlockX();x<=g.getPos2().getBlockX();x++) { + for (int z = g.getPos1().getBlockZ();z<=g.getPos2().getBlockZ();z++) { + Block b = g.getPos1().getWorld().getBlockAt(x, y, z); + if (b.getType().equals(Material.WATER) || b.getType().equals(Material.STATIONARY_WATER) + || b.getType().equals(Material.ICE) || b.getType().equals(Material.PACKED_ICE) + || b.getType().equals(Material.SNOW) || b.getType().equals(Material.SNOW_BLOCK)) { + // Evaporate it + b.setType(Material.AIR); + //ParticleEffects.SMOKE_LARGE.send(Bukkit.getOnlinePlayers(),b.getLocation(),0D,0D,0D,1F,5,20); + b.getWorld().spawnParticle(Particle.SMOKE_LARGE, b.getLocation(), 5); + //ParticleEffect.SMOKE_LARGE.display(0F,0F,0F, 0.1F, 5, b.getLocation(), 30D); + } + } + } + } + } + g.endBiome(); + boolean ownerOnline = false; + if (!ownerOnline) { + setMessage(g.getOwner(), Locale.messagesremovedmessage.replace("[biome]", Util.prettifyText(g.getBiome().toString())) + " [" + g.getPos1().getBlockX() + "," + g.getPos1().getBlockZ() + "]"); + } + /* + // Set the biome + for (int y = g.getPos1().getBlockY(); y< g.getPos2().getBlockY();y++) { + + for (int x = g.getPos1().getBlockX()+1;x onesToRemove = new ArrayList(); + for (Greenhouse g : getGreenhouses()) { + logger(3,"Testing greenhouse owned by " + g.getOwner().toString()); + if (!g.checkEco()) { + // The greenhouse failed an eco check - remove it + onesToRemove.add(g); + } + } + for (Greenhouse gg : onesToRemove) { + // Check if player is online + Player owner = addon.getServer().getPlayer(gg.getOwner()); + if (owner == null) { + // TODO messages.ecolost + setMessage(gg.getOwner(), Locale.messagesecolost.replace("[location]", Greenhouses.getStringLocation(gg.getPos1()))); + } else { + owner.sendMessage(ChatColor.RED + Locale.messagesecolost.replace("[location]", Greenhouses.getStringLocation(gg.getPos1()))); + } + + logger(1,"Greenhouse at " + Greenhouses.getStringLocation(gg.getPos1()) + " lost its eco system and was removed."); + logger(1,"Greenhouse biome was " + Util.prettifyText(gg.getBiome().toString()) + " - reverted to " + Util.prettifyText(gg.getOriginalBiome().toString())); + //UUID ownerUUID = gg.getOwner(); + removeGreenhouse(gg); + removeGHFromPlayer(owner.getUniqueId(), gg); + //players.save(ownerUUID); + } + saveGreenhouses(); + } + + public Inventory getRecipeInv(Player player) { + return biomeInv.getPanel(player); + } + + + /** + * Checks that a greenhouse meets specs and makes it + * @param player + * @return the Greenhouse object + */ + public Greenhouse tryToMakeGreenhouse(final Player player) { + return tryToMakeGreenhouse(player, null); + } + /** + * Checks that a greenhouse meets specs and makes it + * If type is stated then only this specific type will be checked + * @param player + * @param greenhouseRecipe + * @return + */ + @SuppressWarnings("deprecation") + public Greenhouse tryToMakeGreenhouse(final Player player, BiomeRecipe greenhouseRecipe) { + if (greenhouseRecipe != null) { + // Do an immediate permissions check of the biome recipe if the type is declared + if (!greenhouseRecipe.getPermission().isEmpty()) { + if (!VaultHelper.checkPerm(player, greenhouseRecipe.getPermission())) { + player.sendMessage(ChatColor.RED + Locale.errornoPermission); + logger(2,"no permssions to use this biome"); + return null; + } + } + String name = greenhouseRecipe.getFriendlyName(); + if (name.isEmpty()) { + name = Util.prettifyText(greenhouseRecipe.getBiome().name()) + " biome"; + } + player.sendMessage(ChatColor.GOLD + "Trying to make a " + name + " greenhouse..."); + } + // Proceed to check the greenhouse + final Location location = player.getLocation().add(new Vector(0,1,0)); + logger(3,"Player location is " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ()); + final Biome originalBiome = location.getBlock().getBiome(); + final World world = location.getWorld(); + + // we have the height above this location where a roof block is + // Check the sides + + // Now look along the roof until we find the dimensions of the roof + Roof roof = new Roof(this, location); + if (!roof.isRoofFound()) { + player.sendMessage(ChatColor.RED + Locale.createnoroof); + logger(3,"Roof not found with roof check"); + return null; + } + // Check that the player is under the roof + if (!(player.getLocation().getBlockX() > roof.getMinX() && player.getLocation().getBlockX() <= roof.getMaxX() + && player.getLocation().getBlockZ() > roof.getMinZ() && player.getLocation().getBlockZ() <= roof.getMaxZ())) { + logger(3,"Player does not appear to be inside the greenhouse"); + logger(3,"Player location " + player.getLocation()); + logger(3,"Roof minx = " + roof.getMinX() + " maxx = " + roof.getMaxX()); + logger(3,"Roof minz = " + roof.getMinZ() + " maxz = " + roof.getMaxZ()); + player.sendMessage(ChatColor.RED + Locale.errornotinside); + return null; + } + // Now see if the walls match the roof - they may not + Walls walls = new Walls(this, player, roof); + // Roof is never smaller than walls, but walls can be smaller than the roof + int maxX = walls.getMaxX(); + int minX = walls.getMinX(); + int maxZ = walls.getMaxZ(); + int minZ = walls.getMinZ(); + + logger(3,"minx = " + minX); + logger(3,"maxx = " + maxX); + logger(3,"minz = " + minZ); + logger(3,"maxz = " + maxZ); + // Now check again to see if the floor really is the floor and the walls follow the rules + // Counts + int wallDoors = 0; + // Hoppers + int ghHopper = 0; + // Air + boolean airHoles = false; + // Other blocks + boolean otherBlocks = false; + // Ceiling issue + boolean inCeiling = false; + // Blocks above the greenhouse + boolean blocksAbove = false; + // The y height where other blocks were found + // If this is the bottom layer, the player has most likely uneven walls + int otherBlockLayer = -1; + int wallBlockCount = 0; + Location roofHopperLoc = null; + Set redGlass = new HashSet(); + int y = 0; + for (y = world.getMaxHeight(); y >= walls.getFloor(); y--) { + Set redLayer = new HashSet(); + int doorCount = 0; + int hopperCount = 0; + boolean airHole = false; + boolean otherBlock = false; + boolean blockAbove = false; + wallBlockCount = 0; + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + Location thisBlock = new Location(world, x, y, z); + Material blockType = world.getBlockAt(x, y, z).getType(); + // Checking above greenhouse - no blocks allowed + if (y > roof.getHeight()) { + //Greenhouses.logger(3,"DEBUG: Above greenhouse"); + // We are above the greenhouse + if ((world.getEnvironment().equals(Environment.NORMAL) || world.getEnvironment().equals(Environment.THE_END)) + && blockType != Material.AIR) { + blockAbove = true; + redLayer.add(thisBlock); + } + } else { + //Greenhouses.logger(3,"DEBUG: In greenhouse"); + // Check just the walls + if (y == roof.getHeight() || x == minX || x == maxX || z == minZ || z== maxZ) { + //Greenhouses.logger(3,"DEBUG: Checking " + x + " " + y + " " + z); + // Doing string check for DOOR allows all 1.8 doors to be covered even if the server is not 1.8 + if ((y != roof.getHeight() && !WALLBLOCKS.contains(blockType.name()) && !blockType.toString().contains("DOOR")) + || (y == roof.getHeight() && !roof.isRoofBlock(blockType) && !blockType.toString().contains("DOOR"))) { + logger(2,"DEBUG: bad block found at " + x + "," + y+ "," + z + " " + blockType); + if (blockType == Material.AIR) { + airHole = true; + if (y == roof.getHeight()) { + inCeiling = true; + } + } else { + otherBlock = true; + } + redLayer.add(thisBlock); + } else { + wallBlockCount++; + // A string comparison is used to capture 1.8+ door types without stopping pre-1.8 + // servers from working + if (blockType.toString().contains("DOOR")) { + doorCount++; + // If we already have 8 doors add these blocks to the red list + if (wallDoors == 8) { + redLayer.add(thisBlock); + } + } + if (blockType.equals(Material.HOPPER)) { + hopperCount++; + if (ghHopper > 0) { + // Problem! Add extra hoppers to the red glass list + redLayer.add(thisBlock); + } else { + // This is the first hopper + roofHopperLoc = thisBlock.clone(); + } + } + } + } + } + } + } + if (wallBlockCount == 0 && y < roof.getHeight()) { + // This is the floor + break; + } else { + wallBlockCount = 0; + wallDoors += doorCount; + ghHopper += hopperCount; + if (airHole) { + airHoles = true; + } + if (otherBlock) { + otherBlocks = true; + if (otherBlockLayer < 0) { + otherBlockLayer = y; + } + } + if (blockAbove) { + blocksAbove = true; + } + // Collect the holes + redGlass.addAll(redLayer); + } + } + logger(3,"Floor is at height y = " + y); + // Check that the player is vertically in the greenhouse + if (player.getLocation().getBlockY() <= y) { + player.sendMessage(ChatColor.RED + Locale.errornotinside); + return null; + } + if (!redGlass.isEmpty()) { + // Show errors + if (blocksAbove) { + player.sendMessage(ChatColor.RED + Locale.createnothingabove); + } + if (airHoles & !inCeiling) { + player.sendMessage(ChatColor.RED + Locale.createholeinwall); + } else if (airHoles & inCeiling) { + player.sendMessage(ChatColor.RED + Locale.createholeinroof); + } + //Greenhouses.logger(3,"DEBUG: otherBlockLayer = " + otherBlockLayer); + if (otherBlocks && otherBlockLayer == y + 1) { + player.sendMessage(ChatColor.RED + "Walls must be even all the way around"); + } else if (otherBlocks && otherBlockLayer == roof.getHeight()) { + player.sendMessage(ChatColor.RED + "Roof blocks must be glass, glowstone, doors or a hopper."); + } else if (otherBlocks) { + player.sendMessage(ChatColor.RED + "Wall blocks must be glass, glowstone, doors or a hopper."); + } + if (wallDoors > 8) { + player.sendMessage(ChatColor.RED + Locale.createdoorerror); + } + if (ghHopper > 1) { + player.sendMessage(ChatColor.RED + Locale.createhoppererror); + } + // Display the red glass and then erase it + for (Location loc: redGlass) { + player.sendBlockChange(loc,Material.STAINED_GLASS,(byte)14); + } + final Set original = redGlass; + Bukkit.getScheduler().runTaskLater(this, new Runnable() { + + @Override + public void run() { + for (Location loc: original) { + player.sendBlockChange(loc,loc.getBlock().getType(),loc.getBlock().getData()); + } + }}, 120L); + return null; + } + //player.sendMessage(ChatColor.GREEN + "Seems ok"); + + Location insideOne = new Location(world,minX,walls.getFloor(),minZ); + Location insideTwo = new Location(world,maxX,roof.getHeight(),maxZ); + BiomeRecipe winner = null; + // Now check for the greenhouse biomes + if (greenhouseRecipe != null) { + if (greenhouseRecipe.checkRecipe(insideOne, insideTwo, player)) { + winner = greenhouseRecipe; + } else { + return null; + } + } + if (winner == null) { + // Loop through biomes to see which ones match + // Int is the priority. Higher priority ones win + int priority = 0; + for (BiomeRecipe r : addon.getBiomeRecipes()) { + // Only check ones that this player has permission to use + if (r.getPermission().isEmpty() || (!r.getPermission().isEmpty() && VaultHelper.checkPerm(player, r.getPermission()))) { + // Only check higher priority ones + if (r.getPriority()>priority) { + player.sendMessage(ChatColor.GOLD + "Trying " + Util.prettifyText(r.getBiome().toString())); + if (r.checkRecipe(insideOne, insideTwo, null)) { + player.sendMessage(ChatColor.GOLD + "Maybe..."); + winner = r; + priority = r.getPriority(); + } else { + player.sendMessage(ChatColor.GOLD + "No."); + } + } + } else { + addon.logger(2, "No permission for " + player.getName() + " to make " + r.getBiome().toString()); + //player.sendMessage(ChatColor.RED + "No permission for " + r.getType().toString()); + } + } + } + + if (winner != null) { + logger(3,"biome winner is " + winner.getFriendlyName()); + Greenhouse g = new Greenhouse(this, insideOne, insideTwo, player.getUniqueId()); + g.setOriginalBiome(originalBiome); + g.setBiomeRecipe(winner); + String friendlyName = Util.prettifyText(winner.getBiome().toString()); + if (!winner.getFriendlyName().isEmpty()) { + friendlyName = winner.getFriendlyName(); + } + g.setPlayerName(player.getName()); + g.setEnterMessage((Locale.messagesenter.replace("[owner]", player.getDisplayName())).replace("[biome]", friendlyName)); + g.setFarewellMessage((Locale.messagesleave.replace("[owner]", player.getDisplayName())).replace("[biome]", friendlyName)); + // Store the roof hopper location so it can be tapped in the future + if (ghHopper == 1) { + g.setRoofHopperLocation(roofHopperLoc); + } + g.startBiome(false); + player.sendMessage(ChatColor.GREEN + Locale.createsuccess.replace("[biome]", friendlyName)); + players.incGreenhouseCount(player); + // Store this greenhouse + greenhouses.add(g); + // Find everyone who is in this greenhouse and tell them they are in a greenhouse now + for (Player p : getServer().getOnlinePlayers()) { + if (g.insideGreenhouse(p.getLocation())) { + if (!p.equals(player)) { + p.sendMessage((Locale.messagesyouarein.replace("[owner]", player.getDisplayName())).replace("[biome]", friendlyName)); + } + } + } + return g; + } + return null; + } + + /** + * Saves all the greenhouses to greenhouse.yml + */ + public void saveGreenhouses() { + logger(2,"Saving greenhouses..."); + ConfigurationSection greenhouseSection = greenhouseConfig.createSection("greenhouses"); + // Get a list of all the greenhouses + int greenhouseNum = 0; + for (Greenhouse g: greenhouses) { + try { + // Copy over the info + greenhouseSection.set(greenhouseNum + ".owner", g.getOwner().toString()); + greenhouseSection.set(greenhouseNum + ".playerName", g.getPlayerName()); + greenhouseSection.set(greenhouseNum + ".pos-one", getStringLocation(g.getPos1())); + greenhouseSection.set(greenhouseNum + ".pos-two", getStringLocation(g.getPos2())); + greenhouseSection.set(greenhouseNum + ".originalBiome", g.getOriginalBiome().toString()); + greenhouseSection.set(greenhouseNum + ".greenhouseBiome", g.getBiome().toString()); + greenhouseSection.set(greenhouseNum + ".greenhouseRecipe", g.getBiomeRecipe().getName()); + greenhouseSection.set(greenhouseNum + ".roofHopperLocation", getStringLocation(g.getRoofHopperLocation())); + greenhouseSection.set(greenhouseNum + ".farewellMessage", g.getFarewellMessage()); + greenhouseSection.set(greenhouseNum + ".enterMessage", g.getEnterMessage()); + } catch (Exception e) { + addon.getLogger().severe("Problem copying player files"); + e.printStackTrace(); + } + greenhouseNum++; + } + try { + greenhouseConfig.save(greenhouseFile); + } catch (IOException e) { + getLogger().severe("Could not save greenhouse.yml!"); + e.printStackTrace(); + } + } + + /** + * General purpose logger to reduce console spam + * @param level + * @param info + */ + public void logger(int level, String info) { + if (debug.contains("0")) { + return; + } + if (debug.contains("1") && level == 1) { + Bukkit.getLogger().info(info); + } else if (debug.contains(String.valueOf(level))){ + Bukkit.getLogger().info("DEBUG ["+level+"]:"+info); + } + } + + + /** + * @return the debug + */ + public List getDebug() { + return debug; + } + + /** + * Returns the maximum number of greenhouses this player can make + * @param player + * @return number of greenhouses or -1 to indicate unlimited + */ + public int getMaxGreenhouses(Player player) { + // -1 is unimited + int maxGreenhouses = Settings.maxGreenhouses; + for (PermissionAttachmentInfo perms : player.getEffectivePermissions()) { + if (perms.getPermission().startsWith("greenhouses.limit")) { + logger(2,"Permission is = " + perms.getPermission()); + try { + int max = Integer.valueOf(perms.getPermission().split("greenhouses.limit.")[1]); + if (max > maxGreenhouses) { + maxGreenhouses = max; + } + } catch (Exception e) {} // Do nothing + } + // Do some sanity checking + if (maxGreenhouses < 0) { + maxGreenhouses = -1; + } + } + return maxGreenhouses; + } +} diff --git a/src/main/java/world/bentobox/greenhouses/PlayerCache.java b/src/main/java/world/bentobox/greenhouses/PlayerCache.java new file mode 100644 index 0000000..4e6cc57 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/PlayerCache.java @@ -0,0 +1,193 @@ +package world.bentobox.greenhouses; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.ui.Locale; + +/** + * @author tastybento + * Provides a memory cache of online player information + * This is the one-stop-shop of player info + */ +public class PlayerCache { + private HashMap playerCache = new HashMap(); + private final Greenhouses plugin; + + public PlayerCache(Greenhouses plugin) { + this.plugin = plugin; + playerCache.clear(); + // Add any players currently online (handles the /reload condition) + final Collection serverPlayers = plugin.getServer().getOnlinePlayers(); + for (Player p : serverPlayers) { + // Add this player to the online cache + playerCache.put(p.getUniqueId(), new Players(p)); + } + } + + /** + * Add a player to the cache when they join the server (called in JoinLeaveEvents) + * @param player + */ + public void addPlayer(Player player) { + if (!playerCache.containsKey(player.getUniqueId())) { + playerCache.put(player.getUniqueId(),new Players(player)); + } + // Check permission limits on number of greenhouses + int limit = plugin.getMaxGreenhouses(player); // 0 = none allowed. Positive numbers = limit. Negative = unlimited + /* + if (plugin.getPlayerGHouse(player.getUniqueId()) == null) { + return; + } + */ + List toBeRemoved = new ArrayList(); + // Look at how many greenhouses player has and remove any over their limit + int owned = 0; + for (Greenhouse g: plugin.getGreenhouses()) { + if (g.getOwner().equals(player.getUniqueId())) { + owned++; + if (owned <= limit) { + // Allowed + playerCache.get(player.getUniqueId()).incrementGreenhouses(); + g.setPlayerName(player.getName()); + } else { + // Over the limit + toBeRemoved.add(g); + } + } + } + if (Settings.deleteExtras && limit >= 0) { + // Remove greenhouses + for (Greenhouse g : toBeRemoved) { + plugin.removeGreenhouse(g); + plugin.logger(2,"Removed greenhouse over the limit for " + player.getName()); + } + if (toBeRemoved.size() > 0) { + if (limit == 0) { + player.sendMessage(ChatColor.RED + Locale.limitsnoneallowed.replace("[number]",String.valueOf(toBeRemoved.size()))); + } else { + player.sendMessage(ChatColor.RED + Locale.limitslimitedto.replace("[limit]", String.valueOf(limit)).replace("[number]",String.valueOf(toBeRemoved.size()))); + } + } + } + } + + + public void removeOnlinePlayer(Player player) { + if (playerCache.containsKey(player.getUniqueId())) { + playerCache.remove(player); + plugin.logger(3,"Removing player from cache: " + player); + } + } + + /** + * Removes all players on the server now from cache and saves their info + */ + public void removeAllPlayers() { + playerCache.clear(); + } + + /* + * Player info query methods + */ + /* + public void setInGreenhouse(Player player, Greenhouse inGreenhouse) { + if (playerCache.containsKey(player.getUniqueId())) { + playerCache.get(player.getUniqueId()).setInGreenhouse(inGreenhouse); + } + } +*/ + /** + * @param playerUUID + * @return the greenhouse the player is in or null if no greenhouse + */ + public Greenhouse getInGreenhouse(Player player) { + for (Greenhouse g : plugin.getGreenhouses()) { + if (g.insideGreenhouse(player.getLocation())) { + return g; + } + } + return null; + } + + /** + * Increments the player's greenhouse count if permissions allow + * @param player + * @return true if successful, otherwise false + */ + public boolean incGreenhouseCount(Player player) { + // Do a permission check if there are limits + // Check permission limits on number of greenhouses + int limit = plugin.getMaxGreenhouses(player); // 0 = none allowed. Positive numbers = limit. Negative = unlimited + if (limit < 0 || playerCache.get(player.getUniqueId()).getNumberOfGreenhouses() < limit) { + playerCache.get(player.getUniqueId()).incrementGreenhouses(); + return true; + } + + // At the limit, sorry + return false; + } + + /** + * Decrements the number of greenhouses this player has + * @param player + */ + public void decGreenhouseCount(Player player) { + playerCache.get(player.getUniqueId()).decrementGreenhouses(); + } + + /** + * Decrements by UUID + * @param playerUUID + */ + public void decGreenhouseCount(UUID playerUUID) { + if (playerCache.containsKey(playerUUID)) { + playerCache.get(playerUUID).decrementGreenhouses(); + } + } + + + /** + * Returns true if the player is at their permitted limit of greenhouses otherwise false + * @param player + * @return + */ + public boolean isAtLimit(Player player) { + if (getRemainingGreenhouses(player) == 0) { + return true; + } + return false; + } + + /** + * Returns how many greenhouses the player is allowed to make + * @param player + * @return + */ + public int getRemainingGreenhouses(Player player) { + int limit = plugin.getMaxGreenhouses(player); + if (limit < 0) { + return -1; + } + int size = 0; + if (plugin.getPlayerhouses().containsKey(player.getUniqueId())) { + size = plugin.getPlayerhouses().get(player.getUniqueId()).size(); + } + int remaining = limit - size; + if (remaining < 0) { + return 0; + } else { + return remaining; + } + + } +} + + + diff --git a/src/main/java/world/bentobox/greenhouses/Players.java b/src/main/java/world/bentobox/greenhouses/Players.java new file mode 100644 index 0000000..51390ed --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/Players.java @@ -0,0 +1,70 @@ +package world.bentobox.greenhouses; + +import java.util.UUID; + +import org.bukkit.entity.Player; + +import world.bentobox.greenhouses.greenhouse.Greenhouse; + +/** + * Tracks the following info on the player + * UUID, player name, whether they are in a greenhouse or not and how many greenhouses they have + */ +public class Players { + private UUID uuid; + private Greenhouse inGreenhouse; + private int numberOfGreenhouses; + + /** + * @param uuid + * Constructor - initializes the state variables + * + */ + public Players(final Player player) { + this.uuid = player.getUniqueId(); + // We do not know if the player is in a greenhouse or not yet + this.inGreenhouse = null; + // We start with the assumption they have no greenhouses yet + this.numberOfGreenhouses = 0; + } + + /** + * @return the inGreenhouse + */ + public Greenhouse getInGreenhouse() { + return inGreenhouse; + } + + /** + * @param inGreenhouse the inGreenhouse to set + */ + public void setInGreenhouse(Greenhouse inGreenhouse) { + this.inGreenhouse = inGreenhouse; + } + + /** + * @return the numberOfGreenhouses + */ + public int getNumberOfGreenhouses() { + return numberOfGreenhouses; + } + + public void incrementGreenhouses() { + numberOfGreenhouses++; + } + + public void decrementGreenhouses() { + numberOfGreenhouses--; + if (numberOfGreenhouses < 0) { + numberOfGreenhouses = 0; + } + } + + /** + * @return the uuid + */ + public UUID getUuid() { + return uuid; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/Settings.java b/src/main/java/world/bentobox/greenhouses/Settings.java new file mode 100644 index 0000000..ee23c8f --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/Settings.java @@ -0,0 +1,25 @@ +package world.bentobox.greenhouses; + +import java.util.List; + + +/** + * @author ben + * Where all the settings are + */ +public class Settings { + // General + public static List worldName; + public static double snowChanceGlobal; + public static double snowDensity; + public static long snowSpeed; + public static int plantTick; + public static int iceInfluence; + public static int blockTick; + public static int mobTick; + public static int ecoTick; + public static boolean allowFlowIn; + public static boolean allowFlowOut; + public static boolean deleteExtras; + public static int maxGreenhouses; +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java b/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java new file mode 100644 index 0000000..a5334e8 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java @@ -0,0 +1,434 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +import world.bentobox.bentobox.util.Util; +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.ui.Locale; + +public class BiomeRecipe { + private Greenhouses plugin; + private Biome type; + private Material icon; // Biome icon for control panel + private int priority; + private String name; + private String friendlyName; + + private final List ADJ_BLOCKS = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST); + + // Content requirements + // Material, Type, Qty. There can be more than one type of material required + private Map requiredBlocks = new HashMap<>(); + // Plants + private TreeMap plantTree = new TreeMap<>(); + + // Mobs + // Entity Type, Material to Spawn on, Probability + private TreeMap mobTree = new TreeMap<>(); + + // Conversions + // Original Material, Original Type, New Material, New Type, Probability + private Map conversionBlocks = new HashMap<>(); + + private int mobLimit; + private int waterCoverage; + private int iceCoverage; + private int lavaCoverage; + + private String permission = ""; + private Random random = new Random(); + + /** + * @param type + * @param priority + */ + public BiomeRecipe(Greenhouses plugin, Biome type, int priority) { + this.plugin = plugin; + this.type = type; + this.priority = priority; + plugin.logger(3,"" + type.toString() + " priority " + priority); + mobLimit = 9; // Default + } + + /** + * @param oldMaterial - material that will convert + * @param newMaterial - what it will convert to + * @param convChance - percentage chance + * @param localMaterial - what material must be next to it for conversion to happen + */ + public void addConvBlocks(Material oldMaterial, Material newMaterial, double convChance, Material localMaterial) { + double probability = Math.min(convChance/100 , 1D); + conversionBlocks.put(oldMaterial, new GreenhouseBlockConversions(oldMaterial, newMaterial, probability, localMaterial)); + plugin.logger(1," " + convChance + "% chance for " + Util.prettifyText(oldMaterial.toString()) + " to convert to " + Util.prettifyText(newMaterial.toString())); + } + + + /** + * @param mobType + * @param mobProbability + * @param mobSpawnOn + */ + public void addMobs(EntityType mobType, int mobProbability, Material mobSpawnOn) { + plugin.logger(1," " + mobProbability + "% chance for " + Util.prettifyText(mobType.toString()) + " to spawn on " + Util.prettifyText(mobSpawnOn.toString())+ "."); + double probability = ((double)mobProbability/100); + // Add up all the probabilities in the list so far + if ((1D - mobTree.lastKey()) >= probability) { + // Add to probability tree + mobTree.put(mobTree.lastKey() + probability, new GreenhouseMob(mobType, mobSpawnOn)); + } else { + plugin.logError("Mob chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + mobType.toString()); + } + } + + /** + * Creates a list of plants that can grow, the probability and what they must grow on. + * Data is drawn from the file biomes.yml + * @param plantMaterial - plant type + * @param plantProbability - probability of growing + * @param plantGrowOn - material on which it must grow + */ + public void addPlants(Material plantMaterial, int plantProbability, Material plantGrowOn) { + double probability = ((double)plantProbability/100); + // Add up all the probabilities in the list so far + if ((1D - plantTree.lastKey()) >= probability) { + // Add to probability tree + plantTree.put(plantTree.lastKey() + probability, new GreenhousePlant(plantMaterial, plantGrowOn)); + } else { + plugin.logError("Plant chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + plantMaterial.toString()); + } + plugin.logger(1," " + plantProbability + "% chance for " + Util.prettifyText(plantMaterial.toString()) + " to grow on " + Util.prettifyText(plantGrowOn.toString())); + } + + /** + * @param blockMaterial + * @param blockQty + */ + public void addReqBlocks(Material blockMaterial, int blockQty) { + requiredBlocks.put(blockMaterial, blockQty); + plugin.logger(1," " + blockMaterial + " x " + blockQty); + } + + // Check required blocks + /** + * Checks greenhouse meets recipe requirements. If player is not null, a explanation of + * any failures will be provided. + * @param pos1 + * @param pos2 + * @param player + * @return true if a cube defined by pos1 and pos2 meet this biome recipe. + */ + public boolean checkRecipe(Location pos1, Location pos2, Player player) { + plugin.logger(3,"Checking for biome " + type.toString()); + long area = (pos2.getBlockX()-pos1.getBlockX()-1) * (pos2.getBlockZ()-pos1.getBlockZ()-1); + plugin.logger(3,"area =" + area); + plugin.logger(3,"Pos1 = " + pos1.toString()); + plugin.logger(3,"Pos1 = " + pos2.toString()); + boolean pass = true; + Map blockCount = new HashMap<>(); + // Look through the greenhouse and count what is in there + for (int y = pos1.getBlockY(); y en.getKey().equals(Material.ICE) + || en.getKey().equals(Material.BLUE_ICE) + || en.getKey().equals(Material.PACKED_ICE)) + .mapToInt(Map.Entry::getValue).sum(); + double iceRatio = (double)ice/(double)area * 100; + plugin.logger(3,"water req=" + waterCoverage + " lava req=" + lavaCoverage + " ice req="+iceCoverage); + plugin.logger(3,"waterRatio=" + waterRatio + " lavaRatio=" + lavaRatio + " iceRatio="+iceRatio); + + + // Check required ratios - a zero means none of these are allowed, e.g.desert has no water + if (waterCoverage == 0 && waterRatio > 0) { + if (player != null) { + player.sendMessage(ChatColor.RED + Locale.recipenowater); + } + pass=false; + } + if (lavaCoverage == 0 && lavaRatio > 0) { + if (player != null) { + player.sendMessage(ChatColor.RED + Locale.recipenolava); + } + pass=false; + } + if (iceCoverage == 0 && iceRatio > 0) { + if (player != null) { + player.sendMessage(ChatColor.RED + Locale.recipenoice); + } + pass=false; + } + if (waterCoverage > 0 && waterRatio < waterCoverage) { + if (player != null) { + player.sendMessage(ChatColor.RED + Locale.recipewatermustbe.replace("[coverage]", String.valueOf(waterCoverage))); + } + pass=false; + } + if (lavaCoverage > 0 && lavaRatio < lavaCoverage) { + if (player != null) { + player.sendMessage(ChatColor.RED + Locale.recipelavamustbe.replace("[coverage]", String.valueOf(lavaCoverage))); + } + pass=false; + + } + if (iceCoverage > 0 && iceRatio < iceCoverage) { + if (player != null) { + player.sendMessage(ChatColor.RED + Locale.recipeicemustbe.replace("[coverage]", String.valueOf(iceCoverage))); + } + pass=false; + } + // Compare to the required blocks + Map missingBlocks = requiredBlocks.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() - blockCount.getOrDefault(e.getKey(), 0))); + // Remove any entries that are 0 or less + missingBlocks.values().removeIf(v -> v <= 0); + if (!missingBlocks.isEmpty()) { + pass = false; + } + missingBlocks.forEach((k,v) -> player.sendMessage(ChatColor.RED + Locale.recipemissing + " " + Util.prettifyText(k.toString()) + " x " + v)); + return pass; + } + + /** + * @param b + */ + public void convertBlock(Block b) { + plugin.logger(3,"try to convert block"); + GreenhouseBlockConversions bc = conversionBlocks.get(b.getType()); + if (bc == null || random.nextDouble() > bc.getProbability()) { + return; + } + // Check if the block is in the right area, up, down, n,s,e,w + if (ADJ_BLOCKS.stream().map(b::getRelative).map(Block::getType).anyMatch(m -> bc.getLocalMaterial() == null || m == bc.getLocalMaterial())) { + // Convert! + plugin.logger(3,"Convert block"); + b.setType(bc.getNewMaterial()); + } + } + + /** + * @return the type + */ + public Biome getBiome() { + return type; + } + + /** + * @return true if there are blocks to convert for this biome + */ + public boolean getBlockConvert() { + return !conversionBlocks.isEmpty(); + } + + /** + * @return the friendly name + */ + public String getFriendlyName() { + return friendlyName; + } + + /** + * @return the iceCoverage + */ + public int getIceCoverage() { + return iceCoverage; + } + + /** + * @return the icon + */ + public Material getIcon() { + return icon; + } + + /** + * @return the lavaCoverage + */ + public int getLavaCoverage() { + return lavaCoverage; + } + + /** + * @return the mobLimit + */ + public int getMobLimit() { + return mobLimit; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @return the permission + */ + public String getPermission() { + return permission; + } + + /** + * @return the priority + */ + public int getPriority() { + return priority; + } + + /** + * @return a mob that can spawn in the greenhouse + */ + public Optional getRandomMob() { + // Return a random mob that can spawn in the biome or empty + Double key = mobTree.ceilingKey(random.nextDouble()); + return key == null ? Optional.empty() : Optional.ofNullable(mobTree.get(key)); + } + + private Optional getRandomPlant() { + // Grow a random plant that can grow + Double key = plantTree.ceilingKey(random.nextDouble()); + return key == null ? Optional.empty() : Optional.ofNullable(plantTree.get(key)); + + } + + /** + * @return a list of blocks that are required for this recipe + */ + public List getRecipeBlocks() { + return requiredBlocks.entrySet().stream().map(en -> Util.prettifyText(en.getKey().toString()) + " x " + en.getValue()).collect(Collectors.toList()); + } + + /** + * @return the waterCoverage + */ + public int getWaterCoverage() { + return waterCoverage; + } + + /** + * Plants a plant on block bl if it makes sense. + * @param bl + * @return + */ + public void growPlant(Block bl) { + if (bl.getType() != Material.AIR) { + return; + } + getRandomPlant().ifPresent(p -> { + if (bl.getY() != 0 && p.getPlantGrownOn().map(m -> m.equals(bl.getRelative(BlockFace.DOWN).getType())).orElse(true)) { + bl.setType(p.getPlantMaterial()); + } + }); + } + + /** + * @param set the friendly name + */ + public void setFriendlyName(String friendlyName) { + this.friendlyName = friendlyName; + } + /** + * @param icecoverage the icecoverage to set + */ + public void setIcecoverage(int icecoverage) { + if (icecoverage == 0) { + plugin.logger(1," No Ice Allowed"); + } else if (icecoverage > 0) { + plugin.logger(1," Ice > " + icecoverage + "%"); + } + this.iceCoverage = icecoverage; + } + + /** + * @param icon the icon to set + */ + public void setIcon(Material icon) { + this.icon = icon; + } + + /** + * @param lavaCoverage the lavaCoverage to set + */ + public void setLavacoverage(int lavacoverage) { + if (lavacoverage == 0) { + plugin.logger(1," No Lava Allowed"); + } else if (lavacoverage > 0) { + plugin.logger(1," Lava > " + lavacoverage + "%"); + } + this.lavaCoverage = lavacoverage; + } + + /** + * @param mobLimit the mobLimit to set + */ + public void setMobLimit(int mobLimit) { + this.mobLimit = mobLimit; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @param permission the permission to set + */ + public void setPermission(String permission) { + this.permission = permission; + } + + /** + * @param priority the priority to set + */ + public void setPriority(int priority) { + this.priority = priority; + } + + /** + * @param type the type to set + */ + public void setType(Biome type) { + this.type = type; + } + + /** + * @param waterCoverage the waterCoverage to set + */ + public void setWatercoverage(int watercoverage) { + if (watercoverage == 0) { + plugin.logger(1," No Water Allowed"); + } else if (watercoverage > 0) { + plugin.logger(1," Water > " + watercoverage + "%"); + } + this.waterCoverage = watercoverage; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/Ecosystem.java b/src/main/java/world/bentobox/greenhouses/greenhouse/Ecosystem.java new file mode 100644 index 0000000..16400d4 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/Ecosystem.java @@ -0,0 +1,144 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.Hopper; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.weather.WeatherChangeEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitTask; + +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.Settings; + +/** + * Monitors the greenhouses and grows things, adds weather etc. + * @author ben + * + */ +public class Ecosystem implements Listener { + private final Greenhouses addon; + private final static List SNOWBIOMES = Arrays.stream(Biome.values()).filter(b -> b.name().contains("COLD") || b.name().contains("ICE") || b.name().contains("FROZEN")).collect(Collectors.toList()); + private BukkitTask snow; + private List snowGlobes = new ArrayList(); + + public Ecosystem(final Greenhouses plugin) { + this.addon = plugin; + } + + @EventHandler + public void onWeatherChangeEvent(final WeatherChangeEvent e) { + if (!Settings.worldName.contains(e.getWorld().getName())) { + return; + } + if (e.toWeatherState()) { + // It's raining + addon.logger(3,"It's raining!"); + startSnow(); + } else { + // It's stopped raining! + addon.logger(3,"Stopped raining!"); + if (snow != null) + snow.cancel(); + } + } + + private void startSnow() { + if (snow != null) { + // Cancel the old snow task + snow.cancel(); + } + + // Spin off scheduler + snowGlobes.clear(); + snow = addon.getServer().getScheduler().runTaskTimer(addon.getPlugin(), () -> { + + // Run through each greenhouse - only bother with snow biomes + addon.logger(3,"started scheduler"); + // Check all the greenhouses and their hoppers and build a list of snow greenhouses that exist now + List toBeRemoved = new ArrayList(); + for (Greenhouse g : addon.getGreenhouses()) { + addon.logger(3,"Testing greenhouse biome : " + g.getBiome().toString()); + if (SNOWBIOMES.contains(g.getBiome())) { + addon.logger(3,"Snow biome found!"); + // If it is already on the list, just snow, otherwise check if the hopper has water + if (!snowGlobes.contains(g)) { + Location hopper = g.getRoofHopperLocation(); + if (hopper != null) { + addon.logger(3,"Hopper location:" + hopper.toString()); + Block b = hopper.getBlock(); + // Check the hopper is still there + if (b.getType().equals(Material.HOPPER)) { + Hopper h = (Hopper)b.getState(); + addon.logger(3,"Hopper found!"); + // Check what is in the hopper + if (h.getInventory().contains(Material.WATER_BUCKET)) { + // Remove the water in the bucket + // We cannot remove an itemstack the easy way because on acid island the bucket is changed to acid + for (ItemStack item : h.getInventory().getContents()) { + if (item != null && item.getType().equals(Material.WATER_BUCKET)) { + // Remove one from this item stack + // Water buckets in theory do no stack... + ItemStack i = item.clone(); + i.setAmount(1); + h.getInventory().removeItem(i); + h.getInventory().addItem(new ItemStack(Material.BUCKET)); + break; + } + } + // Add to list + snowGlobes.add(g); + } + } else { + // Greenhouse is broken or no longer has a hopper when it should + addon.getLogger().warning("Hopper missing from greenhouse at " + g.getRoofHopperLocation().getBlockX() + " " + + g.getRoofHopperLocation().getBlockY() + " " + g.getRoofHopperLocation().getBlockZ()); + addon.getLogger().warning("Removing greenhouse"); + toBeRemoved.add(g); + } + } + } + } + } + if (!snowGlobes.isEmpty()) { + snowOn(snowGlobes); + } + // Remove any greenhouses that need it + for (Greenhouse g : toBeRemoved) { + //UUID owner = g.getOwner(); + addon.removeGreenhouse(g); + //plugin.players.save(owner); + } + addon.saveGreenhouses(); + }, 0L, (Settings.snowSpeed * 20L)); // Every 30 seconds + + } + + protected void snowOn(List snowGlobes) { + for (Greenhouse g : snowGlobes) { + addon.logger(3,"snowing in a greenhouse"); + // Chance of snow + if (Math.random()>Settings.snowChanceGlobal) + return; + g.snow(); + } + } + + /** + * Removes any greenhouses that are currently in the eco system + * @param g + */ + public void remove(Greenhouse g) { + if (snowGlobes.contains(g)) + snowGlobes.remove(g); + } + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/Greenhouse.java b/src/main/java/world/bentobox/greenhouses/greenhouse/Greenhouse.java new file mode 100644 index 0000000..5e6fe4d --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/Greenhouse.java @@ -0,0 +1,606 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Hopper; +import org.bukkit.block.data.type.Snow; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +import world.bentobox.bentobox.util.Pair; +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.Settings; + +/** + * Defines a greenhouse + * @author tastybento + * + */ +public class Greenhouse { + private Greenhouses plugin; + private final Location pos1; + private final Location pos2; + private final World world; + private UUID owner; + private String playerName; + private HashMap flags = new HashMap(); + private Biome originalBiome; + private Biome greenhouseBiome; + private Location roofHopperLocation; + private int area; + private int heightY; + private int height; + private int groundY; + private BiomeRecipe biomeRecipe; + + + public Greenhouse(Greenhouses plugin, Location pos1, Location pos2, UUID owner) { + this.plugin = plugin; + this.pos1 = pos1; + this.pos2 = pos2; + int minx = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int maxx = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int minz = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxz = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + this.area = (maxx-minx - 1) * (maxz-minz -1); + this.heightY = Math.max(pos1.getBlockY(), pos2.getBlockY()); // Should always be pos2 is higher, but just in case + this.groundY = Math.min(pos1.getBlockY(), pos2.getBlockY()); + this.height = heightY - groundY; + this.world = pos1.getWorld(); + if (pos1.getWorld() == null || pos2.getWorld() == null) { + plugin.getLogger().severe("This greenhouse's world does not exist!"); + } else { + if (!pos1.getWorld().equals(pos2.getWorld())) { + plugin.getLogger().severe("Pos 1 and Pos 2 are not in the same world!"); + } + } + this.originalBiome = pos1.getBlock().getBiome(); + this.greenhouseBiome = pos2.getBlock().getBiome(); + this.owner = owner; + this.playerName = ""; + flags.put("enterMessage", ""); + flags.put("farewellMessage", ""); + } + + + /** + * @return the originalBiome + */ + public Biome getOriginalBiome() { + return originalBiome; + } + + + /** + * @return the greenhouseBiome + */ + public Biome getBiome() { + return greenhouseBiome; + } + + + /** + * @param winner.getType() the greenhouseBiome to set + */ + public void setBiomeRecipe(BiomeRecipe winner) { + this.greenhouseBiome = winner.getBiome(); + this.biomeRecipe = winner; + } + + /** + * @return the greenhouse's biome recipe + */ + public BiomeRecipe getBiomeRecipe() { + return biomeRecipe; + } + + public void setBiome(Biome greenhouseBiome2) { + this.greenhouseBiome = greenhouseBiome2; + } + + + public boolean insideGreenhouse(Location loc) { + if (loc.getWorld().equals(world)) { + plugin.logger(4,"Checking intersection"); + Vector v = loc.toVector(); + plugin.logger(4,"Pos 1 = " + pos1.toString()); + plugin.logger(4,"Pos 2 = " + pos2.toString()); + plugin.logger(4,"V = " + v.toString()); + boolean i = v.isInAABB(Vector.getMinimum(pos1.toVector(), pos2.toVector()), Vector.getMaximum(pos1.toVector(), pos2.toVector())); + return i; + } + return false; + } + + /** + * Check to see if a location is above a greenhouse + * @param loc + * @return + */ + public boolean aboveGreenhouse(Location loc) { + if (loc.getWorld().equals(world)) { + Vector v = loc.toVector(); + Vector p1 = new Vector(pos1.getBlockX(),heightY,pos1.getBlockZ()); + Vector p2 = new Vector(pos2.getBlockX(),world.getMaxHeight(),pos2.getBlockZ()); + boolean i = v.isInAABB(Vector.getMinimum(p1, p2), Vector.getMaximum(p1, p2)); + return i; + } + return false; + } + + + /** + * Returns true if this location is in a greenhouse wall + * @param loc + * @return + */ + public boolean isAWall(Location loc) { + plugin.logger(3,"wall check"); + if (loc.getBlockX() == pos1.getBlockX() || loc.getBlockX() == pos2.getBlockX() + || loc.getBlockZ() == pos1.getBlockZ() || loc.getBlockZ() == pos2.getBlockZ()) { + return true; + } + return false; + } + + /** + * @return the pos1 + */ + public Location getPos1() { + return new Location (world, pos1.getBlockX(), pos1.getBlockY(), pos1.getBlockZ()); + } + + /** + * @return the pos2 + */ + public Location getPos2() { + return new Location (world, pos2.getBlockX(), pos2.getBlockY(), pos2.getBlockZ()); + + } + + /** + * @return the owner + */ + public UUID getOwner() { + return owner; + } + + /** + * @return the flags + */ + public HashMap getFlags() { + return flags; + } + + + /** + * @param flags the flags to set + */ + public void setFlags(HashMap flags) { + this.flags = flags; + } + + + + /** + * @param owner the owner to set + */ + public void setOwner(UUID owner) { + this.owner = owner; + } + + /** + * @return the playerName + */ + public String getPlayerName() { + return playerName; + } + + + /** + * @param playerName the playerName to set + */ + public void setPlayerName(String playerName) { + //plugin.getLogger().info("DEBUG: setting name to " + playerName); + this.playerName = playerName; + } + + + /** + * @return the enterMessage + */ + public String getEnterMessage() { + return (String)flags.get("enterMessage"); + } + + + /** + * @return the farewallMessage + */ + public String getFarewellMessage() { + return (String)flags.get("farewellMessage"); + } + + + /** + * @param enterMessage the enterMessage to set + */ + public void setEnterMessage(String enterMessage) { + flags.put("enterMessage",enterMessage); + } + + + /** + * @param farewallMessage the farewallMessage to set + */ + public void setFarewellMessage(String farewellMessage) { + flags.put("farewellMessage",farewellMessage); + } + + + public void setOriginalBiome(Biome originalBiome) { + this.originalBiome = originalBiome; + } + + + public void setRoofHopperLocation(Location roofHopperLoc) { + this.roofHopperLocation = roofHopperLoc; + + } + + + /** + * @return the world + */ + public World getWorld() { + return world; + } + + + public Location getRoofHopperLocation() { + return roofHopperLocation; + } + + /** + * @return the area + */ + public int getArea() { + return area; + } + + + /** + * @return the heightY + */ + public int getHeightY() { + return heightY; + } + + + /** + * @return the height + */ + public int getHeight() { + return height; + } + + + /** + * Reruns the recipe check to see if this greenhouse is still viable + * @return true if okay, otherwise false + */ + public boolean checkEco() { + plugin.logger(3,"Checking the ecology of the greenhouse."); + if (biomeRecipe != null) { + return this.biomeRecipe.checkRecipe(getPos1(), getPos2(), null); + } else { + plugin.logger(3,"BiomeRecipe is null! "); + plugin.getLogger().warning("[Greenhouse info]"); + plugin.getLogger().warning("Owner: " + getOwner()); + plugin.getLogger().warning("Location :" + getPos1().toString() + " to " + getPos2().toString()); + return false; + } + } + + + /** + * Starts the biome in the greenhouse + * @param teleport - if true will teleport the player away and back to force the biome to change + */ + public void startBiome(boolean teleport) { + setBiomeBlocks(greenhouseBiome, teleport); + } + + /** + * Reverts the biome of the greenhouse to the original unless someone is in this greenhouse + * @param to + */ + public void endBiome() { + setBiomeBlocks(originalBiome, false); + } + + + /** + * Actually set blocks to a biome + * The chunk refresh command has been deprecated and no longer works on 1.8+ + * so jumping through hoops to refresh mobs is no longer needed + * If teleport is true, this biome starting is happening during a teleport + * sequence, i.e, gh is being generated or removed + * @param biome + * @param teleport + */ + private void setBiomeBlocks(Biome biome, boolean teleport) { + if (biome == null) { + return; + } + plugin.logger(2,"Biome seting to " + biome.toString()); + Set> chunkCoords = new HashSet<>(); + final Set chunks = new HashSet(); + for (int x = pos1.getBlockX();x(b.getChunk().getX(), b.getChunk().getZ())); + } + } + } + } + + /** + * Spawns friendly mobs according to the type of biome + */ + public void populateGreenhouse() { + if (biomeRecipe == null) { + return; + } + plugin.logger(3,"populating mobs in greenhouse"); + // Make sure no players are around + /* + if (plugin.players.getNumberInGreenhouse(this) > 0) + return; + */ + // Quick check - see if any animal is going to spawn + EntityType mob = biomeRecipe.getRandomMob(); + if (mob == null) { + return; + } + plugin.logger(3,"Mob ready to spawn in location " + pos1.getBlockX() + "," + pos2.getBlockZ() + " in world " + world.getName()); + // Load greenhouse chunks + int numberOfMobs = 0; + Set chunkSet = new HashSet(); + for (int x = (pos1.getBlockX()-15); x < (pos2.getBlockX()+15); x += 16) { + for (int z = (pos1.getBlockZ()-15); z < (pos2.getBlockZ()+15); z += 16) { + Chunk chunk = world.getChunkAt(x/16, z/16); + chunkSet.add(new Pair(x/16,z/16)); + chunk.load(); + plugin.logger(3, "Chunk = " + (x/16) + "," + (z/16) + " number of entities = " + chunk.getEntities().length); + for (Entity entity : chunk.getEntities()) { + plugin.logger(3,entity.getType().toString()); + if (mob.equals(entity.getType())) { + numberOfMobs++; + } + } + } + } + plugin.logger(3,"Mobs in area = " + numberOfMobs); + plugin.logger(3,"Area of greenhouse = " + area); + if (area - (numberOfMobs * biomeRecipe.getMobLimit()) <= 0) { + plugin.logger(3,"Too many mobs already in this greenhouse"); + for (Pair pair: chunkSet) { + world.unloadChunkRequest(pair.getLeft(), pair.getRight()); + } + return; + } + Material type = biomeRecipe.getMobSpawnOn(mob); + int minx = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int maxx = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int minz = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxz = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + // Try 10 times + for (int i = 0; i<10; i++) { + int x = Greenhouses.randInt(minx,maxx); + int z = Greenhouses.randInt(minz,maxz); + Block h = getHighestBlockInGreenhouse(x,z); + Block b = h.getRelative(BlockFace.DOWN); + Block a = h.getRelative(BlockFace.UP); + plugin.logger(3,"block found " + h.getType().toString()); + plugin.logger(3,"below found " + b.getType().toString()); + plugin.logger(3,"above found " + a.getType().toString()); + if ((b.getType().equals(type) && h.getType().equals(Material.AIR)) + || (h.getType().equals(type) && a.getType().equals(Material.AIR)) ) { + Location midBlock = new Location(world, h.getLocation().getX()+0.5D, h.getLocation().getY(), h.getLocation().getZ()+0.5D); + Entity e = world.spawnEntity(midBlock, mob); + if (e != null) + plugin.logger(1,"Spawned a "+ Util.prettifyText(mob.toString()) + " on "+ Util.prettifyText(type.toString()) + " at " + + midBlock.getBlockX() + "," + midBlock.getBlockY() + "," + midBlock.getBlockZ()); + break; + } + } + for (Pair pair: chunkSet) { + world.unloadChunkRequest(pair.getLeft(), pair.getRight()); + } + } + + /** + * Lay down snow in the greenhouse + */ + public void snow() { + // Lay down snow and ice + List waterToIceBlocks = new ArrayList(); + long water = 0; + int minx = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int maxx = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int minz = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxz = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + for (int x = minx+1; x < maxx; x++) { + for (int z = minz+1; z < maxz;z++) { + Block b = getHighestBlockInGreenhouse(x,z); + // Display snow particles in air above b and count water blocks + for (int y = pos1.getBlockY(); y < heightY; y++) { + Block airCheck = world.getBlockAt(x, y, z); + if (airCheck.getType().equals(Material.AIR)) { + //).display(0F,0F,0F, 0.1F, 5, + //ParticleEffects.SNOWBALL.send(Bukkit.getOnlinePlayers(),airCheck.getLocation(),0D,0D,0D,1F,5,20); + world.spawnParticle(Particle.SNOWBALL, airCheck.getLocation(), 5); + } else if (airCheck.getType().equals(Material.WATER)) { + water++; + } + } + Block belowB = b.getRelative(BlockFace.DOWN); + if (Math.random() 0) { + waterToIceBlocks.add(belowB); + } else { + belowB.setType(Material.ICE); + } + } else if (belowB.getType().equals(Material.SNOW)) { + Snow snow = (Snow)belowB.getBlockData(); + // Increment snow layers + snow.setLayers(Math.min(snow.getLayers() + 1, snow.getMaximumLayers())); + } else if (b.getType().equals(Material.AIR)) { + // Don't put snow on liquids or snow... + if (!belowB.isLiquid() && !belowB.getType().equals(Material.SNOW)) + b.setType(Material.SNOW); + } + } + } + } + plugin.logger(3,"water = " + water); + if (biomeRecipe.getWaterCoverage() > 0 && water >0) { + plugin.logger(3,"water coverage required = " + biomeRecipe.getWaterCoverage()); + // Check if ice can be made + for (Block toIce: waterToIceBlocks) { + plugin.logger(3,"water ratio = " + ((double)(water-1)/(double)area * 100)); + if (((double)(water-1)/(double)area * 100) > biomeRecipe.getWaterCoverage()) { + toIce.setType(Material.ICE); + water--; + } else { + plugin.logger(3,"no more ice allowed"); + break; + } + } + } + } + + public void growFlowers() { + if (biomeRecipe == null) { + return; + } + Location hopper = roofHopperLocation; + if (hopper != null) { + plugin.logger(3,"Hopper location:" + hopper.toString()); + Block b = hopper.getBlock(); + // Check the hopper is still there + if (b.getType().equals(Material.HOPPER)) { + Hopper h = (Hopper)b.getState(); + plugin.logger(3,"Hopper found!"); + // Check what is in the hopper + if (h.getInventory().contains(Material.BONE_MEAL)) { + int total = h.getInventory().all(Material.BONE_MEAL).values().stream().mapToInt(ItemStack::getAmount).sum(); + // We need one bonemeal for each flower made + if (total > 0) { + /* + ItemStack remBoneMeal = new ItemStack(Material.INK_SACK); + remBoneMeal.setDurability((short)15); + remBoneMeal.setAmount(1); + */ + // Rewrite to use on bonemeal per flower + plugin.logger(3,"Bonemeal found! Amount = " + total); + // Now go and grow stuff with the set probability + int minx = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int maxx = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int minz = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxz = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + for (int x = minx+1; x < maxx; x++) { + for (int z = minz+1; z < maxz;z++) { + Block bl = getHighestBlockInGreenhouse(x,z); + //if (Math.random() 0 && biomeRecipe.growPlant(bl)) { + total --; + plugin.logger(3,"Grew plant, spraying bonemeal"); + // Spray the bonemeal + for (int y = bl.getLocation().getBlockY(); y< heightY; y++) { + Block airCheck = world.getBlockAt(x, y, z); + if (airCheck.getType().equals(Material.AIR)) { + world.spawnParticle(Particle.EXPLOSION_NORMAL, airCheck.getLocation(), 5); + //ParticleEffects.EXPLOSION_NORMAL.send(Bukkit.getOnlinePlayers(),airCheck.getLocation(),0D,0D,0D,1F,5,20); + //ParticleEffect.EXPLOSION_NORMAL.display(0F,0F,0F, 0.1F, 5, airCheck.getLocation(), 30D); + } + } + } + } + } + plugin.logger(3,"Bonemeal left = " + total); + // Remove the bonemeal from the hopper + h.getInventory().remove(Material.BONE_MEAL); + h.getInventory().addItem(new ItemStack(Material.BONE_MEAL, total)); + } + } + } else { + // Greenhouse is broken or no longer has a hopper when it should + // TODO remove the greenhouse + plugin.logger(3,"Hopper is not there anymore..."); + } + } + } + + + /** + * Converts blocks in the greenhouse over time at a random rate + * Depends on the biome recipe + */ + public void convertBlocks() { + if (biomeRecipe == null) { + return; + } + if (biomeRecipe.getBlockConvert()) { + // Check biome recipe + int minx = Math.min(pos1.getBlockX(), pos2.getBlockX()); + int maxx = Math.max(pos1.getBlockX(), pos2.getBlockX()); + int minz = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); + int maxz = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); + for (int x = minx+1; x < maxx; x++) { + for (int z = minz+1; z < maxz;z++) { + for (int y = groundY; y < heightY; y++) { + biomeRecipe.convertBlock(world.getBlockAt(x,y,z)); + } + } + } + } + } + /** + * Replaces the getHighestBlock function by only looking within a greenhouse + * @param x + * @param z + * @return Non-solid block just above the highest solid block at x,z - should always be AIR + */ + public Block getHighestBlockInGreenhouse(int x, int z) { + Block bl = world.getBlockAt(x,heightY,z).getRelative(BlockFace.DOWN); + while (bl.getLocation().getBlockY() >= groundY && bl.isEmpty()) { + bl = bl.getRelative(BlockFace.DOWN); + } + return bl.getRelative(BlockFace.UP); + } +} diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseBlockConversions.java b/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseBlockConversions.java new file mode 100644 index 0000000..e65b6b3 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseBlockConversions.java @@ -0,0 +1,42 @@ +package world.bentobox.greenhouses.greenhouse; + +import org.bukkit.Material; + +public class GreenhouseBlockConversions { + private final Material oldMaterial; + private final Material newMaterial; + private final double probability; + private final Material localMaterial; + + public GreenhouseBlockConversions(Material oldMaterial, Material newMaterial, double probability, Material localMaterial) { + this.oldMaterial = oldMaterial; + this.newMaterial = newMaterial; + this.probability = probability; + this.localMaterial = localMaterial; + } + /** + * @return the oldMaterial + */ + public Material getOldMaterial() { + return oldMaterial; + } + /** + * @return the newMaterial + */ + public Material getNewMaterial() { + return newMaterial; + } + /** + * @return the probability + */ + public double getProbability() { + return probability; + } + /** + * @return the localMaterial + */ + public Material getLocalMaterial() { + return localMaterial; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseMob.java b/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseMob.java new file mode 100644 index 0000000..b928c8c --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhouseMob.java @@ -0,0 +1,31 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.Optional; + +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + +public class GreenhouseMob { + private final EntityType mobType; + private final Material mobSpawnOn; + /** + * @param mobType - entity type of mob + * @param mobSpawnOn - material on which it much spawn, or null if any + */ + public GreenhouseMob(EntityType mobType, Material mobSpawnOn) { + this.mobType = mobType; + this.mobSpawnOn = mobSpawnOn; + } + /** + * @return the mobType + */ + public EntityType getMobType() { + return mobType; + } + /** + * @return the mobSpawnOn + */ + public Optional getMobSpawnOn() { + return Optional.of(mobSpawnOn); + } +} diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhousePlant.java b/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhousePlant.java new file mode 100644 index 0000000..3fd8f8b --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/GreenhousePlant.java @@ -0,0 +1,32 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.Optional; + +import org.bukkit.Material; + +public class GreenhousePlant { + private final Material plantMaterial; + private final Material plantGrownOn; + /** + * Describes a recipe plant + * @param plantMaterial - material + * @param plantGrownOn - material on which this grows + */ + public GreenhousePlant(Material plantMaterial,Material plantGrownOn) { + this.plantMaterial = plantMaterial; + this.plantGrownOn = plantGrownOn; + } + /** + * @return the plantMaterial + */ + public Material getPlantMaterial() { + return plantMaterial; + } + /** + * @return the plantGrownOn + */ + public Optional getPlantGrownOn() { + return Optional.ofNullable(plantGrownOn); + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/Roof.java b/src/main/java/world/bentobox/greenhouses/greenhouse/Roof.java new file mode 100644 index 0000000..dc3eb2e --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/Roof.java @@ -0,0 +1,234 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.util.Vector; + +import world.bentobox.greenhouses.Greenhouses; + +/** + * Contains the parameters of a greenhouse roof + * @author tastybento + * + */ +public class Roof { + private int minX; + private int maxX; + private int minZ; + private int maxZ; + private int height; + private boolean roofFound; + private final static List ROOFBLOCKS = Arrays.asList("GLASS","STAINED_GLASS","HOPPER","TRAP_DOOR","IRON_TRAPDOOR","GLOWSTONE"); + + /** + * Finds a roof from a starting location under the roof and characterizes it + * @param loc + */ + public Roof(Greenhouses plugin, Location loc) { + World world = loc.getWorld(); + // This section tries to find a roof block + // Try just going up - this covers every case except if the player is standing under a hole + roofFound = false; + // This does a ever-growing check around the player to find a roof block. It is possible for the player + // to be outside the greenhouse in this situation, so a check is done later to mkae sure the player is inside + //if (!roofFound) { + int roofY = loc.getBlockY(); + // If the roof was not found start going around in circles until something is found + // Expand in ever increasing squares around location until a wall block is found + for (int radius = 0; radius < 100; radius++) { + for (int x = loc.getBlockX() - radius; x <= loc.getBlockX() + radius; x++) { + for (int z = loc.getBlockZ() - radius; z <= loc.getBlockZ() + radius; z++) { + if (!((x > loc.getBlockX() - radius && x < loc.getBlockX() + radius) + && (z > loc.getBlockZ() - radius && z < loc.getBlockZ() + radius))) { + //player.sendBlockChange(new Location(world,x,roofY,z), Material.GLASS, (byte)(radius % 14)); + Block b = world.getBlockAt(x,roofY,z); + plugin.logger(3,"Checking column " + x + " " + z ); + if (!Walls.isWallBlock(b.getType())) { + // Look up + for (int y = roofY; y < world.getMaxHeight(); y++) { + if (ROOFBLOCKS.contains(world.getBlockAt(x,y,z).getType().name())) { + roofFound = true; + loc = new Location(world,x,y,z); + plugin.logger(3,"Roof block found at " + x + " " + y + " " + z + " of type " + loc.getBlock().getType().toString()); + break; + } + } + } + } + if (roofFound) { + break; + } + } + if (roofFound) { + break; + } + } + if (roofFound) { + break; + } + } + //} + // Record the height + this.height = loc.getBlockY(); + // Now we have a roof block, find how far we can go NSWE + minX = loc.getBlockX(); + maxX = loc.getBlockX(); + minZ = loc.getBlockZ(); + maxZ = loc.getBlockZ(); + expandCoords(loc); + int minx = minX; + int maxx = maxX; + int minz = minZ; + int maxz = maxZ; + // Now we have some idea of the mins and maxes, check each block and see if it goes further + do { + plugin.logger(3, "Roof minx=" + minx); + plugin.logger(3, "Roof maxx=" + maxx); + plugin.logger(3, "Roof minz=" + minz); + plugin.logger(3, "Roof maxz=" + maxz); + minx = minX; + maxx = maxX; + minz = minZ; + maxz = maxZ; + for (int x = minx; x <= maxx; x++) { + for (int z = minz; z <= maxz; z++) { + // This will push out the coords if possible + expandCoords(new Location(world, x, loc.getBlockY(), z)); + } + } + // Repeat until nothing changes + } while (minx != minX || maxx != maxX || minz != minZ || maxz != maxZ); + // That's as much as we can do! + } + + + /** + * This takes any location and tries to go as far as possible in NWSE directions finding contiguous roof blocks + * up to 100 in any direction + * @param height + */ + private void expandCoords(Location height) { + Location maxx = height.clone(); + Location minx = height.clone(); + Location maxz = height.clone(); + Location minz = height.clone(); + int limit = 0; + while (ROOFBLOCKS.contains(maxx.getBlock().getType().name()) && limit < 100) { + limit++; + maxx.add(new Vector(1,0,0)); + } + if (maxx.getBlockX()-1 > maxX) { + maxX = maxx.getBlockX()-1; + } + + while (ROOFBLOCKS.contains(minx.getBlock().getType().name()) && limit < 200) { + limit++; + minx.subtract(new Vector(1,0,0)); + } + if (minx.getBlockX() + 1 < minX) { + minX = minx.getBlockX() + 1; + } + + while (ROOFBLOCKS.contains(maxz.getBlock().getType().name()) && limit < 300) { + limit++; + maxz.add(new Vector(0,0,1)); + } + if (maxz.getBlockZ() - 1 > maxZ) { + maxZ = maxz.getBlockZ() - 1; + } + + while (ROOFBLOCKS.contains(minz.getBlock().getType().name()) && limit < 400) { + limit++; + minz.subtract(new Vector(0,0,1)); + } + if (minz.getBlockZ() + 1 < minZ) { + minZ = minz.getBlockZ() + 1; + } + } + /** + * @return the minX + */ + public int getMinX() { + return minX; + } + /** + * @param minX the minX to set + */ + public void setMinX(int minX) { + this.minX = minX; + } + /** + * @return the maxX + */ + public int getMaxX() { + return maxX; + } + /** + * @param maxX the maxX to set + */ + public void setMaxX(int maxX) { + this.maxX = maxX; + } + /** + * @return the minZ + */ + public int getMinZ() { + return minZ; + } + /** + * @param minZ the minZ to set + */ + public void setMinZ(int minZ) { + this.minZ = minZ; + } + /** + * @return the maxZ + */ + public int getMaxZ() { + return maxZ; + } + /** + * @param maxZ the maxZ to set + */ + public void setMaxZ(int maxZ) { + this.maxZ = maxZ; + } + + /** + * @return the area + */ + public int getArea() { + return (maxX - minX) * (maxZ - minZ); + } + /** + * @return the roofFound + */ + public boolean isRoofFound() { + return roofFound; + } + + + /** + * @return the height + */ + public int getHeight() { + return height; + } + + /** + * Check if roof block + * @param blockType + * @return true if this is a roof block + */ + public boolean isRoofBlock(Material blockType) { + if (ROOFBLOCKS.contains(blockType.name())) { + return true; + } + return false; + } +} diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/Walls.java b/src/main/java/world/bentobox/greenhouses/greenhouse/Walls.java new file mode 100644 index 0000000..285f876 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/Walls.java @@ -0,0 +1,274 @@ +package world.bentobox.greenhouses.greenhouse; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.BlockFace; +import org.bukkit.entity.Player; + +import world.bentobox.greenhouses.Greenhouses; + +public class Walls { + private int minX; + private int maxX; + private int minZ; + private int maxZ; + private int floor; + private boolean useRoofMaxX; + private boolean useRoofMinX; + private boolean useRoofMaxZ; + private boolean useRoofMinZ; + private Location roofBlock; + Location roofHopperLoc = null; + public final static List WALL_BLOCKS = Arrays.stream(Material.values()) + .filter(Material::isBlock) // Blocks only, no items + .filter(m -> !m.name().contains("TRAPDOOR")) // No trapdoors + .filter(m -> m.name().contains("DOOR") // All doors + || m.name().contains("GLASS") // All glass blocks + || m.equals(Material.HOPPER) // Hoppers + || m.equals(Material.GLOWSTONE)) // Glowstone + .collect(Collectors.toList()); + + public Walls(Greenhouses addon, final Player player, Roof roof) { + // The player is under the roof + // Assume the player is inside the greenhouse they are trying to create + Location loc = player.getLocation(); + World world = player.getWorld(); + // Find the floor - defined as the last y under the roof where there are no wall blocks + int wallBlockCount = 0; + int y = roof.getHeight(); + do { + wallBlockCount = 0; + for (int x = roof.getMinX(); x <= roof.getMaxX(); x++) { + for (int z = roof.getMinZ(); z <= roof.getMaxZ(); z++) { + if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getType())) { + wallBlockCount++; + } + } + } + + } while( y-- > 0 && wallBlockCount > 0); + floor = y + 1; + addon.logger(3,"#1 Floor found at " + floor); + // Now start with the player's x and z location + int radiusMinX = 0; + int radiusMaxX = 0; + int radiusMinZ = 0; + int radiusMaxZ = 0; + boolean stopMinX = false; + boolean stopMaxX = false; + boolean stopMinZ = false; + boolean stopMaxZ = false; + minX = loc.getBlockX(); + maxX = loc.getBlockX(); + minZ = loc.getBlockZ(); + maxZ = loc.getBlockZ(); + addon.logger(3,"Starting point = " + loc.getBlockX() + "," + loc.getBlockZ()); + addon.logger(3,"roof minX = " + roof.getMinX()); + addon.logger(3,"roof maxX = " + roof.getMaxX()); + addon.logger(3,"roof minZ = " + roof.getMinZ()); + addon.logger(3,"roof maxZ = " + roof.getMaxZ()); + do { + addon.logger(3,"wall radiusminX = " + radiusMinX); + addon.logger(3,"wall radius maxX = " + radiusMaxX); + addon.logger(3,"wall radius minZ = " + radiusMinZ); + addon.logger(3,"wall radius maxZ = " + radiusMaxZ); + + // Look around player in an ever expanding cube + minX = loc.getBlockX() - radiusMinX; + maxX = loc.getBlockX() + radiusMaxX; + minZ = loc.getBlockZ() - radiusMinZ; + maxZ = loc.getBlockZ() + radiusMaxZ; + y = roof.getHeight() - 1; + for (y = roof.getHeight() - 1; y > floor; y--) { + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + // Only look around outside edge + if (!((x > minX && x < maxX) && (z > minZ && z < maxZ))) { + addon.logger(3,"Checking block " + x + " " + y + " " + z); + // Look at block faces + for (BlockFace bf: BlockFace.values()) { + switch (bf) { + case EAST: + // positive x + if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) { + stopMaxX = true; + addon.logger(3,"Wall found, stopping MaxX"); + } + break; + case WEST: + // negative x + if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) { + stopMinX = true; + addon.logger(3,"Wall found, stopping minX"); + } + break; + case NORTH: + // negative Z + if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) { + stopMinZ = true; + addon.logger(3,"Wall found, stopping minZ"); + } + break; + case SOUTH: + // positive Z + if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getRelative(bf).getType())) { + stopMaxZ = true; + addon.logger(3,"Wall found, stopping maxZ"); + } + break; + default: + break; + } + } + } + } + } + } + if (minX < roof.getMinX()) { + addon.logger(3,"minx is less that the roof minX"); + stopMinX = true; + } + if (maxX > roof.getMaxX()) { + addon.logger(3,"maxx is > that the roof minX"); + stopMaxX = true; + } + if (minZ < roof.getMinZ()) { + addon.logger(3,"minz is less that the roof minz"); + stopMinZ = true; + } + if (maxZ > roof.getMaxZ()) { + addon.logger(3,"maxZ is >t the roof maxZ"); + stopMaxZ = true; + } + // Expand the edges + if (!stopMinX) { + radiusMinX++; + } + if (!stopMaxX) { + radiusMaxX++; + } + if (!stopMinZ) { + radiusMinZ++; + } + if (!stopMaxZ) { + radiusMaxZ++; + } + } while (!stopMinX || !stopMaxX || !stopMinZ || !stopMaxZ); + // We should have the largest cube we can make now + minX--; + maxX++; + minZ--; + maxZ++; + addon.logger(3,"wall minX = " + minX); + addon.logger(3,"wall maxX = " + maxX); + addon.logger(3,"wall minZ = " + minZ); + addon.logger(3,"wall maxZ = " + maxZ); + + // Find the floor again, only looking within the walls + y = roof.getHeight(); + do { + wallBlockCount = 0; + for (int x = minX; x <= maxX; x++) { + for (int z = minZ; z <= maxZ; z++) { + if (WALL_BLOCKS.contains(world.getBlockAt(x, y, z).getType())) { + wallBlockCount++; + } + } + } + + } while( y-- > 0 && wallBlockCount > 0); + floor = y + 1; + addon.logger(3,"#2 floor = " + floor); + + } + + /** + * @return the minXX + */ + public int getMinX() { + return minX; + } + /** + * @return the maxXX + */ + public int getMaxX() { + return maxX; + } + /** + * @return the minZZ + */ + public int getMinZ() { + return minZ; + } + /** + * @return the maxZZ + */ + public int getMaxZ() { + return maxZ; + } + /** + * @return the useRoofMaxX + */ + public boolean useRoofMaxX() { + return useRoofMaxX; + } + /** + * @return the useRoofMinX + */ + public boolean useRoofMinX() { + return useRoofMinX; + } + /** + * @return the useRoofMaxZ + */ + public boolean useRoofMaxZ() { + return useRoofMaxZ; + } + /** + * @return the useRoofMinZ + */ + public boolean useRoofMinZ() { + return useRoofMinZ; + } + /** + * @return the wallBlocks + */ + public List getWallBlocks() { + return WALL_BLOCKS; + } + + public int getArea() { + // Get interior area + return (maxX - minX) * (maxZ - minZ); + } + + /** + * @return the roofBlock + */ + public Location getRoofBlock() { + return roofBlock; + } + + /** + * @return the roofHopperLoc + */ + public Location getRoofHopperLoc() { + return roofHopperLoc; + } + + /** + * @return the floor + */ + public int getFloor() { + return floor; + } + + public static boolean isWallBlock(Material blockType) { + return WALL_BLOCKS.contains(blockType); + } +} diff --git a/src/main/java/world/bentobox/greenhouses/listeners/GreenhouseEvents.java b/src/main/java/world/bentobox/greenhouses/listeners/GreenhouseEvents.java new file mode 100644 index 0000000..e1a3a9a --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/listeners/GreenhouseEvents.java @@ -0,0 +1,319 @@ +package world.bentobox.greenhouses.listeners; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.Biome; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.Settings; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.ui.Locale; +import world.bentobox.greenhouses.util.Util; + +/** + * @author tastybento + * This class listens for changes to greenhouses and reacts to them + */ +public class GreenhouseEvents implements Listener { + private final Greenhouses plugin; + private List blockedPistons = new ArrayList(); + + public GreenhouseEvents(final Greenhouses plugin) { + this.plugin = plugin; + + } + + /** + * Permits water to be placed in the Nether if in a greenhouse and in an acceptable biome + * @param event + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled=true) + public void onPlayerInteract(PlayerInteractEvent event){ + Player player = event.getPlayer(); + World world = player.getWorld(); + // Check we are in the right world + if (!world.getEnvironment().equals(Environment.NETHER) || !Settings.worldName.contains(world.getName())) { + return; + } + if (event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) { + Biome biome = event.getClickedBlock().getBiome(); + if (event.getItem() != null && biome != null) { + // Allow pouring of water if biome is okay + if (event.getItem().getType().equals(Material.WATER_BUCKET) && !biome.equals(Biome.NETHER) + && !biome.equals(Biome.DESERT) && !biome.equals(Biome.DESERT_HILLS)) { + event.setCancelled(true); + event.getClickedBlock().getRelative(event.getBlockFace()).setType(Material.WATER); + } + } + } + } + + /** + * Makes water in the Nether if ice is broken and in a greenhouse + * @param event + */ + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled=true) + public void onIceBreak(BlockBreakEvent event){ + Player player = event.getPlayer(); + World world = player.getWorld(); + // Check we are in the right world + if (!Settings.worldName.contains(world.getName())) { + return; + } + Biome biome = event.getBlock().getBiome(); + // Set to water if the biome is okay. + if(event.getBlock().getWorld().getEnvironment() == Environment.NETHER && event.getBlock().getType() == Material.ICE + && !biome.equals(Biome.NETHER) && !biome.equals(Biome.DESERT) && !biome.equals(Biome.DESERT_HILLS)) { + event.setCancelled(true); + event.getBlock().setType(Material.WATER); + } + } + + /** + * Tracks player movement + * @param event + */ + /* + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true) + public void onPlayerMove(PlayerMoveEvent event) { + //plugin.logger(3,event.getEventName()); + Player player = event.getPlayer(); + UUID uuid = player.getUniqueId(); + if (plugin.getPlayerGHouse(uuid) == null || plugin.getPlayerGHouse(uuid).isEmpty()) { + return; + } + + World world = player.getWorld(); + // Check we are in the right world + if (!Settings.worldName.contains(world.getName())) { + plugin.logger(4,"Not in a Greenhouse world"); + return; + } + // Did we move a block? + if (event.getFrom().getBlockX() != event.getTo().getBlockX() + || event.getFrom().getBlockY() != event.getTo().getBlockY() + || event.getFrom().getBlockZ() != event.getTo().getBlockZ()) { + boolean result = checkMove(player, event.getFrom(), event.getTo(), uuid); + if (result) { + Location newLoc = event.getFrom(); + newLoc.setX(newLoc.getBlockX() + 0.5); + newLoc.setY(newLoc.getBlockY()); + newLoc.setZ(newLoc.getBlockZ() + 0.5); + event.setTo(newLoc); + } + } + } + + + @EventHandler(priority = EventPriority.HIGH) + public void onPlayerTeleport(PlayerTeleportEvent event) { + // Strangely, sometimes these worlds can be null + if (event.getFrom() == null || event.getTo() == null) { + return; + } + // Check if they changed worlds + UUID uuid = event.getPlayer().getUniqueId(); + if (plugin.getPlayerGHouse(uuid) == null || plugin.getPlayerGHouse(uuid).isEmpty()) { + return; + } + + World fromWorld = event.getFrom().getWorld(); + World toWorld = event.getTo().getWorld(); + // Check we are in the right world + if (!Settings.worldName.contains(fromWorld.getName()) && !Settings.worldName.contains(toWorld.getName())) { + return; + } + // Did we move a block? + checkMove(event.getPlayer(), event.getFrom(), event.getTo(), uuid); + } + */ + /** + * @param player + * @param from + * @param to + * @return false if the player can move into that area, true if not allowed + */ + /* + private boolean checkMove(Player player, Location from, Location to, UUID uuid) { + Greenhouse fromGreenhouse = null; + Greenhouse toGreenhouse= null; + if (plugin.getGreenhouses().isEmpty()) { + // No greenhouses yet + return false; + } + plugin.logger(4,"Checking greenhouses"); + plugin.logger(4,"From : " + from.toString()); + plugin.logger(4,"From: " + from.getBlockX() + "," + from.getBlockZ()); + plugin.logger(4,"To: " + to.getBlockX() + "," + to.getBlockZ()); + for (Greenhouse d: plugin.getPlayerGHouse(uuid)) { + plugin.logger(4,"Greenhouse (" + d.getPos1().getBlockX() + "," + d.getPos1().getBlockZ() + " : " + d.getPos2().getBlockX() + "," + d.getPos2().getBlockZ() + ")"); + if (d.insideGreenhouse(to)) { + plugin.logger(4,"To intersects d!"); + toGreenhouse = d; + } + if (d.insideGreenhouse(from)) { + plugin.logger(4,"From intersects d!"); + fromGreenhouse = d; + } + + + } + // No greenhouse interaction + if (fromGreenhouse == null && toGreenhouse == null) { + // Clear the greenhouse flag (the greenhouse may have been deleted while they were offline) + plugin.players.setInGreenhouse(player, null); + return false; + } else if (fromGreenhouse == toGreenhouse) { + // Set the greenhouse - needs to be done if the player teleports too (should be done on a teleport event) + plugin.players.setInGreenhouse(player, toGreenhouse); + return false; + } + if (fromGreenhouse != null && toGreenhouse == null) { + // leaving a greenhouse + if (!fromGreenhouse.getFarewellMessage().isEmpty()) { + player.sendMessage(fromGreenhouse.getFarewellMessage()); + } + plugin.players.setInGreenhouse(player, null); + //if (plugin.players.getNumberInGreenhouse(fromGreenhouse) == 0) { + // fromGreenhouse.Æ’(); + //} + } else if (fromGreenhouse == null && toGreenhouse != null){ + // Going into a greenhouse + if (!toGreenhouse.getEnterMessage().isEmpty()) { + player.sendMessage(toGreenhouse.getEnterMessage()); + + //plugin.visualize(toGreenhouse, player); + } + toGreenhouse.startBiome(false); + plugin.players.setInGreenhouse(player, toGreenhouse); + + } else if (fromGreenhouse != null && toGreenhouse != null){ + // Leaving one greenhouse and entering another greenhouse immediately + if (!fromGreenhouse.getFarewellMessage().isEmpty()) { + player.sendMessage(fromGreenhouse.getFarewellMessage()); + } + plugin.players.setInGreenhouse(player, toGreenhouse); + //if (plugin.players.getNumberInGreenhouse(fromGreenhouse) == 0) { + //fromGreenhouse.endBiome(); + //} + toGreenhouse.startBiome(false); + if (!toGreenhouse.getEnterMessage().isEmpty()) { + player.sendMessage(toGreenhouse.getEnterMessage()); + } + } + return false; + } + */ + + /** + * Checks is broken blocks cause the greenhouse to fail + * @param e + */ + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled=true) + public void onBlockBreak(final BlockBreakEvent e) { + if (!Settings.worldName.contains(e.getPlayer().getWorld().getName())) { + return; + } + plugin.logger(3,"block break"); + // Get the greenhouse that this block is in (if any) + Greenhouse g = plugin.getInGreenhouse(e.getBlock().getLocation()); + + if (g == null) { + // Not in a greenhouse + plugin.logger(3,"not in greenhouse"); + return; + } + // Check to see if this causes the greenhouse to break + if ((e.getBlock().getLocation().getBlockY() == g.getHeightY()) || (g.isAWall(e.getBlock().getLocation()))) { + e.getPlayer().sendMessage(ChatColor.RED + Locale.eventbroke.replace("[biome]",Util.prettifyText(g.getOriginalBiome().toString()))); + e.getPlayer().sendMessage(ChatColor.RED + Locale.eventfix); + plugin.removeGreenhouse(g); + return; + } + } + + /** + * Prevents placing of blocks above the greenhouses + * @param e + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled=true) + public void onPlayerBlockPlace(final BlockPlaceEvent e) { + if (!Settings.worldName.contains(e.getPlayer().getWorld().getName())) { + return; + } + if (e.getPlayer().getWorld().getEnvironment().equals(Environment.NETHER)) { + return; + } + // If the offending block is not above a greenhouse, forget it! + Greenhouse g = plugin.aboveAGreenhouse(e.getBlock().getLocation()); + if (g == null) { + return; + } + e.getPlayer().sendMessage(ChatColor.RED + Locale.eventcannotplace); + e.getPlayer().sendMessage("Greenhouse is at " + g.getPos1() + " to " + g.getPos2()); + e.setCancelled(true); + } + + /** + * Check to see if anyone is sneaking a block over a greenhouse by using a piston + * @param e + */ + @EventHandler + public void onPistonPush(final BlockPistonExtendEvent e) { + if (!Settings.worldName.contains(e.getBlock().getWorld().getName())) { + return; + } + if (e.getBlock().getWorld().getEnvironment().equals(Environment.NETHER)) { + return; + } + // Check if piston is already extended to avoid the double event effect + + Location l = e.getBlock().getLocation(); + if (blockedPistons.contains(l)) { + // Cancel the double call + blockedPistons.remove(l); + e.setCancelled(true); + return; + } + plugin.logger(3,"Direction: " + e.getDirection()); + plugin.logger(3,"Location of piston block:" + l); + // Pistons can push up to 12 blocks - find the end block + 1 + for (int i = 0; i < 13; i++) { + l = l.getBlock().getRelative(e.getDirection()).getLocation(); + if (!l.getBlock().getType().isSolid()) { + break; + } + } + plugin.logger(3,"Location of end block + 1:" + l); + // The end block location is now l + if (plugin.aboveAGreenhouse(l) == null) { + return; + } + // Find out who is around the piston + for (Player p : plugin.getServer().getOnlinePlayers()) { + if (Settings.worldName.contains(p.getLocation().getWorld().getName())) { + if (p.getLocation().distanceSquared(e.getBlock().getLocation()) <= 25) { + p.sendMessage(ChatColor.RED + Locale.eventpistonerror); + e.setCancelled(true); + blockedPistons.add(e.getBlock().getLocation()); + } + } + } + } +} + diff --git a/src/main/java/world/bentobox/greenhouses/listeners/GreenhouseGuard.java b/src/main/java/world/bentobox/greenhouses/listeners/GreenhouseGuard.java new file mode 100644 index 0000000..dc8d585 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/listeners/GreenhouseGuard.java @@ -0,0 +1,57 @@ +package world.bentobox.greenhouses.listeners; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockFromToEvent; + +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.Settings; +import world.bentobox.greenhouses.greenhouse.Greenhouse; + +public class GreenhouseGuard implements Listener { + private final Greenhouses plugin; + public GreenhouseGuard(final Greenhouses plugin) { + this.plugin = plugin; + + } + + // Stop lava flow or water into or out of a greenhouse + @EventHandler(priority = EventPriority.NORMAL) + public void onFlow(final BlockFromToEvent e) { + // Flow may be allowed anyway + if (Settings.allowFlowIn && Settings.allowFlowOut) { + return; + } + if (!Settings.worldName.isEmpty() && !Settings.worldName.contains(e.getBlock().getWorld().getName())) { + return; + } + // Get To and From Districts + Greenhouse to = plugin.getInGreenhouse(e.getToBlock().getLocation()); + Greenhouse from = plugin.getInGreenhouse(e.getBlock().getLocation()); + // Scenarios + // 1. inside district or outside - always ok + // 2. inside to outside - allowFlowOut determines + // 3. outside to inside - allowFlowIn determines + if (to == null && from == null) { + return; + } + if (to !=null && from != null && to.equals(from)) { + return; + } + // to or from or both are districts, NOT the same and flow is across a boundary + // if to is a district, flow in is allowed + if (to != null && Settings.allowFlowIn) { + return; + } + // if from is a district, flow may allowed + if (from != null && Settings.allowFlowOut) { + return; + } + // Otherwise cancel - the flow is not allowed + e.setCancelled(true); + } + + +} + diff --git a/src/main/java/world/bentobox/greenhouses/listeners/JoinLeaveEvents.java b/src/main/java/world/bentobox/greenhouses/listeners/JoinLeaveEvents.java new file mode 100644 index 0000000..616163a --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/listeners/JoinLeaveEvents.java @@ -0,0 +1,54 @@ +package world.bentobox.greenhouses.listeners; + +import java.util.List; +import java.util.UUID; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.PlayerCache; +import world.bentobox.greenhouses.ui.Locale; + +public class JoinLeaveEvents implements Listener { + private Greenhouses addon; + private PlayerCache players; + + public JoinLeaveEvents(Greenhouses greenhouses, PlayerCache onlinePlayers) { + this.addon = greenhouses; + this.players = onlinePlayers; + } + + /** + * @param event + */ + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerJoin(final PlayerJoinEvent event) { + Player p = event.getPlayer(); + final UUID playerUUID = p.getUniqueId(); + // Add player to the cache, and clear any greenhouses over their permitted limit + addon.players.addPlayer(p); + addon.logger(3,"Cached " + p.getName()); + // Load any messages for the player + final List messages = addon.getMessages(playerUUID); + if (!messages.isEmpty()) { + addon.getServer().getScheduler().runTaskLater(addon.getPlugin(), () -> { + event.getPlayer().sendMessage(ChatColor.AQUA + Locale.newsheadline); + int i = 1; + for (String message : messages) { + event.getPlayer().sendMessage(i++ + ": " + message); + } + }, 40L); + } + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPlayerQuit(final PlayerQuitEvent event) { + players.removeOnlinePlayer(event.getPlayer()); + } +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/greenhouses/ui/ControlPanel.java b/src/main/java/world/bentobox/greenhouses/ui/ControlPanel.java new file mode 100644 index 0000000..97d29b0 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/ControlPanel.java @@ -0,0 +1,195 @@ +package world.bentobox.greenhouses.ui; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.greenhouse.BiomeRecipe; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.util.Util; +import world.bentobox.greenhouses.util.VaultHelper; + + +/** + * @author tastybento + * Provides a handy control panel + */ +public class ControlPanel implements Listener { + + private Greenhouses plugin; + + private HashMap> biomePanels = new HashMap>(); + /** + * @param store + */ + public ControlPanel(Greenhouses plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) { + Player player = (Player) event.getWhoClicked(); // The player that clicked the item + Inventory inventory = event.getInventory(); // The inventory that was clicked in + int slot = event.getRawSlot(); + HashMap panel = biomePanels.get(player.getUniqueId()); + if (panel == null) { + // No panel, so just return + return; + } + // Check this is the Greenhouse Biome Panel + if (inventory.getName().equals(ChatColor.translateAlternateColorCodes('&', Locale.controlpaneltitle))) { + //String message = ""; + event.setCancelled(true); // Don't let them pick anything up + if (slot < 0) { + player.closeInventory(); + return; + } + if (slot == 0) { + player.performCommand("greenhouse info"); + player.closeInventory(); + return; + } + if (panel.containsKey(slot)) { + BiomeRecipe biomeRecipe = panel.get(slot); + // Sets up a greenhouse + Greenhouse oldg = plugin.players.getInGreenhouse(player); + // Check ownership + if (oldg != null && !oldg.getOwner().equals(player.getUniqueId())) { + player.sendMessage(Locale.errornotyours); + player.closeInventory(); + return; + } + if (oldg != null) { + // Player wants to try and change biome + //player.closeInventory(); // Closes the inventory + // error.exists + //player.sendMessage(ChatColor.RED + Locale.erroralreadyexists); + //return; + plugin.removeGreenhouse(oldg); + } + // Check if they are at their limit + if (plugin.players.isAtLimit(player)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Locale.infonomore)); + } else { + // Make greenhouse + Greenhouse g = plugin.tryToMakeGreenhouse(player, biomeRecipe); + if (g == null) { + player.closeInventory(); // Closes the inventory + //error.norecipe + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return; + } + player.closeInventory(); // Closes the inventory + } + //player.performCommand("greenhouse make"); + //player.sendMessage(message); + } + } + } + + /** + * Creates a player-specific biome panel based on permissions + * @param player + * @return + */ + public Inventory getPanel(Player player) { + HashMap store = new HashMap(); + // Index 0 is reserved for instructions + int index = 1; + // Run through biomes and add to the inventory if this player is allowed to use them + for (BiomeRecipe br : plugin.getBiomeRecipes()) { + // Gather the info + if (br.getPermission().isEmpty() || VaultHelper.checkPerm(player, br.getPermission())) { + // Add this biome recipe to the list + store.put(index++, br); + } + } + // Now create the panel + //int panelSize = store.size() + 9 - 1; + int panelSize = store.size() + 9; + panelSize -= ( panelSize % 9); + Inventory biomePanel = Bukkit.createInventory(player, panelSize, ChatColor.translateAlternateColorCodes('&', Locale.controlpaneltitle)); + // Add the instructions item + ItemStack item = new ItemStack(Material.THIN_GLASS); + ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(Locale.generalgreenhouses); + List lore = new ArrayList(); + lore.addAll(new ArrayList(Arrays.asList(Locale.infowelcome.split("\\|")))); + if (plugin.players.isAtLimit(player)) { + lore.add(ChatColor.translateAlternateColorCodes('&', Locale.infonomore)); + } else { + if (plugin.players.getRemainingGreenhouses(player) > 0) { + if (plugin.players.getRemainingGreenhouses(player) == 1) { + lore.addAll(new ArrayList(Arrays.asList(Locale.infoonemore.split("\\|")))); + } else { + String temp = Locale.infoyoucanbuild.replace("[number]", String.valueOf(plugin.players.getRemainingGreenhouses(player))); + lore.addAll(new ArrayList(Arrays.asList(temp.split("\\|")))); + } + } else { + lore.addAll(new ArrayList(Arrays.asList(Locale.infounlimited.split("\\|")))); + } + } + meta.setLore(lore); + item.setItemMeta(meta); + biomePanel.setItem(0, item); + // Now add the biomes + + index = 1; + for (BiomeRecipe br : store.values()) { + // Create an itemStack + item = new ItemStack(br.getIcon()); + meta = item.getItemMeta(); + if (br.getFriendlyName().isEmpty()) { + meta.setDisplayName(Util.prettifyText(br.getBiome().toString())); + } else { + meta.setDisplayName(br.getFriendlyName()); + } + lore.clear(); + List reqBlocks = br.getRecipeBlocks(); + if (reqBlocks.size() > 0) { + lore.add(ChatColor.YELLOW + Locale.recipeminimumblockstitle); + int i = 1; + for (String list : reqBlocks) { + lore.add(Locale.lineColor + (i++) + ": " + list); + } + } else { + lore.add(ChatColor.YELLOW + Locale.recipenootherblocks); + } + if (br.getWaterCoverage() == 0) { + lore.add(Locale.recipenowater); + } else if (br.getWaterCoverage() > 0) { + lore.add(Locale.recipewatermustbe.replace("[coverage]", String.valueOf(br.getWaterCoverage()))); + } + if (br.getIceCoverage() == 0) { + lore.add(Locale.recipenoice); + } else if (br.getIceCoverage() > 0) { + lore.add(Locale.recipeicemustbe.replace("[coverage]", String.valueOf(br.getIceCoverage()))); + } + if (br.getLavaCoverage() == 0) { + lore.add(Locale.recipenolava); + } else if (br.getLavaCoverage() > 0) { + lore.add(Locale.recipelavamustbe.replace("[coverage]", String.valueOf(br.getLavaCoverage()))); + } + meta.setLore(lore); + item.setItemMeta(meta); + biomePanel.setItem(index++,item); + } + // Stash the panel for later use when clicked + biomePanels.put(player.getUniqueId(), store); + return biomePanel; + } +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/GreenhouseCmd.java b/src/main/java/world/bentobox/greenhouses/ui/GreenhouseCmd.java new file mode 100644 index 0000000..a23042f --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/GreenhouseCmd.java @@ -0,0 +1,248 @@ + +package world.bentobox.greenhouses.ui; + +import java.util.List; +import java.util.UUID; + +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.ChatColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.PlayerCache; +import world.bentobox.greenhouses.Settings; +import world.bentobox.greenhouses.greenhouse.BiomeRecipe; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.util.Util; +import world.bentobox.greenhouses.util.VaultHelper; + +public class GreenhouseCmd implements CommandExecutor { + public boolean busyFlag = true; + private Greenhouses plugin; + private PlayerCache players; + + /** + * Constructor + * + * @param plugin + * @param players + */ + public GreenhouseCmd(Greenhouses plugin, PlayerCache players) { + + // Plugin instance + this.plugin = plugin; + this.players = players; + } + /* + * (non-Javadoc) + * + * @see + * org.bukkit.command.CommandExecutor#onCommand(org.bukkit.command.CommandSender + * , org.bukkit.command.Command, java.lang.String, java.lang.String[]) + */ + public boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] split) { + if (!(sender instanceof Player)) { + return false; + } + final Player player = (Player) sender; + // Check we are in the right world + if (!Settings.worldName.contains(player.getWorld().getName())) { + // notavailable + player.sendMessage(ChatColor.RED + Locale.generalnotavailable); + return true; + } + // Basic permissions check to even use /greenhouse + if (!VaultHelper.checkPerm(player, "greenhouses.player")) { + player.sendMessage(ChatColor.RED + Locale.errornoPermission); + return true; + } + /* + * Grab data for this player - may be null or empty + * playerUUID is the unique ID of the player who issued the command + */ + final UUID playerUUID = player.getUniqueId(); + + switch (split.length) { + // /greenhouse command by itself + case 0: + final Greenhouse greenhouseInNow = players.getInGreenhouse(player); + if (greenhouseInNow==null || greenhouseInNow.getOwner().equals(playerUUID)) { + player.openInventory(plugin.getRecipeInv(player)); + return true; + } else { + player.sendMessage(ChatColor.RED + Locale.errornotowner); + return true; + } + case 1: + // /greenhouse + if (split[0].equalsIgnoreCase("help")) { + player.sendMessage(ChatColor.GREEN + Locale.generalgreenhouses +" " + plugin.getDescription().getVersion() + " " + Locale.helphelp + ":"); + player.sendMessage(ChatColor.YELLOW + "/" + label + " : " + ChatColor.WHITE + Locale.helpopengui); + player.sendMessage(ChatColor.YELLOW + "/" + label + " make: " + ChatColor.WHITE + Locale.helpmake); + player.sendMessage(ChatColor.YELLOW + "/" + label + " remove: " + ChatColor.WHITE + Locale.helpremove); + player.sendMessage(ChatColor.YELLOW + "/" + label + " info: " + ChatColor.WHITE + Locale.helpinfo); + player.sendMessage(ChatColor.YELLOW + "/" + label + " list: " + ChatColor.WHITE + Locale.helplist); + player.sendMessage(ChatColor.YELLOW + "/" + label + " recipe : " + ChatColor.WHITE + Locale.helprecipe); + return true; + } else if (split[0].equalsIgnoreCase("list")) { + // List all the biomes that can be made + player.sendMessage(ChatColor.GREEN + Locale.listtitle); + player.sendMessage(Locale.listinfo); + int index = 0; + for (BiomeRecipe br : plugin.getBiomeRecipes()) { + if (br.getFriendlyName().isEmpty()) { + player.sendMessage(ChatColor.YELLOW + Integer.toString(index++) + ": " + Util.prettifyText(br.getBiome().toString())); + } else { + player.sendMessage(ChatColor.YELLOW + Integer.toString(index++) + ": " + br.getFriendlyName()); + } + } + return true; + } else if (split[0].equalsIgnoreCase("remove")) { + final Greenhouse greenhouseNow = players.getInGreenhouse(player); + if (greenhouseNow != null) { + if (greenhouseNow.getOwner().equals(playerUUID)) { + player.sendMessage(ChatColor.RED + Locale.errorremoving); + plugin.removeGreenhouse(greenhouseNow); + return true; + } + player.sendMessage(ChatColor.RED + Locale.errornotyours); + } else { + player.sendMessage(ChatColor.RED + Locale.errornotinside); + } + return true; + } else if (split[0].equalsIgnoreCase("make")) { + // Sets up a greenhouse + final Greenhouse greenhouseN = players.getInGreenhouse(player); + if (greenhouseN != null) { + // alreadyexists + player.sendMessage(ChatColor.RED + Locale.erroralreadyexists); + return true; + } + // Check if they are at their limit + if (plugin.players.isAtLimit(player)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Locale.infonomore)); + } else { + // Try to make greenhouse + Greenhouse g = plugin.tryToMakeGreenhouse(player); + if (g == null) { + // norecipe + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + // Greenhouse is made + } + return true; + } else if (split[0].equalsIgnoreCase("info")) { + // Show some instructions on how to make greenhouses + player.sendMessage(ChatColor.GREEN + ChatColor.translateAlternateColorCodes('&', Locale.infotitle)); + for (String instructions : Locale.infoinstructions) { + player.sendMessage(ChatColor.GREEN + ChatColor.translateAlternateColorCodes('&', instructions)); + } + final Greenhouse greenhouseIn = players.getInGreenhouse(player); + if (greenhouseIn != null) { + player.sendMessage(ChatColor.GOLD + Locale.infoinfo); + // general.biome + player.sendMessage(ChatColor.GREEN + Locale.generalbiome + ": " + Util.prettifyText(greenhouseIn.getBiome().toString())); + if (greenhouseIn.getOwner() != null) { + Player owner = plugin.getServer().getPlayer(greenhouseIn.getOwner()); + if (owner != null) { + player.sendMessage(ChatColor.YELLOW + Locale.generalowner + ": " + owner.getDisplayName() + " (" + owner.getName() + ")"); + } else { + player.sendMessage(ChatColor.YELLOW + Locale.generalowner + ": " + greenhouseIn.getPlayerName()); + } + } + } + return true; + + } + break; + case 2: + if (split[0].equalsIgnoreCase("make")) { + // Sets up a greenhouse for a specific biome + if (players.getInGreenhouse(player) != null) { + // alreadyexists + player.sendMessage(ChatColor.RED + Locale.erroralreadyexists); + return true; + } + // Check if they are at their limit + if (plugin.players.isAtLimit(player)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Locale.infonomore)); + } else { + // Check we are in a greenhouse + try { + if (NumberUtils.isNumber(split[1])) { + int recipeNum = Integer.valueOf(split[1]); + List recipeList = plugin.getBiomeRecipes(); + if (recipeNum < 1 || recipeNum > recipeList.size()) { + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + if (plugin.tryToMakeGreenhouse(player,recipeList.get(recipeNum)) == null) { + // Failed for some reason - maybe permissions + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + } + } catch (Exception e) { + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + } + return true; + } else if (split[0].equalsIgnoreCase("recipe")) { + int recipeNumber = 0; + try { + recipeNumber = Integer.valueOf(split[1]); + } catch (Exception e) { + player.sendMessage(ChatColor.RED + Locale.recipehint); + return true; + } + List recipeList = plugin.getBiomeRecipes(); + if (recipeNumber <1 || recipeNumber > recipeList.size()) { + player.sendMessage(ChatColor.RED + Locale.recipewrongnumber.replace("[size]", String.valueOf(recipeList.size()))); + return true; + } + BiomeRecipe br = recipeList.get(recipeNumber-1); + if (br.getFriendlyName().isEmpty()) { + player.sendMessage(ChatColor.GREEN + "[" + Util.prettifyText(br.getBiome().toString()) + " recipe]"); + } else { + player.sendMessage(ChatColor.GREEN + "[" + br.getFriendlyName() + " recipe]"); + } + player.sendMessage(ChatColor.YELLOW + "Biome: " + Util.prettifyText(br.getBiome().toString())); + if (br.getWaterCoverage() == 0) { + player.sendMessage(Locale.recipenowater); + } else if (br.getWaterCoverage() > 0) { + player.sendMessage(Locale.recipewatermustbe.replace("[coverage]", String.valueOf(br.getWaterCoverage()))); + } + if (br.getIceCoverage() == 0) { + player.sendMessage(Locale.recipenoice); + } else if (br.getIceCoverage() > 0) { + player.sendMessage(Locale.recipeicemustbe.replace("[coverage]", String.valueOf(br.getIceCoverage()))); + } + if (br.getLavaCoverage() == 0) { + player.sendMessage(Locale.recipenolava); + } else if (br.getLavaCoverage() > 0) { + player.sendMessage(Locale.recipelavamustbe.replace("[coverage]", String.valueOf(br.getLavaCoverage()))); + } + List reqBlocks = br.getRecipeBlocks(); + if (reqBlocks.size() > 0) { + player.sendMessage(ChatColor.YELLOW + Locale.recipeminimumblockstitle); + int index = 1; + for (String list : reqBlocks) { + player.sendMessage(Locale.lineColor + (index++) + ": " + list); + } + } else { + player.sendMessage(ChatColor.YELLOW + Locale.recipenootherblocks); + } + return true; + } + + } + return false; + } + + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/greenhouses/ui/Locale.java b/src/main/java/world/bentobox/greenhouses/ui/Locale.java new file mode 100644 index 0000000..3903405 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/Locale.java @@ -0,0 +1,90 @@ +package world.bentobox.greenhouses.ui; + +import java.util.ArrayList; +import java.util.List; + + + +/** + * @author ben + * All the text strings in the game sent to players + */ +public class Locale { + + public static String generalnotavailable; + public static String generalgreenhouses; + public static String generalbiome; + public static String generalowner; + public static String helphelp; + public static String helpmake; + public static String helpremove; + public static String helpinfo; + public static String helplist; + public static String helprecipe; + public static String listtitle; + public static String listinfo; + public static String errorunknownPlayer; + public static String errornoPermission; + public static String errorcommandNotReady; + public static String errorofflinePlayer; + public static String errorunknownCommand; + public static String errormove; + public static String errornotowner; + public static String errorremoving; + public static String errornotyours; + public static String errornotinside; + public static String errortooexpensive; + public static String erroralreadyexists; + public static String errornorecipe; + public static String messagesenter; + public static String messagesleave; + public static String messagesyouarein; + public static String messagesremoved; + public static String messagesremovedmessage; + public static String messagesecolost; + public static String infotitle; + public static List infoinstructions = new ArrayList(); + public static String infoinfo; + public static String infonone; + public static String recipehint; + public static String recipewrongnumber; + public static String recipetitle; + public static String recipenowater; + public static String recipenoice; + public static String recipenolava; + public static String recipewatermustbe; + public static String recipeicemustbe; + public static String recipelavamustbe; + public static String recipeminimumblockstitle; + public static String recipenootherblocks; + public static String eventbroke; + public static String eventfix; + public static String eventcannotplace; + public static String eventpistonerror; + public static String createnoroof; + public static String createmissingwall; + public static String createnothingabove; + public static String createholeinroof; + public static String createholeinwall; + public static String createhoppererror; + public static String createdoorerror; + public static String createsuccess; + public static String adminHelpreload; + public static String adminHelpinfo; + public static String reloadconfigReloaded; + public static String admininfoerror; + public static String admininfoerror2; + public static String admininfoflags; + public static String newsheadline; + public static String controlpaneltitle; + public static String recipemissing; + public static String infoyoucanbuild; + public static String infoonemore; + public static String infonomore; + public static String infounlimited; + public static String infowelcome; + public static String helpopengui; + public static String limitsnoneallowed; + public static String limitslimitedto; + public static String lineColor; +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/admin/AdminCmd.java b/src/main/java/world/bentobox/greenhouses/ui/admin/AdminCmd.java new file mode 100644 index 0000000..7c0833c --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/admin/AdminCmd.java @@ -0,0 +1,110 @@ +package world.bentobox.greenhouses.ui.admin; + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.greenhouses.Greenhouses; + +/** + * This class handles commands for admins + * + */ +public class AdminCmd extends CompositeCommand { + + public AdminCmd(Greenhouses greenhouses) { + super(greenhouses, "gadmin"); + } + + @Override + public void setup() { + this.setPermission("greenhouses.admin"); + this.setOnlyPlayer(false); + this.setParametersHelp("greenhouses.admin.parameters"); + this.setDescription("greenhouses.admin.description"); + + new GreenhousesAdminReloadCommand(this); + new GreenhousesAdminInfoCommand(this); + } + + @Override + public boolean execute(User user, String label, List args) { + return false; + + } + + /* + case 1: + if (split[0].equalsIgnoreCase("reload")) { + plugin.reloadConfig(); + plugin.loadPluginConfig(); + plugin.loadBiomeRecipes(); + plugin.ecoTick(); + sender.sendMessage(ChatColor.YELLOW + Locale.reloadconfigReloaded); + return true; + } else if (split[0].equalsIgnoreCase("info")) { + if (!(sender instanceof Player)) { + sender.sendMessage(ChatColor.RED + Locale.admininfoerror); + return true; + } + Player player = (Player)sender; + Greenhouse greenhouse = players.getInGreenhouse(player); + if (greenhouse == null) { + sender.sendMessage(ChatColor.RED + Locale.admininfoerror2); + return true; + } + sender.sendMessage(ChatColor.GREEN + Locale.infoinfo); + sender.sendMessage(ChatColor.GREEN + Locale.generalowner + ":" + greenhouse.getPlayerName()); + sender.sendMessage(ChatColor.GREEN + Locale.admininfoflags); + for (String flag : greenhouse.getFlags().keySet()) { + sender.sendMessage(flag + ": " + greenhouse.getFlags().get(flag)); + } + return true; + } else { + sender.sendMessage(ChatColor.RED + Locale.errorunknownCommand); + return false; + } + case 2: + if (split[0].equalsIgnoreCase("info")) { + sender.sendMessage(ChatColor.GREEN + Locale.infoinfo); + int index = 0; + boolean found = false; + for (Greenhouse g : plugin.getGreenhouses()) { + if (g.getPlayerName().equalsIgnoreCase(split[1])) { + if (!found) + sender.sendMessage(ChatColor.GREEN + Locale.generalowner + ":" + g.getPlayerName()); + found = true; + sender.sendMessage("Greenhouse #" + (++index)); + sender.sendMessage("Biome: " + g.getBiome().name()); + sender.sendMessage("Recipe: " + g.getBiomeRecipe().getFriendlyName()); + sender.sendMessage(g.getWorld().getName()); + sender.sendMessage(g.getPos1().getBlockX() + ", " + g.getPos1().getBlockZ() + " to " + g.getPos2().getBlockX() + ", " + g.getPos2().getBlockZ()); + sender.sendMessage("Base at " + g.getPos1().getBlockY()); + sender.sendMessage("Height = " + g.getHeight()); + sender.sendMessage("Area = " + g.getArea()); + } + } + if (found) { + if (index == 0) { + sender.sendMessage("Player has no greenhouses."); + } else { + Player player = plugin.getServer().getPlayer(split[1]); + if (player != null) { + sender.sendMessage("Player has " + index + " greenhouses and is allowed to build " + plugin.getMaxGreenhouses(player)); + } else { + sender.sendMessage("Player has " + index + " greenhouses. Player is offline."); + } + } + } else { + sender.sendMessage(ChatColor.RED + "Cannot find that player. (May not have logged on recently)"); + } + return true; + } else { + sender.sendMessage(ChatColor.RED + Locale.errorunknownCommand); + return false; + } + default: + return false; + } + }*/ +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminInfoCommand.java b/src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminInfoCommand.java new file mode 100644 index 0000000..4fc3cd2 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminInfoCommand.java @@ -0,0 +1,45 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.admin; + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +/** + * @author tastybento + * + */ +public class GreenhousesAdminInfoCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public GreenhousesAdminInfoCommand(CompositeCommand parent) { + super(parent, "info"); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminReloadCommand.java b/src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminReloadCommand.java new file mode 100644 index 0000000..9368d99 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/admin/GreenhousesAdminReloadCommand.java @@ -0,0 +1,44 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.admin; + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +/** + * @author tastybento + * + */ +public class GreenhousesAdminReloadCommand extends CompositeCommand { + + /** + * @param addon + * @param label + * @param aliases + */ + public GreenhousesAdminReloadCommand(CompositeCommand parent) { + super(parent, "reload"); + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/user/GhCommand.java b/src/main/java/world/bentobox/greenhouses/ui/user/GhCommand.java new file mode 100644 index 0000000..fdb8833 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/user/GhCommand.java @@ -0,0 +1,62 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.user; + +import java.util.List; + +import org.bukkit.ChatColor; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.ui.Locale; + +/** + * @author tastybento + * + */ +public class GhCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public GhCommand(CompositeCommand parent) { + super(parent, "greenhouse", "gh"); + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + this.setPermission("greenhouses.command"); + this.setOnlyPlayer(true); + this.setParametersHelp("greenhouses.command.parameters"); + this.setDescription("greenhouses.command.description"); + + new InfoCommand(this); + new ListCommand(this); + new MakeCommand(this); + new RecipeCommand(this); + new RemoveCommand(this); + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + final Greenhouse greenhouseInNow = players.getInGreenhouse(player); + if (greenhouseInNow==null || greenhouseInNow.getOwner().equals(playerUUID)) { + player.openInventory(plugin.getRecipeInv(player)); + return true; + } else { + player.sendMessage(ChatColor.RED + Locale.errornotowner); + return true; + } + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/user/InfoCommand.java b/src/main/java/world/bentobox/greenhouses/ui/user/InfoCommand.java new file mode 100644 index 0000000..ad40891 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/user/InfoCommand.java @@ -0,0 +1,45 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.user; + +import java.util.List; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; + +/** + * @author tastybento + * + */ +public class InfoCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public InfoCommand(CompositeCommand parent) { + super(parent, "make"); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/user/ListCommand.java b/src/main/java/world/bentobox/greenhouses/ui/user/ListCommand.java new file mode 100644 index 0000000..26fdd4d --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/user/ListCommand.java @@ -0,0 +1,62 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.user; + +import java.util.List; + +import org.bukkit.ChatColor; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.greenhouse.BiomeRecipe; +import world.bentobox.greenhouses.ui.Locale; +import world.bentobox.greenhouses.util.Util; + +/** + * @author tastybento + * + */ +public class ListCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public ListCommand(CompositeCommand parent) { + super(parent, "list"); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + // List all the biomes that can be made + user.sendMessage(ChatColor.GREEN + Locale.listtitle); + user.sendMessage(Locale.listinfo); + int index = 0; + for (BiomeRecipe br : ((Greenhouses)getAddon()).getBiomeRecipes()) { + if (br.getFriendlyName().isEmpty()) { + user.sendMessage(ChatColor.YELLOW + Integer.toString(index++) + ": " + Util.prettifyText(br.getBiome().toString())); + } else { + user.sendMessage(ChatColor.YELLOW + Integer.toString(index++) + ": " + br.getFriendlyName()); + } + } + return true; + + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/user/MakeCommand.java b/src/main/java/world/bentobox/greenhouses/ui/user/MakeCommand.java new file mode 100644 index 0000000..6466b81 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/user/MakeCommand.java @@ -0,0 +1,105 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.user; + +import java.util.List; + +import org.apache.commons.lang.math.NumberUtils; +import org.bukkit.ChatColor; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.greenhouses.greenhouse.BiomeRecipe; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.ui.Locale; + +/** + * @author tastybento + * + */ +public class MakeCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public MakeCommand(CompositeCommand parent) { + super(parent, "make"); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + // Sets up a greenhouse + final Greenhouse greenhouseN = players.getInGreenhouse(player); + if (greenhouseN != null) { + // alreadyexists + player.sendMessage(ChatColor.RED + Locale.erroralreadyexists); + return true; + } + // Check if they are at their limit + if (plugin.players.isAtLimit(player)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Locale.infonomore)); + } else { + // Try to make greenhouse + Greenhouse g = plugin.tryToMakeGreenhouse(player); + if (g == null) { + // norecipe + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + // Greenhouse is made + } + return true; + + // Second arg + case 2: + if (split[0].equalsIgnoreCase("make")) { + // Sets up a greenhouse for a specific biome + if (players.getInGreenhouse(player) != null) { + // alreadyexists + player.sendMessage(ChatColor.RED + Locale.erroralreadyexists); + return true; + } + // Check if they are at their limit + if (plugin.players.isAtLimit(player)) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', Locale.infonomore)); + } else { + // Check we are in a greenhouse + try { + if (NumberUtils.isNumber(split[1])) { + int recipeNum = Integer.valueOf(split[1]); + List recipeList = plugin.getBiomeRecipes(); + if (recipeNum < 1 || recipeNum > recipeList.size()) { + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + if (plugin.tryToMakeGreenhouse(player,recipeList.get(recipeNum)) == null) { + // Failed for some reason - maybe permissions + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + } + } catch (Exception e) { + player.sendMessage(ChatColor.RED + Locale.errornorecipe); + return true; + } + } + return true; + } + + } diff --git a/src/main/java/world/bentobox/greenhouses/ui/user/RecipeCommand.java b/src/main/java/world/bentobox/greenhouses/ui/user/RecipeCommand.java new file mode 100644 index 0000000..fcda6c7 --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/user/RecipeCommand.java @@ -0,0 +1,95 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.user; + +import java.util.List; + +import org.bukkit.ChatColor; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.greenhouses.greenhouse.BiomeRecipe; +import world.bentobox.greenhouses.ui.Locale; +import world.bentobox.greenhouses.util.Util; + +/** + * @author tastybento + * + */ +public class RecipeCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public RecipeCommand(CompositeCommand parent) { + super(parent, "make"); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + + // Second arg + int recipeNumber = 0; + try { + recipeNumber = Integer.valueOf(split[1]); + } catch (Exception e) { + player.sendMessage(ChatColor.RED + Locale.recipehint); + return true; + } + List recipeList = plugin.getBiomeRecipes(); + if (recipeNumber <1 || recipeNumber > recipeList.size()) { + player.sendMessage(ChatColor.RED + Locale.recipewrongnumber.replace("[size]", String.valueOf(recipeList.size()))); + return true; + } + BiomeRecipe br = recipeList.get(recipeNumber-1); + if (br.getFriendlyName().isEmpty()) { + player.sendMessage(ChatColor.GREEN + "[" + Util.prettifyText(br.getBiome().toString()) + " recipe]"); + } else { + player.sendMessage(ChatColor.GREEN + "[" + br.getFriendlyName() + " recipe]"); + } + player.sendMessage(ChatColor.YELLOW + "Biome: " + Util.prettifyText(br.getBiome().toString())); + if (br.getWaterCoverage() == 0) { + player.sendMessage(Locale.recipenowater); + } else if (br.getWaterCoverage() > 0) { + player.sendMessage(Locale.recipewatermustbe.replace("[coverage]", String.valueOf(br.getWaterCoverage()))); + } + if (br.getIceCoverage() == 0) { + player.sendMessage(Locale.recipenoice); + } else if (br.getIceCoverage() > 0) { + player.sendMessage(Locale.recipeicemustbe.replace("[coverage]", String.valueOf(br.getIceCoverage()))); + } + if (br.getLavaCoverage() == 0) { + player.sendMessage(Locale.recipenolava); + } else if (br.getLavaCoverage() > 0) { + player.sendMessage(Locale.recipelavamustbe.replace("[coverage]", String.valueOf(br.getLavaCoverage()))); + } + List reqBlocks = br.getRecipeBlocks(); + if (reqBlocks.size() > 0) { + player.sendMessage(ChatColor.YELLOW + Locale.recipeminimumblockstitle); + int index = 1; + for (String list : reqBlocks) { + player.sendMessage(Locale.lineColor + (index++) + ": " + list); + } + } else { + player.sendMessage(ChatColor.YELLOW + Locale.recipenootherblocks); + } + return true; + } + +} diff --git a/src/main/java/world/bentobox/greenhouses/ui/user/RemoveCommand.java b/src/main/java/world/bentobox/greenhouses/ui/user/RemoveCommand.java new file mode 100644 index 0000000..898484f --- /dev/null +++ b/src/main/java/world/bentobox/greenhouses/ui/user/RemoveCommand.java @@ -0,0 +1,61 @@ +/** + * + */ +package world.bentobox.greenhouses.ui.user; + +import java.util.List; + +import org.bukkit.ChatColor; + +import world.bentobox.bentobox.api.commands.CompositeCommand; +import world.bentobox.bentobox.api.user.User; +import world.bentobox.greenhouses.Greenhouses; +import world.bentobox.greenhouses.greenhouse.Greenhouse; +import world.bentobox.greenhouses.ui.Locale; + +/** + * @author tastybento + * + */ +public class RemoveCommand extends CompositeCommand { + + /** + * @param parent + * @param label + * @param aliases + */ + public RemoveCommand(CompositeCommand parent) { + super(parent, "make"); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#setup() + */ + @Override + public void setup() { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see world.bentobox.bentobox.api.commands.BentoBoxCommand#execute(world.bentobox.bentobox.api.user.User, java.lang.String, java.util.List) + */ + @Override + public boolean execute(User user, String label, List args) { + final Greenhouse greenhouseNow = ((Greenhouses)getAddon()).getInGreenhouse(user); + if (greenhouseNow != null) { + if (greenhouseNow.getOwner().equals(user.getUniqueId())) { + user.sendMessage(ChatColor.RED + Locale.errorremoving); + plugin.removeGreenhouse(greenhouseNow); + return true; + } + user.sendMessage(ChatColor.RED + Locale.errornotyours); + } else { + user.sendMessage(ChatColor.RED + Locale.errornotinside); + } + return true; + + } + +} diff --git a/src/main/resources/addon.yml b/src/main/resources/addon.yml new file mode 100755 index 0000000..0cfe154 --- /dev/null +++ b/src/main/resources/addon.yml @@ -0,0 +1,22 @@ +name: WelcomeWarps +main: world.bentobox.warps.Warp +version: ${version} + +authors: tastybento + +softdepend: AcidIsland, BSkyBlock + +permissions: + bskyblock.island.warp: + description: Player can use warp or warps commands + default: true + bskyblock.island.addwarp: + description: Player can create a welcome warp sign + default: true + acidisland.island.warp: + description: Player can use warp or warps commands + default: true + acidisland.island.addwarp: + description: Player can create a welcome warp sign + default: true + diff --git a/src/main/resources/biomes.yml b/src/main/resources/biomes.yml new file mode 100644 index 0000000..19a4504 --- /dev/null +++ b/src/main/resources/biomes.yml @@ -0,0 +1,198 @@ +# This file lists the recipes for the greenhouse biomes +biomes: + # Biome recipe name - can be anything you like, but must be unique + beaches: + # The name of the icon. Can use & for color codes, e.g. &c + friendlyname: "Beach" + # The biome of the recipe. Allows multiple recipes for the same biome. + biome: BEACHES + # The icon is shown in the panel. It must be a Bukkit Material + icon: SAND + # Priority is used if the greenhouse can be more than one biome. The highest + # priority wins + priority: 0 + # Contents - The minimum requirement for this biome. + # Format is Material:Type/Durability (optional):Number of blocks + # So SAND:1:1 would require 1 block of red sand (durability of 1 = red sand) + contents: SAND:1 + # The number of blocks in the greenhouse that must be water, ice or lava + # Floor area * this % = number of blocks required + watercoverage: 50 + # If the value is zero, then NO ice/water/lava is allowed + # If the values are missing, or negative, then ice/water/lava is allowed, but not + # required for the biome. + # icecoverage: 0 + # lavacoverage: 0 + # Plants that can grow via the hopper/bonemeal system + # Format is: + # Material:Type(optional): % chance:Block type on which it can grow + # Note that with really small greenhouses, melons and pumpkins can change + # grass to dirt, which may break the eco system! + plants: + DEAD_BUSH: 5:SAND + # Mobs that may spawn. + # Format: + # Entity name: % chance:Block on which the mob will spawn + mobs: + SQUID: 10:STATIONARY_WATER + # The minimum number of blocks each mob requires. + # Mobs will not spawn if there is more than 1 per this number of + # blocks in the greenhouse. e.g., in this case only 2 mobs will spawn if the + # greenhouse area is 18 blocks + moblimit: 9 + Snowy_beach: + friendlyname: "Snowy beach" + biome: COLD_BEACH + icon: SNOW_BLOCK + priority: 21 + contents: SAND:1 + watercoverage: 50 + icecoverage: 10 + ThreeWolfMoon: + friendlyname: "Three Wolf Moon Forest" + # Could do with more wolves, but the magic works with 3. + # If you are using 1.8 or earlier, this biome should be COLD_TAIGA + biome: TAIGA_COLD + icon: SAPLING + priority: 20 + contents: LOG:3 LEAVES:3 GRASS:3 + icecoverage: 10 + plants: + LONG_GRASS:1: 10:GRASS + mobs: + WOLF: 10:SNOW + moblimit: 9 + Cold_Rabbit: + friendlyname: "Cold Taiga Forest" + # If you are using 1.8 or earlier, this biome should be COLD_TAIGA + biome: TAIGA_COLD + icon: SAPLING + priority: 20 + contents: LOG:3 LEAVES:3 GRASS:3 + icecoverage: 10 + plants: + LONG_GRASS:1: 10:GRASS + mobs: + RABBIT: 10:SNOW + moblimit: 9 + DESERT: + friendlyname: "Desert" + biome: "Desert" + icon: DEAD_BUSH + priority: 3 + contents: SAND:1 + # No water allowed + watercoverage: 0 + # No ice allowed + icecoverage: 0 + plants: + DEAD_BUSH: 10:SAND + CACTUS: 10:SAND + # Conversions (see below for another variation) + # Format is: + # Original Block Material:Durability:Chance of change:New Block Material:Durability + # So, for below, regular dirt (durability 0) has a 30% chance of changing into regular sand. + conversions: DIRT:0:30:SAND:0 + FOREST: + friendlyname: "Flowery forest" + biome: FOREST + icon: RED_ROSE + priority: 4 + contents: LOG:3 LEAVES:4 GRASS:4 + plants: + RED_ROSE:8: 2:GRASS + DOUBLE_PLANT:5: 4:GRASS + LONG_GRASS:1: 20:GRASS + HELL: + friendlyname: "&cNether" + biome: HELL + icon: LAVA_BUCKET + priority: 5 + contents: NETHERRACK:1 + # Lava required, no ice or water allowed + lavacoverage: 21 + icecoverage: 0 + watercoverage: 0 + mobs: + PIG_ZOMBIE: 10:NETHERRACK + moblimit: 9 + permission: greenhouses.biome.nether + JUNGLE: + biome: JUNGLE + icon: VINE + priority: 6 + contents: GRASS:4 LOG:3:3 LEAVES:3:4 + plants: + YELLOW_FLOWER: 20:GRASS + MELON_BLOCK: 10:GRASS + RED_ROSE: 20:GRASS + DOUBLE_PLANT:3: 20:GRASS + LONG_GRASS:2: 20:GRASS + MUSHROOM_ISLAND: + friendlyname: "Mushroom Island" + biome: MUSHROOM_ISLAND + icon: RED_MUSHROOM + priority: 11 + contents: MYCEL:2 + # Water required at 30% + watercoverage: 30 + plants: + BROWN_MUSHROOM: 10:MYCEL + RED_MUSHROOM: 10:MYCEL + mobs: + MUSHROOM_COW: 10:MYCEL + moblimit: 9 + OCEAN: + biome: OCEAN + icon: WATER_BUCKET + priority: 8 + # Lots of water required! + watercoverage: 95 + mobs: + SQUID: 10:STATIONARY_WATER + moblimit: 9 + PLAINS: + friendlyname: "Horse Plains" + biome: PLAINS + icon: GRASS + priority: 1 + contents: GRASS:3 + plants: + LONG_GRASS:1: 10:GRASS + mobs: + HORSE: 10:GRASS + moblimit: 9 + RIVER: + friendlyname: "Clay river" + biome: RIVER + icon: CLAY + priority: 10 + contents: SAND:1 + # 50% water required + watercoverage: 50 + # Conversions - in this case, an adjacent block is required to convert + # Format is: + # Original Block:Durability:% chance:New Block:Durability:Adjacent Block:Durability + # So, for below, dirt has a 50% chance of changing into clay if it is next to water! + conversions: DIRT:0:50:CLAY:0:STATIONARY_WATER:0 + SAVANNA: + biome: SAVANNA + icon: LEAVES + priority: 11 + contents: LOG_2:3 LEAVES_2:4 GRASS:4 + plants: + DOUBLE_PLANT:2: 10:GRASS + SWAMPLAND: + friendlyname: "&2Slimy Swamp" + icon: WATER_LILY + priority: 13 + contents: GRASS:4 LOG:3 LEAVES:4 + # 50% water coverage required + watercoverage: 50 + plants: + RED_MUSHROOM: 20:GRASS + BROWN_MUSHROOM: 20:GRASS + WATER_LILY: 5:STATIONARY_WATER + mobs: + SLIME: 5:STATIONARY_WATER + moblimit: 3 diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..67ae7e1 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,52 @@ +greenhouses: + # World Name where Greenhouses will operate + worldName: + - world + - creative + - ASkyBlock + - AcidIsland + + # Console message level. + # List the levels of debug or messages you want. + # 0 = no messages except for warnings or severe errors + # 1 = normal messages (default) + # 2 and higher - debug levels - no need to use these unless you want to try debug + debug: + - 1 + + ### Permissions limits + # Set the maximum number of greenhouses a player can build + # using greenhouses.limit. + # This is the default max. -1 is unlimited + maxgreenhouses: -1 + # To have extra greenhouses deleted upon login set the following to true + deleteextras: false + + ### Weather and ecosystem settings + + # How often it should snow in the g/h when the weather is raining, in seconds + snowspeed: 30 + # Chance of any snow falling in a greenhouse when the snow tick occurs + # (1.0 = always, 0.0 = never) + snowchance: 1 + # How many blocks should get snow 1 = all of them, 0 = none, 0.1 = 1 in 10 + snowdensity: 0.1 + + # Biome activity + # How often should greenhouse biomes be checked to make sure they are still valid + ecotick: 5 + # How often should plants potentially grow in minutes if bonemeal is in the hopper + planttick: 1 + # How often should blocks potentially convert, in minutes + # Example: dirt-> sand in desert greenhouse + blocktick: 2 + # How often should mobs be potentially spawned in a greenhouse, in minutes + mobtick: 5 + + + ### Default settings for greenhouse actions + # Allow lava or water to flow out of a greenhouse, e.g. through the door, floor + allowflowout: false + # Allow lava or water to flow into a greenhouse, e.g., through the door + allowflowin: false + \ No newline at end of file