001package squidpony.squidai; 002 003import squidpony.annotation.GwtIncompatible; 004import squidpony.squidgrid.FOVCache; 005import squidpony.squidgrid.Radius; 006import squidpony.squidgrid.Spill; 007import squidpony.squidmath.Coord; 008import squidpony.squidmath.LightRNG; 009import squidpony.squidmath.RNG; 010 011import java.util.*; 012 013/** 014 * An AOE type that has a center and a volume, and will randomly expand in all directions until it reaches volume or 015 * cannot expand further. Specify the RadiusType as Radius.DIAMOND for Manhattan distance (and the best results), 016 * RADIUS.SQUARE for Chebyshev, or RADIUS.CIRCLE for Euclidean. You can specify a seed for the RNG and a fresh RNG will 017 * be used for all random expansion; the RNG will reset to the specified seed after each generation so the same 018 * CloudAOE can be used in different places by just changing the center. You can cause the CloudAOE to not reset after 019 * generating each time by using setExpanding(true) and cause it to reset after the next generation by setting it back 020 * to the default of false. If expanding is true, then multiple calls to findArea with the same center and larger 021 * volumes will produce more solid clumps of affected area with fewer gaps, and can be spaced out over multiple calls. 022 * 023 * This will produce doubles for its findArea() method which are equal to 1.0. 024 * 025 * This class uses squidpony.squidgrid.Spill to create its area of effect. 026 * Created by Tommy Ettinger on 7/13/2015. 027 */ 028public class CloudAOE implements AOE { 029 private Spill spill; 030 private Coord center, origin = null; 031 private int volume; 032 private long seed; 033 private boolean expanding; 034 private Radius rt; 035 private Reach reach = new Reach(1, 1, Radius.SQUARE, null); 036 private char[][] dungeon; 037 038 public CloudAOE(Coord center, int volume, Radius radiusType) 039 { 040 LightRNG l = new LightRNG(); 041 seed = l.getState(); 042 spill = new Spill(new RNG(l)); 043 this.center = center; 044 this.volume = volume; 045 expanding = false; 046 rt = radiusType; 047 switch (radiusType) 048 { 049 case SPHERE: 050 case CIRCLE: 051 spill.measurement = Spill.Measurement.EUCLIDEAN; 052 break; 053 case CUBE: 054 case SQUARE: 055 spill.measurement = Spill.Measurement.CHEBYSHEV; 056 break; 057 default: 058 spill.measurement = Spill.Measurement.MANHATTAN; 059 break; 060 } 061 } 062 063 public CloudAOE(Coord center, int volume, Radius radiusType, int minRange, int maxRange) 064 { 065 LightRNG l = new LightRNG(); 066 seed = l.getState(); 067 spill = new Spill(new RNG(l)); 068 this.center = center; 069 this.volume = volume; 070 expanding = false; 071 rt = radiusType; 072 reach.minDistance = minRange; 073 reach.maxDistance = maxRange; 074 switch (radiusType) 075 { 076 case SPHERE: 077 case CIRCLE: 078 spill.measurement = Spill.Measurement.EUCLIDEAN; 079 break; 080 case CUBE: 081 case SQUARE: 082 spill.measurement = Spill.Measurement.CHEBYSHEV; 083 break; 084 default: 085 spill.measurement = Spill.Measurement.MANHATTAN; 086 break; 087 } 088 } 089 public CloudAOE(Coord center, int volume, Radius radiusType, long rngSeed) 090 { 091 seed = rngSeed; 092 spill = new Spill(new RNG(new LightRNG(rngSeed))); 093 this.center = center; 094 this.volume = volume; 095 expanding = false; 096 rt = radiusType; 097 switch (radiusType) 098 { 099 case SPHERE: 100 case CIRCLE: 101 spill.measurement = Spill.Measurement.EUCLIDEAN; 102 break; 103 case CUBE: 104 case SQUARE: 105 spill.measurement = Spill.Measurement.CHEBYSHEV; 106 break; 107 default: 108 spill.measurement = Spill.Measurement.MANHATTAN; 109 break; 110 } 111 } 112 public CloudAOE(Coord center, int volume, Radius radiusType, long rngSeed, int minRange, int maxRange) 113 { 114 seed = rngSeed; 115 spill = new Spill(new RNG(new LightRNG(rngSeed))); 116 this.center = center; 117 this.volume = volume; 118 expanding = false; 119 rt = radiusType; 120 switch (radiusType) 121 { 122 case SPHERE: 123 case CIRCLE: 124 spill.measurement = Spill.Measurement.EUCLIDEAN; 125 break; 126 case CUBE: 127 case SQUARE: 128 spill.measurement = Spill.Measurement.CHEBYSHEV; 129 break; 130 default: 131 spill.measurement = Spill.Measurement.MANHATTAN; 132 break; 133 } 134 reach.minDistance = minRange; 135 reach.maxDistance = maxRange; 136 } 137 138 public Coord getCenter() { 139 return center; 140 } 141 142 public void setCenter(Coord center) { 143 144 if (dungeon != null && center.isWithin(dungeon.length, dungeon[0].length) && 145 AreaUtils.verifyReach(reach, origin, center)) 146 { 147 this.center = center; 148 } 149 } 150 151 public int getVolume() { 152 return volume; 153 } 154 155 public void setVolume(int volume) { 156 this.volume = volume; 157 } 158 159 public Radius getRadiusType() { 160 return rt; 161 } 162 163 public void setRadiusType(Radius radiusType) { 164 rt = radiusType; 165 switch (radiusType) 166 { 167 case SPHERE: 168 case CIRCLE: 169 break; 170 case CUBE: 171 case SQUARE: 172 break; 173 default: 174 break; 175 } 176 } 177 178 @Override 179 public void shift(Coord aim) { 180 setCenter(aim); 181 } 182 183 @Override 184 public boolean mayContainTarget(Set<Coord> targets) { 185 for (Coord p : targets) 186 { 187 if(rt.radius(center.x, center.y, p.x, p.y) <= Math.sqrt(volume) * 0.75) 188 return true; 189 } 190 return false; 191 } 192 193 @Override 194 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 195 if(targets == null) 196 return new LinkedHashMap<>(); 197 if(requiredExclusions == null) requiredExclusions = new LinkedHashSet<>(); 198 199 //requiredExclusions.remove(origin); 200 int totalTargets = targets.size(); 201 LinkedHashMap<Coord, ArrayList<Coord>> bestPoints = new LinkedHashMap<>(totalTargets * 8); 202 203 if(totalTargets == 0 || volume <= 0) 204 return bestPoints; 205 206 if(volume == 1) 207 { 208 for(Coord p : targets) 209 { 210 ArrayList<Coord> ap = new ArrayList<>(); 211 ap.add(p); 212 bestPoints.put(p, ap); 213 } 214 return bestPoints; 215 } 216 Coord[] ts = targets.toArray(new Coord[targets.size()]); 217 Coord[] exs = requiredExclusions.toArray(new Coord[requiredExclusions.size()]); 218 Coord t = exs[0]; 219 220 double[][][] compositeMap = new double[ts.length][dungeon.length][dungeon[0].length]; 221 222 Spill sp; 223 224 char[][] dungeonCopy = new char[dungeon.length][dungeon[0].length]; 225 for (int i = 0; i < dungeon.length; i++) { 226 System.arraycopy(dungeon[i], 0, dungeonCopy[i], 0, dungeon[i].length); 227 } 228 229 Coord tempPt; 230 for (int i = 0; i < exs.length; ++i) { 231 t = exs[i]; 232 sp = new Spill(dungeon, spill.measurement); 233 sp.lrng.setState(seed); 234 235 sp.start(t, volume, null); 236 for (int x = 0; x < dungeon.length; x++) { 237 for (int y = 0; y < dungeon[x].length; y++) { 238 tempPt = Coord.get(x, y); 239 dungeonCopy[x][y] = (sp.spillMap[x][y] || !AreaUtils.verifyReach(reach, origin, tempPt)) ? '!' : dungeonCopy[x][y]; 240 } 241 } 242 } 243 244 DijkstraMap.Measurement dmm = DijkstraMap.Measurement.MANHATTAN; 245 if(spill.measurement == Spill.Measurement.CHEBYSHEV) dmm = DijkstraMap.Measurement.CHEBYSHEV; 246 else if(spill.measurement == Spill.Measurement.EUCLIDEAN) dmm = DijkstraMap.Measurement.EUCLIDEAN; 247 248 double radius = Math.sqrt(volume) * 0.75; 249 250 for (int i = 0; i < ts.length; ++i) { 251 DijkstraMap dm = new DijkstraMap(dungeon, dmm); 252 253 t = ts[i]; 254 sp = new Spill(dungeon, spill.measurement); 255 sp.lrng.setState(seed); 256 257 sp.start(t, volume, null); 258 259 double dist = 0.0; 260 for (int x = 0; x < dungeon.length; x++) { 261 for (int y = 0; y < dungeon[x].length; y++) { 262 if (sp.spillMap[x][y]){ 263 dist = reach.metric.radius(origin.x, origin.y, x, y); 264 if(dist <= reach.maxDistance + radius && dist >= reach.minDistance - radius) 265 compositeMap[i][x][y] = dm.physicalMap[x][y]; 266 else 267 compositeMap[i][x][y] = DijkstraMap.WALL; 268 } 269 else compositeMap[i][x][y] = DijkstraMap.WALL; 270 } 271 } 272 if(compositeMap[i][ts[i].x][ts[i].y] > DijkstraMap.FLOOR) 273 { 274 for (int x = 0; x < dungeon.length; x++) { 275 Arrays.fill(compositeMap[i][x], 99999.0); 276 } 277 continue; 278 } 279 280 dm.initialize(compositeMap[i]); 281 dm.setGoal(t); 282 dm.scan(null); 283 for (int x = 0; x < dungeon.length; x++) { 284 for (int y = 0; y < dungeon[x].length; y++) { 285 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!') ? dm.gradientMap[x][y] : 99999.0; 286 } 287 } 288 dm.resetMap(); 289 dm.clearGoals(); 290 } 291 double bestQuality = 99999 * ts.length; 292 double[][] qualityMap = new double[dungeon.length][dungeon[0].length]; 293 for (int x = 0; x < qualityMap.length; x++) { 294 for (int y = 0; y < qualityMap[x].length; y++) { 295 qualityMap[x][y] = 0.0; 296 long bits = 0; 297 for (int i = 0; i < ts.length; ++i) { 298 qualityMap[x][y] += compositeMap[i][x][y]; 299 if(compositeMap[i][x][y] < 99999.0 && i < 63) 300 bits |= 1 << i; 301 } 302 if(qualityMap[x][y] < bestQuality) 303 { 304 ArrayList<Coord> ap = new ArrayList<>(); 305 306 for (int i = 0; i < ts.length && i < 63; ++i) { 307 if((bits & (1 << i)) != 0) 308 ap.add(ts[i]); 309 } 310 311 if(ap.size() > 0) { 312 bestQuality = qualityMap[x][y]; 313 bestPoints.clear(); 314 bestPoints.put(Coord.get(x, y), ap); 315 } 316 } 317 else if(qualityMap[x][y] == bestQuality) 318 { 319 ArrayList<Coord> ap = new ArrayList<>(); 320 321 for (int i = 0; i < ts.length && i < 63; ++i) { 322 if((bits & (1 << i)) != 0) 323 ap.add(ts[i]); 324 } 325 326 if (ap.size() > 0) { 327 bestPoints.put(Coord.get(x, y), ap); 328 } 329 } 330 } 331 } 332 333 return bestPoints; 334 } 335 336 @Override 337 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Set<Coord> priorityTargets, Set<Coord> lesserTargets, Set<Coord> requiredExclusions) { 338 if(priorityTargets == null) 339 return idealLocations(lesserTargets, requiredExclusions); 340 if(requiredExclusions == null) requiredExclusions = new LinkedHashSet<>(); 341 342 //requiredExclusions.remove(origin); 343 344 int totalTargets = priorityTargets.size() + lesserTargets.size(); 345 LinkedHashMap<Coord, ArrayList<Coord>> bestPoints = new LinkedHashMap<>(totalTargets * 8); 346 347 if(totalTargets == 0 || volume <= 0) 348 return bestPoints; 349 350 if(volume == 1) 351 { 352 for(Coord p : priorityTargets) 353 { 354 ArrayList<Coord> ap = new ArrayList<>(); 355 ap.add(p); 356 bestPoints.put(p, ap); 357 } 358 return bestPoints; 359 } 360 Coord[] pts = priorityTargets.toArray(new Coord[priorityTargets.size()]); 361 Coord[] lts = lesserTargets.toArray(new Coord[lesserTargets.size()]); 362 Coord[] exs = requiredExclusions.toArray(new Coord[requiredExclusions.size()]); 363 Coord t = exs[0]; 364 365 double[][][] compositeMap = new double[totalTargets][dungeon.length][dungeon[0].length]; 366 Spill sp; 367 368 char[][] dungeonCopy = new char[dungeon.length][dungeon[0].length], 369 dungeonPriorities = new char[dungeon.length][dungeon[0].length]; 370 for (int i = 0; i < dungeon.length; i++) { 371 System.arraycopy(dungeon[i], 0, dungeonCopy[i], 0, dungeon[i].length); 372 Arrays.fill(dungeonPriorities[i], '#'); 373 } 374 Coord tempPt = Coord.get(0, 0); 375 for (int i = 0; i < exs.length; ++i) { 376 t = exs[i]; 377 sp = new Spill(dungeon, spill.measurement); 378 sp.lrng.setState(seed); 379 380 sp.start(t, volume, null); 381 for (int x = 0; x < dungeon.length; x++) { 382 for (int y = 0; y < dungeon[x].length; y++) { 383 tempPt = Coord.get(x, y); 384 dungeonCopy[x][y] = (sp.spillMap[x][y] || !AreaUtils.verifyReach(reach, origin, tempPt)) ? '!' : dungeonCopy[x][y]; 385 } 386 } 387 } 388 389 t = pts[0]; 390 391 DijkstraMap.Measurement dmm = DijkstraMap.Measurement.MANHATTAN; 392 if(spill.measurement == Spill.Measurement.CHEBYSHEV) dmm = DijkstraMap.Measurement.CHEBYSHEV; 393 else if(spill.measurement == Spill.Measurement.EUCLIDEAN) dmm = DijkstraMap.Measurement.EUCLIDEAN; 394 395 double radius = Math.sqrt(volume) * 0.75; 396 397 for (int i = 0; i < pts.length; ++i) { 398 DijkstraMap dm = new DijkstraMap(dungeon, dmm); 399 400 t = pts[i]; 401 sp = new Spill(dungeon, spill.measurement); 402 sp.lrng.setState(seed); 403 404 sp.start(t, volume, null); 405 406 407 408 double dist = 0.0; 409 for (int x = 0; x < dungeon.length; x++) { 410 for (int y = 0; y < dungeon[x].length; y++) { 411 if (sp.spillMap[x][y]){ 412 dist = reach.metric.radius(origin.x, origin.y, x, y); 413 if(dist <= reach.maxDistance + radius && dist >= reach.minDistance - radius) { 414 compositeMap[i][x][y] = dm.physicalMap[x][y]; 415 dungeonPriorities[x][y] = dungeon[x][y]; 416 } 417 else 418 compositeMap[i][x][y] = DijkstraMap.WALL; 419 } 420 else compositeMap[i][x][y] = DijkstraMap.WALL; 421 } 422 } 423 if(compositeMap[i][pts[i].x][pts[i].y] > DijkstraMap.FLOOR) 424 { 425 for (int x = 0; x < dungeon.length; x++) { 426 Arrays.fill(compositeMap[i][x], 399999.0); 427 } 428 continue; 429 } 430 431 432 dm.initialize(compositeMap[i]); 433 dm.setGoal(t); 434 dm.scan(null); 435 for (int x = 0; x < dungeon.length; x++) { 436 for (int y = 0; y < dungeon[x].length; y++) { 437 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!') ? dm.gradientMap[x][y] : 399999.0; 438 } 439 } 440 dm.resetMap(); 441 dm.clearGoals(); 442 } 443 444 t = lts[0]; 445 446 for (int i = pts.length; i < totalTargets; ++i) { 447 DijkstraMap dm = new DijkstraMap(dungeon, dmm); 448 449 t = lts[i - pts.length]; 450 sp = new Spill(dungeon, spill.measurement); 451 sp.lrng.setState(seed); 452 453 sp.start(t, volume, null); 454 455 456 double dist = 0.0; 457 for (int x = 0; x < dungeon.length; x++) { 458 for (int y = 0; y < dungeon[x].length; y++) { 459 if (sp.spillMap[x][y]){ 460 dist = reach.metric.radius(origin.x, origin.y, x, y); 461 if(dist <= reach.maxDistance + radius && dist >= reach.minDistance - radius) 462 compositeMap[i][x][y] = dm.physicalMap[x][y]; 463 else 464 compositeMap[i][x][y] = DijkstraMap.WALL; 465 } 466 else compositeMap[i][x][y] = DijkstraMap.WALL; 467 } 468 } 469 if(compositeMap[i][lts[i - pts.length].x][lts[i - pts.length].y] > DijkstraMap.FLOOR) 470 { 471 for (int x = 0; x < dungeon.length; x++) 472 { 473 Arrays.fill(compositeMap[i][x], 99999.0); 474 } 475 continue; 476 } 477 478 479 dm.initialize(compositeMap[i]); 480 dm.setGoal(t); 481 dm.scan(null); 482 for (int x = 0; x < dungeon.length; x++) { 483 for (int y = 0; y < dungeon[x].length; y++) { 484 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!' && dungeonPriorities[x][y] != '#') ? dm.gradientMap[x][y] : 99999.0; 485 } 486 } 487 dm.resetMap(); 488 dm.clearGoals(); 489 } 490 double bestQuality = 99999 * lts.length + 399999 * pts.length; 491 double[][] qualityMap = new double[dungeon.length][dungeon[0].length]; 492 for (int x = 0; x < qualityMap.length; x++) { 493 for (int y = 0; y < qualityMap[x].length; y++) { 494 qualityMap[x][y] = 0.0; 495 long pbits = 0, lbits = 0; 496 for (int i = 0; i < pts.length; ++i) { 497 qualityMap[x][y] += compositeMap[i][x][y]; 498 if(compositeMap[i][x][y] < 399999.0 && i < 63) 499 pbits |= 1 << i; 500 } 501 for (int i = pts.length; i < totalTargets; ++i) { 502 qualityMap[x][y] += compositeMap[i][x][y]; 503 if(compositeMap[i][x][y] < 99999.0 && i < 63) 504 lbits |= 1 << i; 505 } 506 if(qualityMap[x][y] < bestQuality) 507 { 508 ArrayList<Coord> ap = new ArrayList<>(); 509 510 for (int i = 0; i < pts.length && i < 63; ++i) { 511 if((pbits & (1 << i)) != 0) 512 ap.add(pts[i]); 513 } 514 for (int i = pts.length; i < totalTargets && i < 63; ++i) { 515 if((lbits & (1 << i)) != 0) 516 ap.add(lts[i - pts.length]); 517 } 518 519 if(ap.size() > 0) { 520 bestQuality = qualityMap[x][y]; 521 bestPoints.clear(); 522 bestPoints.put(Coord.get(x, y), ap); 523 } 524 } 525 else if(qualityMap[x][y] == bestQuality) 526 { 527 ArrayList<Coord> ap = new ArrayList<>(); 528 529 for (int i = 0; i < pts.length && i < 63; ++i) { 530 if((pbits & (1 << i)) != 0) 531 ap.add(pts[i]); 532 } 533 for (int i = pts.length; i < totalTargets && i < 63; ++i) { 534 if ((pbits & (1 << i)) != 0) { 535 ap.add(pts[i]); 536 ap.add(pts[i]); 537 ap.add(pts[i]); 538 ap.add(pts[i]); 539 } 540 } 541 542 if (ap.size() > 0) { 543 bestPoints.put(Coord.get(x, y), ap); 544 } 545 } 546 } 547 } 548 549 return bestPoints; 550 } 551 552 553/* 554 @Override 555 public ArrayList<ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 556 int totalTargets = targets.size() + 1; 557 int radius = Math.max(1, (int) (Math.sqrt(volume) * 1.5)); 558 ArrayList<ArrayList<Coord>> locs = new ArrayList<ArrayList<Coord>>(totalTargets); 559 560 for(int i = 0; i < totalTargets; i++) 561 { 562 locs.add(new ArrayList<Coord>(volume)); 563 } 564 if(totalTargets == 1) 565 return locs; 566 double ctr = 0; 567 if(radius < 1) 568 { 569 locs.get(totalTargets - 2).addAll(targets); 570 return locs; 571 } 572 double tempRad; 573 boolean[][] tested = new boolean[dungeon.length][dungeon[0].length]; 574 for (int x = 1; x < dungeon.length - 1; x += radius) { 575 BY_POINT: 576 for (int y = 1; y < dungeon[x].length - 1; y += radius) { 577 for(Coord ex : requiredExclusions) 578 { 579 if(rt.radius(x, y, ex.x, ex.y) <= radius * 0.75) 580 continue BY_POINT; 581 } 582 ctr = 0; 583 for(Coord tgt : targets) 584 { 585 tempRad = rt.radius(x, y, tgt.x, tgt.y); 586 if(tempRad < radius) 587 ctr += 1.0 - (tempRad / radius) * 0.5; 588 } 589 if(ctr >= 1) 590 locs.get((int)(totalTargets - ctr)).add(Coord.get(x, y)); 591 } 592 } 593 Coord it; 594 for(int t = 0; t < totalTargets - 1; t++) 595 { 596 if(locs.get(t).size() > 0) { 597 int numPoints = locs.get(t).size(); 598 for (int i = 0; i < numPoints; i++) { 599 it = locs.get(t).get(i); 600 for (int x = Math.max(1, it.x - radius / 2); x < it.x + (radius + 1) / 2 && x < dungeon.length - 1; x++) { 601 BY_POINT: 602 for (int y = Math.max(1, it.y - radius / 2); y <= it.y + (radius - 1) / 2 && y < dungeon[0].length - 1; y++) 603 { 604 if(tested[x][y]) 605 continue; 606 tested[x][y] = true; 607 608 for(Coord ex : requiredExclusions) 609 { 610 if(rt.radius(x, y, ex.x, ex.y) <= radius * 0.75) 611 continue BY_POINT; 612 } 613 614 ctr = 0; 615 for(Coord tgt : targets) 616 { 617 tempRad = rt.radius(x, y, tgt.x, tgt.y); 618 if(tempRad < radius) 619 ctr += 1.0 - (tempRad / radius) * 0.5; 620 } 621 if(ctr >= 1) 622 locs.get((int)(totalTargets - ctr)).add(Coord.get(x, y)); 623 } 624 } 625 } 626 } 627 } 628 return locs; 629 } 630*/ 631 632 @Override 633 public void setMap(char[][] map) { 634 spill.initialize(map); 635 dungeon = map; 636 } 637 638 @Override 639 public LinkedHashMap<Coord, Double> findArea() { 640 spill.start(center, volume, null); 641 LinkedHashMap<Coord, Double> r = AreaUtils.arrayToHashMap(spill.spillMap); 642 if(!expanding) 643 { 644 spill.reset(); 645 spill.lrng.setState(seed); 646 } 647 return r; 648 } 649 650 @Override 651 public Coord getOrigin() { 652 return origin; 653 } 654 655 @Override 656 public void setOrigin(Coord origin) { 657 this.origin = origin; 658 659 } 660 661 @Override 662 public AimLimit getLimitType() { 663 return reach.limit; 664 } 665 666 @Override 667 public int getMinRange() { 668 return reach.minDistance; 669 } 670 671 @Override 672 public int getMaxRange() { 673 return reach.maxDistance; 674 } 675 676 @Override 677 public Radius getMetric() { 678 return reach.metric; 679 } 680 681 /** 682 * Gets the same values returned by getLimitType(), getMinRange(), getMaxRange(), and getMetric() bundled into one 683 * Reach object. 684 * 685 * @return a non-null Reach object. 686 */ 687 @Override 688 public Reach getReach() { 689 return reach; 690 } 691 692 @Override 693 public void setLimitType(AimLimit limitType) { 694 reach.limit = limitType; 695 696 } 697 698 @Override 699 public void setMinRange(int minRange) { 700 reach.minDistance = minRange; 701 } 702 703 @Override 704 public void setMaxRange(int maxRange) { 705 reach.maxDistance = maxRange; 706 707 } 708 709 @Override 710 public void setMetric(Radius metric) { 711 reach.metric = metric; 712 } 713 714 /** 715 * Sets the same values as setLimitType(), setMinRange(), setMaxRange(), and setMetric() using one Reach object. 716 * 717 * @param reach a non-null Reach object. 718 */ 719 @Override 720 public void setReach(Reach reach) { 721 if(reach != null) 722 this.reach = reach; 723 } 724 725 public boolean isExpanding() { 726 return expanding; 727 } 728 729 public void setExpanding(boolean expanding) { 730 this.expanding = expanding; 731 } 732 733 /** 734 * If you use FOVCache to pre-compute FOV maps for a level, you can share the speedup from using the cache with 735 * some AOE implementations that rely on FOV. Not all implementations need to actually make use of the cache, but 736 * those that use FOV for calculations should benefit. The cache parameter this receives should have completed its 737 * calculations, which can be confirmed by calling awaitCache(). Ideally, the FOVCache will have done its initial 738 * calculations in another thread while the previous level or menu was being displayed, and awaitCache() will only 739 * be a formality. 740 * 741 * @param cache The FOVCache for the current level; can be null to stop using the cache 742 */ 743 @GwtIncompatible 744 @Override 745 public void setCache(FOVCache cache) { 746 747 } 748}