/** * Recursively expand the search area so we can define the number of blocks that are in the * cauldron. The search will not exceed 24 blocks as no pot will ever use up that many blocks. The * Y are bounded both directions so we don't ever search the lava or anything above, although in * the case of non-wall blocks, we also make sure that there is standing lava underneath. * * @param world * @param pt * @param minY * @param maxY * @param visited * @throws Cauldron.NotACauldronException */ public void findCauldronContents( World world, BlockWorldVector pt, int minY, int maxY, Map<BlockWorldVector, Integer> visited) throws NotACauldronException { int blockID = plugin.getLocalConfiguration().cauldronSettings.cauldronBlock; // Don't want to go too low or high if (pt.getBlockY() < minY) { return; } if (pt.getBlockY() > maxY) { return; } // There is likely a leak in the cauldron (or this isn't a cauldron) if (visited.size() > 24) { throw new NotACauldronException("mech.cauldron.leaky"); } // Prevent infinite looping if (visited.containsKey(pt)) { return; } int type = world.getBlockTypeIdAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); // Make water work reliably if (type == 9) { type = 8; } // Make lava work reliably if (type == 11) { type = 10; } visited.put(pt, type); // It's a wall -- we only needed to remember that we visited it but // we don't need to recurse if (type == blockID) { return; } // Must have a lava floor BlockWorldVector lavaPos = recurse(0, pt.getBlockY() - minY + 1, 0, pt); if ((world.getBlockTypeIdAt(lavaPos.getBlockX(), lavaPos.getBlockY(), lavaPos.getBlockZ())) == BlockID.LAVA) { throw new NotACauldronException("mech.cauldron.no-lava"); } // Now we recurse! findCauldronContents(world, recurse(1, 0, 0, pt), minY, maxY, visited); findCauldronContents(world, recurse(-1, 0, 0, pt), minY, maxY, visited); findCauldronContents(world, recurse(0, 0, 1, pt), minY, maxY, visited); findCauldronContents(world, recurse(0, 0, -1, pt), minY, maxY, visited); findCauldronContents(world, recurse(0, 1, 0, pt), minY, maxY, visited); findCauldronContents(world, recurse(0, -1, 0, pt), minY, maxY, visited); }
/** * Do cauldron. * * @param pt * @param player * @param world */ public boolean preCauldron(Player player, World world, BlockWorldVector pt) { double x = pt.getX(); double y = pt.getY(); double z = pt.getZ(); int ix = pt.getBlockX(); int iy = pt.getBlockY(); int iz = pt.getBlockZ(); double rootY = y; int below = world.getBlockTypeIdAt(ix, iy - 1, iz); int below2 = world.getBlockTypeIdAt(ix, iy - 2, iz); int s1 = world.getBlockTypeIdAt(ix + 1, iy, iz); int s3 = world.getBlockTypeIdAt(ix - 1, iy, iz); int s2 = world.getBlockTypeIdAt(ix, iy, iz + 1); int s4 = world.getBlockTypeIdAt(ix, iy, iz - 1); int blockID = plugin.getLocalConfiguration().cauldronSettings.cauldronBlock; // stop strange lava ids if (below == 11) below = 10; if (below2 == 11) below2 = 10; // Preliminary check so we don't waste CPU cycles if ((below == BlockID.LAVA || below2 == BlockID.LAVA) && (s1 == blockID || s2 == blockID || s3 == blockID || s4 == blockID)) { // Cauldron is 2 units deep if (below == BlockID.LAVA) { rootY++; } performCauldron(player, world, new BlockWorldVector(pt.getWorld(), x, rootY, z)); return true; } return false; }
/** * Toggle lights in the immediate area. * * @param pt * @return true if the block was recogized as a lightswitch; this may or may not mean that any * lights were actually toggled. */ private boolean toggleLights(BlockWorldVector pt) { World world = BukkitUtil.toWorld(pt); int wx = pt.getBlockX(); int wy = pt.getBlockY(); int wz = pt.getBlockZ(); int aboveID = world.getBlockTypeIdAt(wx, wy + 1, wz); if (aboveID == BlockID.TORCH || aboveID == BlockID.REDSTONE_TORCH_OFF || aboveID == BlockID.REDSTONE_TORCH_ON) { // Check if block above is a redstone torch. // Used to get what to change torches to. boolean on = (aboveID != BlockID.TORCH); // Prevent spam Long lastUse = recentLightToggles.remove(pt); long currTime = System.currentTimeMillis(); if (lastUse != null && currTime - lastUse < 500) { recentLightToggles.put(pt, lastUse); return true; } recentLightToggles.put(pt, currTime); int changed = 0; for (int x = -10 + wx; x <= 10 + wx; x++) { for (int y = -10 + wy; y <= 10 + wy; y++) { for (int z = -5 + wz; z <= 5 + wz; z++) { int id = world.getBlockTypeIdAt(x, y, z); if (id == BlockID.TORCH || id == BlockID.REDSTONE_TORCH_OFF || id == BlockID.REDSTONE_TORCH_ON) { // Limit the maximum number of changed lights if (changed >= 20) { return true; } if (on) { world.getBlockAt(x, y, z).setTypeId(BlockID.TORCH); } else { world.getBlockAt(x, y, z).setTypeId(BlockID.REDSTONE_TORCH_ON); } changed++; } } } } return true; } return false; }
/** * Attempt to perform a cauldron recipe. * * @param player * @param world * @param pt */ private void performCauldron(Player player, World world, BlockWorldVector pt) { // Gotta start at a root Y then find our orientation int rootY = pt.getBlockY(); int blockID = plugin.getLocalConfiguration().cauldronSettings.cauldronBlock; // Used to store cauldron blocks -- walls are counted Map<BlockWorldVector, Integer> visited = new HashMap<BlockWorldVector, Integer>(); try { // The following attempts to recursively find adjacent blocks so // that it can find all the blocks used within the cauldron findCauldronContents(world, pt, rootY - 1, rootY, visited); // We want cauldrons of a specific shape and size, and 24 is just // the right number of blocks that the cauldron we want takes up -- // nice and cheap check if (visited.size() != 24) { throw new NotACauldronException("mech.cauldron.too-small"); } // Key is the block ID and the value is the amount Map<Integer, Integer> contents = new HashMap<Integer, Integer>(); // Now we have to ignore cauldron blocks so that we get the real // contents of the cauldron for (Map.Entry<BlockWorldVector, Integer> entry : visited.entrySet()) { if (entry.getValue() != blockID) { if (!contents.containsKey(entry.getValue())) { contents.put(entry.getValue(), 1); } else { contents.put(entry.getValue(), contents.get(entry.getValue()) + 1); } } } // Find the recipe CauldronCookbook.Recipe recipe = recipes.find(contents); if (recipe != null) { String[] groups = recipe.getGroups(); if (groups != null) { boolean found = false; for (@SuppressWarnings("unused") String group : groups) { found = true; break; // TODO: Add an isInGroup method /* * if (player.isInGroup(group)) { found = true; break; } */ } if (!found) { player.sendMessage(ChatColor.DARK_RED + "Doesn't seem as if you have the ability..."); return; } } player.sendMessage( ChatColor.GOLD + "In a poof of smoke, you've made " + recipe.getName() + "."); List<Integer> ingredients = new ArrayList<Integer>(recipe.getIngredients()); List<BlockWorldVector> removeQueue = new ArrayList<BlockWorldVector>(); // Get rid of the blocks in world for (Map.Entry<BlockWorldVector, Integer> entry : visited.entrySet()) { // This is not a fast operation, but we should not have // too many ingredients if (ingredients.contains(entry.getValue())) { // Some blocks need to removed first otherwise they will // drop an item, so let's remove those first // if // (!BlockID.isBottomDependentBlock(entry.getValue())) { // removeQueue.add(entry.getKey()); // } else { world .getBlockAt( entry.getKey().getBlockX(), entry.getKey().getBlockY(), entry.getKey().getBlockZ()) .setType(Material.AIR); // } ingredients.remove(entry.getValue()); } } for (BlockWorldVector v : removeQueue) { world.getBlockAt(v.getBlockX(), v.getBlockY(), v.getBlockZ()).setType(Material.AIR); } // Give results for (Integer id : recipe.getResults()) { HashMap<Integer, ItemStack> map = player.getInventory().addItem(new ItemStack(id, 1)); for (Entry<Integer, ItemStack> i : map.entrySet()) { world.dropItem(player.getLocation(), i.getValue()); } } // Didn't find a recipe } else { player.sendMessage(ChatColor.RED + "Hmm, this doesn't make anything..."); } } catch (NotACauldronException ignored) { } }
/** * Toggle lights in the immediate area. * * @param pt * @return true if the block was recogized as a lightswitch; this may or may not mean that any * lights were actually toggled. */ private boolean toggleLights(BlockWorldVector pt) { World world = BukkitUtil.toWorld(pt); Block block = BukkitUtil.toBlock(pt); // check if this looks at all like something we're interested in first if (block.getTypeId() != BlockID.WALL_SIGN) return false; int radius = 10; int maximum = 20; try { radius = Integer.parseInt(((Sign) block.getState()).getLine(2)); } catch (Exception ignored) { } try { maximum = Integer.parseInt(((Sign) block.getState()).getLine(3)); } catch (Exception ignored) { } if (radius > plugin.getLocalConfiguration().lightSwitchSettings.maxRange) { radius = plugin.getLocalConfiguration().lightSwitchSettings.maxRange; } if (maximum > plugin.getLocalConfiguration().lightSwitchSettings.maxMaximum) { maximum = plugin.getLocalConfiguration().lightSwitchSettings.maxMaximum; } int wx = pt.getBlockX(); int wy = pt.getBlockY(); int wz = pt.getBlockZ(); int aboveID = world.getBlockTypeIdAt(wx, wy + 1, wz); if (aboveID == BlockID.TORCH || aboveID == BlockID.REDSTONE_TORCH_OFF || aboveID == BlockID.REDSTONE_TORCH_ON) { // Check if block above is a redstone torch. // Used to get what to change torches to. boolean on = aboveID != BlockID.TORCH; // Prevent spam Long lastUse = recentLightToggles.remove(pt); long currTime = System.currentTimeMillis(); if (lastUse != null && currTime - lastUse < 500) { recentLightToggles.put(pt, lastUse); return true; } recentLightToggles.put(pt, currTime); int changed = 0; for (int x = -radius + wx; x <= radius + wx; x++) { for (int y = -radius + wy; y <= radius + wy; y++) { for (int z = -radius + wz; z <= radius + wz; z++) { int id = world.getBlockTypeIdAt(x, y, z); if (id == BlockID.TORCH || id == BlockID.REDSTONE_TORCH_OFF || id == BlockID.REDSTONE_TORCH_ON) { // Limit the maximum number of changed lights if (changed >= maximum) return true; if (on) { world.getBlockAt(x, y, z).setTypeId(BlockID.TORCH); } else { world.getBlockAt(x, y, z).setTypeId(BlockID.REDSTONE_TORCH_ON); } changed++; } } } } return true; } return false; }
public static Block toBlock(BlockWorldVector pt) { return toWorld(pt).getBlock(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); }