Package com.sk89q.craftbook.mech

Source Code of com.sk89q.craftbook.mech.Cauldron

package com.sk89q.craftbook.mech;
// $Id$
/*
* CraftBook
* Copyright (C) 2010 sk89q <http://www.sk89q.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import com.sk89q.craftbook.BlockType;
import com.sk89q.craftbook.Colors;
import com.sk89q.craftbook.access.PlayerInterface;
import com.sk89q.craftbook.access.WorldInterface;
import com.sk89q.craftbook.util.BlockVector;
import com.sk89q.craftbook.util.Vector;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Handler for cauldrons.
*
* @author sk89q
*/
public class Cauldron {

    /**
     * Stores the recipes.
     */
    private CauldronCookbook recipes;

    /**
     * Construct the handler.
     *
     * @param recipes
     */
    public Cauldron(CauldronCookbook recipes) {

        this.recipes = recipes;
    }

    /**
     * Thrown when a suspected formation is not actually a valid cauldron.
     */
    private class NotACauldronException extends Exception {

        private static final long serialVersionUID = 3091428924893050849L;

        /**
         * Construct the exception with a message.
         *
         * @param msg
         */
        public NotACauldronException(String msg) {

            super(msg);
        }
    }

    /**
     * Do cauldron.
     *
     * @param pt
     * @param player
     */
    public void preCauldron(PlayerInterface player, WorldInterface world, Vector pt) {

        int x = pt.getBlockX();
        int y = pt.getBlockY();
        int z = pt.getBlockZ();

        int rootY = y;
        int below = world.getId(x, y - 1, z);
        int below2 = world.getId(x, y - 2, z);
        int s1 = world.getId(x + 1, y, z);
        int s3 = world.getId(x - 1, y, z);
        int s2 = world.getId(x, y, z + 1);
        int s4 = world.getId(x, y, z - 1);

        // Preliminary check so we don't waste CPU cycles
        if ((BlockType.isLava(below) || BlockType.isLava(below2))
                && (s1 == BlockType.STONE || s2 == BlockType.STONE
                || s3 == BlockType.STONE || s4 == BlockType.STONE)) {
            // Cauldron is 2 units deep
            if (BlockType.isLava(below)) {
                rootY++;
            }

            performCauldron(player, world, new BlockVector(x, rootY, z));
        }
    }

    /**
     * Attempt to perform a cauldron recipe.
     *
     * @param pt
     * @param player
     * @param recipes
     */
    private void performCauldron(PlayerInterface player, WorldInterface world, BlockVector pt) {
        // Gotta start at a root Y then find our orientation
        int rootY = pt.getBlockY();

        // Used to store cauldron blocks -- walls are counted
        Map<BlockVector, Integer> visited = new HashMap<BlockVector, 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("Cauldron is 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 stone blocks so that we get the real
            // contents of the cauldron
            for (Map.Entry<BlockVector, Integer> entry : visited.entrySet()) {
                if (entry.getValue() != BlockType.STONE) {
                    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 (String group : groups) {
                        if (player.isInGroup(group)) {
                            found = true;
                            break;
                        }
                    }

                    if (!found) {
                        player.sendMessage(Colors.DARK_RED + "Doesn't seem as if you have the ability...");
                        return;
                    }
                }

                player.sendMessage(Colors.GOLD + "In a poof of smoke, you've made "
                        + recipe.getName() + ".");

                List<Integer> ingredients =
                        new ArrayList<Integer>(recipe.getIngredients());

                List<BlockVector> removeQueue = new ArrayList<BlockVector>();

                // Get rid of the blocks in world
                for (Map.Entry<BlockVector, 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 (!BlockType.isBottomDependentBlock(entry.getValue())) {
                            removeQueue.add(entry.getKey());
                        } else {
                            world.setId(entry.getKey(), 0);
                        }
                        ingredients.remove(entry.getValue());
                    }
                }

                for (BlockVector v : removeQueue) {
                    world.setId(v, 0);
                }

                // Give results
                for (Integer id : recipe.getResults()) {
                    player.giveItem(id, 1);
                }
                // Didn't find a recipe
            } else {
                player.sendMessage(Colors.RED + "Hmm, this doesn't make anything...");
            }
        } catch (NotACauldronException e) {
        }
    }

    /**
     * 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 pt
     * @param minY
     * @param maxY
     * @param visited
     *
     * @throws Cauldron.NotACauldronException
     */
    public void findCauldronContents(WorldInterface world, BlockVector pt, int minY, int maxY,
                                     Map<BlockVector, Integer> visited) throws NotACauldronException {

        // 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("Cauldron has a leak");
        }

        // Prevent infinite looping
        if (visited.containsKey(pt)) {
            return;
        }

        int type = world.getId(pt);

        // 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 == BlockType.STONE) {
            return;
        }

        // Must have a lava floor
        Vector lavaPos = pt.subtract(0, pt.getBlockY() - minY + 1, 0);
        if (!BlockType.isLava(world.getId(lavaPos))) {
            throw new NotACauldronException("Cauldron lacks lava below");
        }

        // Now we recurse!
        findCauldronContents(world, pt.add(1, 0, 0).toBlockVector(), minY, maxY, visited);
        findCauldronContents(world, pt.add(-1, 0, 0).toBlockVector(), minY, maxY, visited);
        findCauldronContents(world, pt.add(0, 0, 1).toBlockVector(), minY, maxY, visited);
        findCauldronContents(world, pt.add(0, 0, -1).toBlockVector(), minY, maxY, visited);
        findCauldronContents(world, pt.add(0, 1, 0).toBlockVector(), minY, maxY, visited);
        findCauldronContents(world, pt.add(0, -1, 0).toBlockVector(), minY, maxY, visited);
    }
}
TOP

Related Classes of com.sk89q.craftbook.mech.Cauldron

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');