com.wasteofplastic.beaconz.BeaconObj.java Source code

Java tutorial

Introduction

Here is the source code for com.wasteofplastic.beaconz.BeaconObj.java

Source

/*
 * Copyright (c) 2015 - 2016 tastybento
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.wasteofplastic.beaconz;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.scoreboard.Team;

/**
 * Represents a beacon
 * @author tastybento
 *
 */
public class BeaconObj extends BeaconzPluginDependent {
    private Point2D location;
    private int x;
    private int z;
    private int y;
    private long hackTimer;
    private Team ownership;
    //private Set<BeaconObj> links;
    private Integer id = null;
    private boolean newBeacon = true;
    private static final List<BlockFace> FACES = new ArrayList<BlockFace>(
            Arrays.asList(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH_EAST,
                    BlockFace.NORTH_WEST, BlockFace.SOUTH_EAST, BlockFace.SOUTH_WEST));
    private HashMap<Block, DefenseBlock> defenseBlocks = new HashMap<Block, DefenseBlock>();
    private Set<BeaconObj> links = new HashSet<BeaconObj>();

    /**
     * Represents a beacon
     * @param plugin
     * @param x
     * @param y
     * @param z
     * @param owner
     */
    public BeaconObj(Beaconz plugin, int x, int y, int z, Team owner) {
        super(plugin);
        this.location = new Point2D.Double(x, z);
        this.x = x;
        this.z = z;
        this.y = y;
        this.hackTimer = System.currentTimeMillis();
        this.ownership = owner;
        this.links.clear();
        this.newBeacon = true;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getZ() {
        return z;
    }

    /**
     * @return the location of this beacon
     */
    public Point2D getPoint() {
        return location;
    }

    /**
     * @return the Bukkit location of this beacon
     */
    public Location getLocation() {
        return new Location(getBeaconzWorld(), location.getX(), y, location.getY());
    }

    /**
     * @return the beacons directly linked from this beacon
     */
    public Set<BeaconObj> getLinks() {
        return links;
    }

    /**
     * Add a link from this beacon to another beacon and make a return link
     * @param beaconPair
     * @return true if link made, false if not
     */
    public boolean addOutboundLink(BeaconObj beacon) {
        //getLogger().info("DEBUG: Trying to add link");
        // There is a max of 8 outgoing links allowed
        if (links.size() == 8) {
            //getLogger().info("DEBUG: outbound link limit reached");
            return false;
        }
        beacon.addLink(this);
        links.add(beacon);
        return true;

    }

    /**
     * Called by another beacon. Adds a link
     * @param beaconPair
     */
    public void addLink(BeaconObj beacon) {
        links.add(beacon);
    }

    /**
     * @return the hackTimer
     */
    public long getHackTimer() {
        return hackTimer;
    }

    public void resetHackTimer() {
        this.hackTimer = System.currentTimeMillis();
        this.newBeacon = false;
    }

    /**
     * @return the ownership
     */
    public Team getOwnership() {
        return ownership;
    }

    /**
     * @param ownership the ownership to set
     */
    public void setOwnership(Team ownership) {
        this.ownership = ownership;
    }

    /**
     * @return the number of links
     */
    public int getNumberOfLinks() {
        return links.size();
    }

    /**
     * Name for this beacon based on its coordinates
     * @return
     */
    public String getName() {
        return x + ", " + z;
    }

    public void setId(int indexOf) {
        id = indexOf;
    }

    /**
     * @return the id
     */
    public Integer getId() {
        return id;
    }

    /**
     * Remove link from this beacon to the specified beacon
     * @param beacon
     */
    public void removeLink(BeaconObj beacon) {
        // Devisualize the link
        new LineVisualizer(this.getBeaconzPlugin(), new BeaconLink(this, beacon), false);
        // remove the link
        links.remove(beacon);
    }

    /**
     * Remove all links from this beacon
     */
    public void removeLinks() {
        links.clear();
    }

    /**
     * @return the height
     */
    public int getHeight() {
        return y;
    }

    /**
     * @return the newBeacon
     */
    public boolean isNewBeacon() {
        return newBeacon;
    }

    /**
     * Checks if a beacon base is clear of blocks and above the blocks all the way to the sky
     * @return true if clear, false if not
     */
    public boolean isClear() {
        Block beacon = getBeaconzWorld().getBlockAt((int) location.getX(), y, (int) location.getY());
        //getLogger().info("DEBUG: block y = " + beacon.getY() + " " + beacon.getLocation());
        for (BlockFace face : FACES) {
            Block block = beacon.getRelative(face);
            //getLogger().info("DEBUG: highest block at " + block.getX() + "," + block.getZ() + " y = " + getHighestBlockYAt(block.getX(), block.getZ()));
            if (block.getY() != getHighestBlockYAt(block.getX(), block.getZ())) {
                return false;
            }
        }
        // Check all the defense blocks too
        for (Point2D point : getRegister().getDefensesAtBeacon(this)) {
            beacon = getBeaconzWorld().getBlockAt((int) point.getX(), y, (int) point.getY());
            if (beacon.getY() != getHighestBlockYAt((int) point.getX(), (int) point.getY())) {
                return false;
            }
        }
        return true;
    }

    /**
     * Tracks defense blocks
     * @param block
     * @param levelRequired
     * @param uuid 
     */
    public void addDefenseBlock(Block block, int levelRequired, UUID uuid) {
        defenseBlocks.put(block, new DefenseBlock(block, levelRequired, uuid));
    }

    /**
     * Tracks defense blocks
     * @param block
     * @param levelRequired
     * @param uuid 
     */
    public void addDefenseBlock(Block block, int levelRequired, String uuid) {
        defenseBlocks.put(block, new DefenseBlock(block, levelRequired, uuid));
    }

    /**
     * Removes the defense block
     * @param block
     */
    public void removeDefenseBlock(Block block) {
        defenseBlocks.remove(block);
    }

    /**
     * @return the defenseBlocks
     */
    public HashMap<Block, DefenseBlock> getDefenseBlocks() {
        return defenseBlocks;
    }

    /**
     * @param defenseBlocks the defenseBlocks to set
     */
    public void setDefenseBlocks(HashMap<Block, DefenseBlock> defenseBlocks) {
        this.defenseBlocks = defenseBlocks;
    }

    /**
     * Removes the longest link. Used when a link block is removed.
     * @return true if a link was actually removed
     */
    public boolean removeLongestLink() {
        BeaconObj furthest = null;
        double maxDistance = 0;
        for (BeaconObj link : links) {
            double distance = location.distanceSq(link.getPoint());
            if (distance > maxDistance) {
                maxDistance = distance;
                furthest = link;
            }
        }
        if (furthest != null) {
            // Remove link from both ends
            furthest.removeLink(this);
            removeLink(furthest);
            // Remove any triangles related to these two beaconz
            Iterator<TriangleField> it = getRegister().getTriangleFields().iterator();
            while (it.hasNext()) {
                TriangleField triangle = it.next();
                if (triangle.hasVertex(this.location) && triangle.hasVertex(furthest.location)) {
                    // Find any players in the triangle being removed
                    for (Player player : getServer().getOnlinePlayers()) {
                        if (getBeaconzWorld().equals(player.getWorld())) {
                            if (triangle.contains(
                                    new Point2D.Double(player.getLocation().getX(), player.getLocation().getZ()))) {
                                // Player is in triangle, remove effects
                                for (PotionEffect effect : getPml().getTriangleEffects(player.getUniqueId()))
                                    player.removePotionEffect(effect.getType());
                            }
                        }
                    }
                    it.remove();
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Checks the integrity of the beacon and fixes it if required
     */
    public void checkIntegrity() {
        //Bukkit.getLogger().info("DEBUG: made beacon at " + (source.getX() * 16 + x) + " " + y + " " + (source.getZ()*16 + z) );
        Block b = getBeaconzWorld().getBlockAt(x, y, z);
        if (!b.getType().equals(Material.BEACON)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing beacon block!");
            b.setType(Material.BEACON);
        }
        // Check the capstone
        if (ownership == null && !b.getRelative(BlockFace.UP).getType().equals(Material.OBSIDIAN)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing capstone block!");
            b.getRelative(BlockFace.UP).setType(Material.OBSIDIAN);
        }

        if (ownership != null && (!b.getRelative(BlockFace.UP).getType().equals(Material.STAINED_GLASS)
                && b.getRelative(BlockFace.UP).getData() != Settings.teamBlock.get(ownership).getData())) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing team glass block!");
            b.getRelative(BlockFace.UP).setType(Material.STAINED_GLASS);
            b.getRelative(BlockFace.UP).setData(Settings.teamBlock.get(ownership).getData());
        }
        // Create the pyramid
        b = b.getRelative(BlockFace.DOWN);

        // All diamond blocks for now
        if (!b.getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing diamond block!");
            b.setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.SOUTH).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing S diamond block!");
            b.getRelative(BlockFace.SOUTH).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.SOUTH_EAST).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing SE diamond block!");
            b.getRelative(BlockFace.SOUTH_EAST).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.SOUTH_WEST).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing SW diamond block!");
            b.getRelative(BlockFace.SOUTH_WEST).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.EAST).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing E diamond block!");
            b.getRelative(BlockFace.EAST).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.WEST).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing W diamond block!");
            b.getRelative(BlockFace.WEST).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.NORTH).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing N diamond block!");
            b.getRelative(BlockFace.NORTH).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.NORTH_EAST).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing NE diamond block!");
            b.getRelative(BlockFace.NORTH_EAST).setType(Material.DIAMOND_BLOCK);
        }
        if (!b.getRelative(BlockFace.NORTH_WEST).getType().equals(Material.DIAMOND_BLOCK)) {
            getLogger().severe("Beacon at " + x + " " + y + " " + z + " missing SW diamond block!");
            b.getRelative(BlockFace.NORTH_WEST).setType(Material.DIAMOND_BLOCK);
        }
    }

    /** 
     * Get the level of the highest defense block placed on top of a beacon
     * @return
     */
    public int getHighestBlockLevel() {
        int highestBlock = 0;
        Iterator<Entry<Block, DefenseBlock>> it = this.getDefenseBlocks().entrySet().iterator();
        while (it.hasNext()) {
            Entry<Block, DefenseBlock> defenseBlock = it.next();
            if (defenseBlock.getKey().getType().equals(Material.AIR)) {
                // Clean up if any blocks have been removed by creatives, or moved due to gravity.
                it.remove();
            } else {
                highestBlock = Math.max(highestBlock, defenseBlock.getValue().getLevel());
            }
        }
        return highestBlock;
    }

    /** 
     * Get the height of the highest defense block placed on top of a beacon
     * @return
     */
    public int getHighestBlockY() {
        int highestBlock = y;
        Iterator<Entry<Block, DefenseBlock>> it = this.getDefenseBlocks().entrySet().iterator();
        while (it.hasNext()) {
            Entry<Block, DefenseBlock> defenseBlock = it.next();
            if (defenseBlock.getKey().getType().equals(Material.AIR)) {
                // Clean up if any blocks have been removed by creatives, or moved due to gravity.
                it.remove();
            } else {
                highestBlock = Math.max(highestBlock, defenseBlock.getValue().getBlock().getY());
            }
        }
        return highestBlock;
    }

    /**
     * Checks if any layer above the beacon matches the locking criteria
     * This is a mechanism to allow for permanent ownership of beacons
     * If any one level above the beacon has a certain number of "locking blocks", these blocks are unbreakable by all but the owner team
     * 
     * @return
     */
    public Boolean isLocked() {
        Boolean rc = false;
        int maxHeight = getHighestBlockY();
        for (int i = y; i <= maxHeight; i++) {
            if (nbrToLock(i) <= 0) {
                rc = true;
                break;
            }
        }
        return rc;
    }

    /**
     * Returns the number of "locking blocks" that must still be placed at the given height to block the beacon
     * The returned integer can be negative, indicating that more than enough blocks have been placed
     * Locking blocks could be defined in Settings, but I really think we should stick with Settings.linkRewards (default: Emerald)
     * The number of locking blocks required is proportional to the number of players in the teams. 
     * The number given in Settings applies to the largest team, all others go down proportionately.
     * 
     * @param height
     * @return
     */

    public int nbrToLock(int height) {

        Integer maxLocking = Settings.nbrLockingBlocks;
        Material lockingBlock = Material.EMERALD_BLOCK;
        int missing = maxLocking;

        // Make sure the height is above the beacon
        if (height >= y) {

            // Get the locking block material
            if (Material.getMaterial(Settings.lockingBlock.toUpperCase()) != null) {
                lockingBlock = Material.getMaterial(Settings.lockingBlock.toUpperCase());
            }

            // Figure out how many locking blocks we need for the owner team
            Integer reqLocking = 3;
            Integer maxSize = ownership.getSize();
            /*for (Team t : ownership.getScoreboard().getTeams()) {
            if (t.getSize() > maxSize) {
                maxSize = t.getSize();
            }
            }*/
            Scorecard sc = getGameMgr().getSC(ownership);
            if (sc != null) {
                for (Team t : sc.getTeams()) {
                    if (t.getSize() > maxSize) {
                        maxSize = t.getSize();
                    }
                }
            }
            reqLocking = maxLocking * ownership.getSize() / maxSize; // integer division...
            if (reqLocking > 8)
                reqLocking = 8; // ensure it's at most 8 blocks

            // See how many locking blocks are present at the given height
            int lockBlocks = 0;
            Material lockBlock = lockingBlock;
            Block b = getBeaconzWorld().getBlockAt(x, height, z);
            if (b.getType().equals(lockBlock))
                missing--;
            if (b.getRelative(BlockFace.SOUTH).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.SOUTH_EAST).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.SOUTH_WEST).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.EAST).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.WEST).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.NORTH).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.NORTH_EAST).getType().equals(lockBlock))
                lockBlocks++;
            if (b.getRelative(BlockFace.NORTH_WEST).getType().equals(lockBlock))
                lockBlocks++;

            missing = reqLocking - lockBlocks;
        }

        return missing;
    }

}