Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
at develop 375 lines 12 kB view raw
1package org.bukkit.util; 2 3import org.bukkit.Location; 4import org.bukkit.World; 5import org.bukkit.block.Block; 6import org.bukkit.block.BlockFace; 7import org.bukkit.entity.LivingEntity; 8 9import java.util.Iterator; 10import java.util.NoSuchElementException; 11 12/** 13 * This class performs ray tracing and iterates along blocks on a line 14 * 15 * @author raphfrk 16 */ 17 18public class BlockIterator implements Iterator<Block> { 19 20 private final World world; 21 private final int maxDistance; 22 23 private static final int gridSize = 1 << 24; 24 25 private boolean end = false; 26 27 private Block[] blockQueue = new Block[3]; 28 private int currentBlock = 0; 29 private int currentDistance = 0; 30 private int maxDistanceInt; 31 32 private int secondError; 33 private int thirdError; 34 35 private int secondStep; 36 private int thirdStep; 37 38 private BlockFace mainFace; 39 private BlockFace secondFace; 40 private BlockFace thirdFace; 41 42 /** 43 * Constructs the BlockIterator 44 * 45 * @param world The world to use for tracing 46 * @param start A Vector giving the initial location for the trace 47 * @param direction A Vector pointing in the direction for the trace 48 * @param yOffset The trace begins vertically offset from the start vector by this value 49 * @param maxDistance This is the maximum distance in blocks for the trace. Setting this value above 140 may lead to problems with unloaded chunks. A value of 0 indicates no limit 50 */ 51 52 public BlockIterator(World world, Vector start, Vector direction, double yOffset, int maxDistance) { 53 this.world = world; 54 this.maxDistance = maxDistance; 55 56 Vector startClone = start.clone(); 57 58 startClone.setY(startClone.getY() + yOffset); 59 60 currentDistance = 0; 61 62 double mainDirection = 0; 63 double secondDirection = 0; 64 double thirdDirection = 0; 65 66 double mainPosition = 0; 67 double secondPosition = 0; 68 double thirdPosition = 0; 69 70 Block startBlock = world.getBlockAt((int) Math.floor(startClone.getX()), (int) Math.floor(startClone.getY()), (int) Math.floor(startClone.getZ())); 71 72 if (getXLength(direction) > mainDirection) { 73 mainFace = getXFace(direction); 74 mainDirection = getXLength(direction); 75 mainPosition = getXPosition(direction, startClone, startBlock); 76 77 secondFace = getYFace(direction); 78 secondDirection = getYLength(direction); 79 secondPosition = getYPosition(direction, startClone, startBlock); 80 81 thirdFace = getZFace(direction); 82 thirdDirection = getZLength(direction); 83 thirdPosition = getZPosition(direction, startClone, startBlock); 84 } 85 if (getYLength(direction) > mainDirection) { 86 mainFace = getYFace(direction); 87 mainDirection = getYLength(direction); 88 mainPosition = getYPosition(direction, startClone, startBlock); 89 90 secondFace = getZFace(direction); 91 secondDirection = getZLength(direction); 92 secondPosition = getZPosition(direction, startClone, startBlock); 93 94 thirdFace = getXFace(direction); 95 thirdDirection = getXLength(direction); 96 thirdPosition = getXPosition(direction, startClone, startBlock); 97 } 98 if (getZLength(direction) > mainDirection) { 99 mainFace = getZFace(direction); 100 mainDirection = getZLength(direction); 101 mainPosition = getZPosition(direction, startClone, startBlock); 102 103 secondFace = getXFace(direction); 104 secondDirection = getXLength(direction); 105 secondPosition = getXPosition(direction, startClone, startBlock); 106 107 thirdFace = getYFace(direction); 108 thirdDirection = getYLength(direction); 109 thirdPosition = getYPosition(direction, startClone, startBlock); 110 } 111 112 // trace line backwards to find intercept with plane perpendicular to the main axis 113 114 double d = mainPosition / mainDirection; // how far to hit face behind 115 double secondd = secondPosition - secondDirection * d; 116 double thirdd = thirdPosition - thirdDirection * d; 117 118 // Guarantee that the ray will pass though the start block. 119 // It is possible that it would miss due to rounding 120 // This should only move the ray by 1 grid position 121 secondError = (int) (Math.floor(secondd * gridSize)); 122 secondStep = (int) (Math.round(secondDirection / mainDirection * gridSize)); 123 thirdError = (int) (Math.floor(thirdd * gridSize)); 124 thirdStep = (int) (Math.round(thirdDirection / mainDirection * gridSize)); 125 126 if (secondError + secondStep <= 0) { 127 secondError = -secondStep + 1; 128 } 129 130 if (thirdError + thirdStep <= 0) { 131 thirdError = -thirdStep + 1; 132 } 133 134 Block lastBlock; 135 136 lastBlock = startBlock.getRelative(reverseFace(mainFace)); 137 138 if (secondError < 0) { 139 secondError += gridSize; 140 lastBlock = lastBlock.getRelative(reverseFace(secondFace)); 141 } 142 143 if (thirdError < 0) { 144 thirdError += gridSize; 145 lastBlock = lastBlock.getRelative(reverseFace(thirdFace)); 146 } 147 148 // This means that when the variables are positive, it means that the coord=1 boundary has been crossed 149 secondError -= gridSize; 150 thirdError -= gridSize; 151 152 blockQueue[0] = lastBlock; 153 currentBlock = -1; 154 155 scan(); 156 157 boolean startBlockFound = false; 158 159 for (int cnt = currentBlock; cnt >= 0; cnt--) { 160 if (blockEquals(blockQueue[cnt], startBlock)) { 161 currentBlock = cnt; 162 startBlockFound = true; 163 break; 164 } 165 } 166 167 if (!startBlockFound) { 168 throw new IllegalStateException("Start block missed in BlockIterator"); 169 } 170 171 // Calculate the number of planes passed to give max distance 172 maxDistanceInt = (int) Math.round(maxDistance / (Math.sqrt(mainDirection * mainDirection + secondDirection * secondDirection + thirdDirection * thirdDirection) / mainDirection)); 173 174 } 175 176 private boolean blockEquals(Block a, Block b) { 177 return a.getX() == b.getX() && a.getY() == b.getY() && a.getZ() == b.getZ(); 178 } 179 180 private BlockFace reverseFace(BlockFace face) { 181 switch (face) { 182 case UP: 183 return BlockFace.DOWN; 184 185 case DOWN: 186 return BlockFace.UP; 187 188 case NORTH: 189 return BlockFace.SOUTH; 190 191 case SOUTH: 192 return BlockFace.NORTH; 193 194 case EAST: 195 return BlockFace.WEST; 196 197 case WEST: 198 return BlockFace.EAST; 199 200 default: 201 return null; 202 } 203 } 204 205 private BlockFace getXFace(Vector direction) { 206 return ((direction.getX() > 0) ? BlockFace.SOUTH : BlockFace.NORTH); 207 } 208 209 private BlockFace getYFace(Vector direction) { 210 return ((direction.getY() > 0) ? BlockFace.UP : BlockFace.DOWN); 211 } 212 213 private BlockFace getZFace(Vector direction) { 214 return ((direction.getZ() > 0) ? BlockFace.WEST : BlockFace.EAST); 215 } 216 217 private double getXLength(Vector direction) { 218 return (Math.abs(direction.getX())); 219 } 220 221 private double getYLength(Vector direction) { 222 return (Math.abs(direction.getY())); 223 } 224 225 private double getZLength(Vector direction) { 226 return (Math.abs(direction.getZ())); 227 } 228 229 private double getPosition(double direction, double position, int blockPosition) { 230 return direction > 0 ? (position - blockPosition) : (blockPosition + 1 - position); 231 } 232 233 private double getXPosition(Vector direction, Vector position, Block block) { 234 return getPosition(direction.getX(), position.getX(), block.getX()); 235 } 236 237 private double getYPosition(Vector direction, Vector position, Block block) { 238 return getPosition(direction.getY(), position.getY(), block.getY()); 239 } 240 241 private double getZPosition(Vector direction, Vector position, Block block) { 242 return getPosition(direction.getZ(), position.getZ(), block.getZ()); 243 } 244 245 /** 246 * Constructs the BlockIterator 247 * 248 * @param loc The location for the start of the ray trace 249 * @param yOffset The trace begins vertically offset from the start vector by this value 250 * @param maxDistance This is the maximum distance in blocks for the trace. Setting this value above 140 may lead to problems with unloaded chunks. A value of 0 indicates no limit 251 */ 252 253 public BlockIterator(Location loc, double yOffset, int maxDistance) { 254 this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, maxDistance); 255 } 256 257 /** 258 * Constructs the BlockIterator. 259 * 260 * @param loc The location for the start of the ray trace 261 * @param yOffset The trace begins vertically offset from the start vector by this value 262 */ 263 264 public BlockIterator(Location loc, double yOffset) { 265 this(loc.getWorld(), loc.toVector(), loc.getDirection(), yOffset, 0); 266 } 267 268 /** 269 * Constructs the BlockIterator. 270 * 271 * @param loc The location for the start of the ray trace 272 */ 273 274 public BlockIterator(Location loc) { 275 this(loc, 0D); 276 } 277 278 /** 279 * Constructs the BlockIterator. 280 * 281 * @param entity Information from the entity is used to set up the trace 282 * @param maxDistance This is the maximum distance in blocks for the trace. Setting this value above 140 may lead to problems with unloaded chunks. A value of 0 indicates no limit 283 */ 284 285 public BlockIterator(LivingEntity entity, int maxDistance) { 286 this(entity.getLocation(), entity.getEyeHeight(), maxDistance); 287 } 288 289 /** 290 * Constructs the BlockIterator. 291 * 292 * @param entity Information from the entity is used to set up the trace 293 */ 294 295 public BlockIterator(LivingEntity entity) { 296 this(entity, 0); 297 } 298 299 /** 300 * Returns true if the iteration has more elements 301 */ 302 303 public boolean hasNext() { 304 scan(); 305 return currentBlock != -1; 306 } 307 308 /** 309 * Returns the next Block in the trace 310 * 311 * @return the next Block in the trace 312 */ 313 314 public Block next() { 315 scan(); 316 if (currentBlock <= -1) { 317 throw new NoSuchElementException(); 318 } else { 319 return blockQueue[currentBlock--]; 320 } 321 } 322 323 public void remove() { 324 throw new UnsupportedOperationException("[BlockIterator] doesn't support block removal"); 325 } 326 327 private void scan() { 328 if (currentBlock >= 0) { 329 return; 330 } 331 if (maxDistance != 0 && currentDistance > maxDistanceInt) { 332 end = true; 333 return; 334 } 335 if (end) { 336 return; 337 } 338 339 currentDistance++; 340 341 secondError += secondStep; 342 thirdError += thirdStep; 343 344 if (secondError > 0 && thirdError > 0) { 345 blockQueue[2] = blockQueue[0].getRelative(mainFace); 346 if (((long) secondStep) * ((long) thirdError) < ((long) thirdStep) * ((long) secondError)) { 347 blockQueue[1] = blockQueue[2].getRelative(secondFace); 348 blockQueue[0] = blockQueue[1].getRelative(thirdFace); 349 } else { 350 blockQueue[1] = blockQueue[2].getRelative(thirdFace); 351 blockQueue[0] = blockQueue[1].getRelative(secondFace); 352 } 353 thirdError -= gridSize; 354 secondError -= gridSize; 355 currentBlock = 2; 356 return; 357 } else if (secondError > 0) { 358 blockQueue[1] = blockQueue[0].getRelative(mainFace); 359 blockQueue[0] = blockQueue[1].getRelative(secondFace); 360 secondError -= gridSize; 361 currentBlock = 1; 362 return; 363 } else if (thirdError > 0) { 364 blockQueue[1] = blockQueue[0].getRelative(mainFace); 365 blockQueue[0] = blockQueue[1].getRelative(thirdFace); 366 thirdError -= gridSize; 367 currentBlock = 1; 368 return; 369 } else { 370 blockQueue[0] = blockQueue[0].getRelative(mainFace); 371 currentBlock = 0; 372 return; 373 } 374 } 375}