001package squidpony.squidai; 002 003import squidpony.annotation.GwtIncompatible; 004import squidpony.squidgrid.FOV; 005import squidpony.squidgrid.FOVCache; 006import squidpony.squidgrid.Radius; 007import squidpony.squidgrid.mapping.DungeonUtility; 008import squidpony.squidmath.Coord; 009 010import java.util.*; 011 012/** 013 * An AOE type that has an origin, a radius, an angle, and a span; it will blast from the origin to a length equal to 014 * radius along the angle (in degrees), moving somewhat around corners/obstacles, and also spread a total of span 015 * degrees around the angle (a span of 90 will affect a full quadrant, centered on angle). You can specify the 016 * RadiusType to Radius.DIAMOND for Manhattan distance, RADIUS.SQUARE for Chebyshev, or RADIUS.CIRCLE for Euclidean. 017 * 018 * RADIUS.CIRCLE (Euclidean measurement) will produce the most real-looking cones. This will produce doubles for its 019 * findArea() method which are greater than 0.0 and less than or equal to 1.0. 020 * 021 * This class uses squidpony.squidgrid.FOV to create its area of effect. 022 * Created by Tommy Ettinger on 7/13/2015. 023 */ 024public class ConeAOE implements AOE { 025 private FOV fov; 026 private Coord origin; 027 private double radius, angle, span; 028 private double[][] map; 029 private char[][] dungeon; 030 private Radius radiusType; 031 private Reach reach = new Reach(1, 1, Radius.SQUARE, null); 032 033 public ConeAOE(Coord origin, Coord endCenter, double span, Radius radiusType) 034 { 035 fov = new FOV(FOV.RIPPLE_LOOSE); 036 this.origin = origin; 037 radius = radiusType.radius(origin.x, origin.y, endCenter.x, endCenter.y); 038 angle = (Math.toDegrees(Math.atan2(endCenter.y - origin.y, endCenter.x - origin.x)) % 360.0 + 360.0) % 360.0; 039// this.startAngle = Math.abs((angle - span / 2.0) % 360.0); 040// this.endAngle = Math.abs((angle + span / 2.0) % 360.0); 041 this.span = span; 042 this.radiusType = radiusType; 043 } 044 public ConeAOE(Coord origin, int radius, double angle, double span, Radius radiusType) 045 { 046 fov = new FOV(FOV.RIPPLE_LOOSE); 047 this.origin = origin; 048 this.radius = radius; 049// this.startAngle = Math.abs((angle - span / 2.0) % 360.0); 050// this.endAngle = Math.abs((angle + span / 2.0) % 360.0); 051 this.angle = angle; 052 this.span = span; 053 this.radiusType = radiusType; 054 } 055 056 @Override 057 public Coord getOrigin() { 058 return origin; 059 } 060 061 @Override 062 public void setOrigin(Coord origin) { 063 this.origin = origin; 064 } 065 066 @Override 067 public AimLimit getLimitType() { 068 return reach.limit; 069 } 070 071 @Override 072 public int getMinRange() { 073 return reach.minDistance; 074 } 075 076 @Override 077 public int getMaxRange() { 078 return reach.maxDistance; 079 } 080 081 @Override 082 public Radius getMetric() { 083 return reach.metric; 084 } 085 086 /** 087 * Gets the same values returned by getLimitType(), getMinRange(), getMaxRange(), and getMetric() bundled into one 088 * Reach object. 089 * 090 * @return a non-null Reach object. 091 */ 092 @Override 093 public Reach getReach() { 094 return reach; 095 } 096 097 @Override 098 public void setLimitType(AimLimit limitType) { 099 reach.limit = limitType; 100 101 } 102 103 @Override 104 public void setMinRange(int minRange) { 105 reach.minDistance = minRange; 106 } 107 108 @Override 109 public void setMaxRange(int maxRange) { 110 reach.maxDistance = maxRange; 111 112 } 113 114 @Override 115 public void setMetric(Radius metric) { 116 reach.metric = metric; 117 } 118 119 /** 120 * Sets the same values as setLimitType(), setMinRange(), setMaxRange(), and setMetric() using one Reach object. 121 * 122 * @param reach a non-null Reach object. 123 */ 124 @Override 125 public void setReach(Reach reach) { 126 if(reach != null) 127 this.reach = reach; 128 } 129 130 public double getRadius() { 131 return radius; 132 } 133 134 public void setRadius(double radius) { 135 this.radius = radius; 136 } 137 138 public double getAngle() { 139 return angle; 140 } 141 142 public void setAngle(double angle) { 143 if (reach.limit == null || reach.limit == AimLimit.FREE || 144 (reach.limit == AimLimit.EIGHT_WAY && (int)(angle) % 45 == 0) || 145 (reach.limit == AimLimit.DIAGONAL && (int)(angle) % 90 == 45) || 146 (reach.limit == AimLimit.ORTHOGONAL && (int)(angle) % 90 == 0)) { 147 this.angle = angle; 148// this.startAngle = Math.abs((angle - span / 2.0) % 360.0); 149// this.endAngle = Math.abs((angle + span / 2.0) % 360.0); 150 } 151 } 152 153 public void setEndCenter(Coord endCenter) { 154// radius = radiusType.radius(origin.x, origin.y, endCenter.x, endCenter.y); 155 if (AreaUtils.verifyLimit(reach.limit, origin, endCenter)) { 156 angle = (Math.toDegrees(Math.atan2(endCenter.y - origin.y, endCenter.x - origin.x)) % 360.0 + 360.0) % 360.0; 157// startAngle = Math.abs((angle - span / 2.0) % 360.0); 158// endAngle = Math.abs((angle + span / 2.0) % 360.0); 159 } 160 } 161 162 public double getSpan() { 163 return span; 164 } 165 166 public void setSpan(double span) { 167 this.span = span; 168// this.startAngle = Math.abs((angle - span / 2.0) % 360.0); 169// this.endAngle = Math.abs((angle + span / 2.0) % 360.0); 170 } 171 172 public Radius getRadiusType() { 173 return radiusType; 174 } 175 176 public void setRadiusType(Radius radiusType) { 177 this.radiusType = radiusType; 178 } 179 180 @Override 181 public void shift(Coord aim) { 182 setEndCenter(aim); 183 } 184 185 @Override 186 public boolean mayContainTarget(Set<Coord> targets) { 187 for (Coord p : targets) { 188 if (radiusType.radius(origin.x, origin.y, p.x, p.y) <= radius) { 189 double d = ((angle - Math.toDegrees(Math.atan2(p.y - origin.y, p.x - origin.x)) % 360.0 + 360.0) % 360.0); 190 if(d > 180) 191 d = 360 - d; 192 if(d < span / 2.0) 193 return true; 194 } 195 } 196 return false; 197 } 198 199 @Override 200 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 201 if(targets == null) 202 return new LinkedHashMap<>(); 203 if(requiredExclusions == null) requiredExclusions = new LinkedHashSet<>(); 204 205 //requiredExclusions.remove(origin); 206 int totalTargets = targets.size(); 207 LinkedHashMap<Coord, ArrayList<Coord>> bestPoints = new LinkedHashMap<>(totalTargets * 8); 208 209 if(totalTargets == 0) 210 return bestPoints; 211 212 Coord[] ts = targets.toArray(new Coord[targets.size()]); 213 Coord[] exs = requiredExclusions.toArray(new Coord[requiredExclusions.size()]); 214 Coord t = exs[0]; 215 216 double[][][] compositeMap = new double[ts.length][dungeon.length][dungeon[0].length]; 217 double tAngle; //, tStartAngle, tEndAngle; 218 219 220 char[][] dungeonCopy = new char[dungeon.length][dungeon[0].length]; 221 for (int i = 0; i < dungeon.length; i++) { 222 System.arraycopy(dungeon[i], 0, dungeonCopy[i], 0, dungeon[i].length); 223 } 224 double[][] tmpfov; 225 Coord tempPt = Coord.get(0, 0); 226 227 for (int i = 0; i < exs.length; i++) { 228 t = exs[i]; 229// tRadius = radiusType.radius(origin.x, origin.y, t.x, t.y); 230 tAngle = (Math.toDegrees(Math.atan2(t.y - origin.y, t.x - origin.x)) % 360.0 + 360.0) % 360.0; 231// tStartAngle = Math.abs((tAngle - span / 2.0) % 360.0); 232// tEndAngle = Math.abs((tAngle + span / 2.0) % 360.0); 233 tmpfov = fov.calculateFOV(map, origin.x, origin.y, radius, radiusType, tAngle, span); 234 for (int x = 0; x < dungeon.length; x++) { 235 for (int y = 0; y < dungeon[x].length; y++) { 236 tempPt = Coord.get(x, y); 237 dungeonCopy[x][y] = (tmpfov[x][y] > 0.0 || !AreaUtils.verifyLimit(reach.limit, origin, tempPt)) ? '!' : dungeonCopy[x][y]; 238 } 239 } 240 } 241 242 t = ts[0]; 243 244 DijkstraMap.Measurement dmm = DijkstraMap.Measurement.MANHATTAN; 245 if(radiusType == Radius.SQUARE || radiusType == Radius.CUBE) dmm = DijkstraMap.Measurement.CHEBYSHEV; 246 else if(radiusType == Radius.CIRCLE || radiusType == Radius.SPHERE) dmm = DijkstraMap.Measurement.EUCLIDEAN; 247 248 for (int i = 0; i < ts.length; ++i) { 249 DijkstraMap dm = new DijkstraMap(dungeon, dmm); 250 t = ts[i]; 251// tRadius = radiusType.radius(origin.x, origin.y, t.x, t.y); 252 tAngle = (Math.toDegrees(Math.atan2(t.y - origin.y, t.x - origin.x)) % 360.0 + 360.0) % 360.0; 253// tStartAngle = Math.abs((tAngle - span / 2.0) % 360.0); 254// tEndAngle = Math.abs((tAngle + span / 2.0) % 360.0); 255 256 tmpfov = fov.calculateFOV(map, origin.x, origin.y, radius, radiusType, tAngle, span); 257 258 259 for (int x = 0; x < dungeon.length; x++) { 260 for (int y = 0; y < dungeon[x].length; y++) { 261 if (tmpfov[x][y] > 0.0) 262 { 263 compositeMap[i][x][y] = dm.physicalMap[x][y]; 264 } 265 else compositeMap[i][x][y] = DijkstraMap.WALL; 266 } 267 } 268 if(compositeMap[i][t.x][t.y] > DijkstraMap.FLOOR) 269 { 270 for (int x = 0; x < dungeon.length; x++) { 271 Arrays.fill(compositeMap[i][x], 99999.0); 272 } 273 continue; 274 } 275 276 277 dm.initialize(compositeMap[i]); 278 dm.setGoal(t); 279 dm.scan(null); 280 for (int x = 0; x < dungeon.length; x++) { 281 for (int y = 0; y < dungeon[x].length; y++) { 282 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!') ? dm.gradientMap[x][y] : 99999.0; 283 } 284 } 285 } 286 double bestQuality = 99999 * ts.length; 287 double[][] qualityMap = new double[dungeon.length][dungeon[0].length]; 288 for (int x = 0; x < qualityMap.length; x++) { 289 for (int y = 0; y < qualityMap[x].length; y++) { 290 qualityMap[x][y] = 0.0; 291 long bits = 0; 292 for (int i = 0; i < ts.length; ++i) { 293 qualityMap[x][y] += compositeMap[i][x][y]; 294 if(compositeMap[i][x][y] < 99999.0 && i < 63) 295 bits |= 1 << i; 296 } 297 if(qualityMap[x][y] < bestQuality) 298 { 299 ArrayList<Coord> ap = new ArrayList<>(); 300 301 for (int i = 0; i < ts.length && i < 63; ++i) { 302 if((bits & (1 << i)) != 0) 303 ap.add(ts[i]); 304 } 305 if(ap.size() > 0) { 306 bestQuality = qualityMap[x][y]; 307 bestPoints.clear(); 308 bestPoints.put(Coord.get(x, y), ap); 309 } 310 } 311 else if(qualityMap[x][y] == bestQuality) { 312 ArrayList<Coord> ap = new ArrayList<>(); 313 314 for (int i = 0; i < ts.length && i < 63; ++i) { 315 if ((bits & (1 << i)) != 0) 316 ap.add(ts[i]); 317 } 318 if (ap.size() > 0) { 319 bestPoints.put(Coord.get(x, y), ap); 320 } 321 } 322 323 } 324 } 325 326 return bestPoints; 327 } 328 329 @Override 330 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Set<Coord> priorityTargets, Set<Coord> lesserTargets, Set<Coord> requiredExclusions) { 331 if(priorityTargets == null) 332 return idealLocations(lesserTargets, requiredExclusions); 333 if(requiredExclusions == null) requiredExclusions = new LinkedHashSet<>(); 334 335 //requiredExclusions.remove(origin); 336 int totalTargets = priorityTargets.size() + lesserTargets.size(); 337 LinkedHashMap<Coord, ArrayList<Coord>> bestPoints = new LinkedHashMap<>(totalTargets * 8); 338 339 if(totalTargets == 0) 340 return bestPoints; 341 342 Coord[] pts = priorityTargets.toArray(new Coord[priorityTargets.size()]); 343 Coord[] lts = lesserTargets.toArray(new Coord[lesserTargets.size()]); 344 Coord[] exs = requiredExclusions.toArray(new Coord[requiredExclusions.size()]); 345 Coord t = exs[0]; 346 347 double[][][] compositeMap = new double[totalTargets][dungeon.length][dungeon[0].length]; 348 double tAngle; //, tStartAngle, tEndAngle; 349 350 char[][] dungeonCopy = new char[dungeon.length][dungeon[0].length], 351 dungeonPriorities = new char[dungeon.length][dungeon[0].length]; 352 for (int i = 0; i < dungeon.length; i++) { 353 System.arraycopy(dungeon[i], 0, dungeonCopy[i], 0, dungeon[i].length); 354 Arrays.fill(dungeonPriorities[i], '#'); 355 } 356 double[][] tmpfov; 357 Coord tempPt = Coord.get(0, 0); 358 for (int i = 0; i < exs.length; ++i) { 359 t = exs[i]; 360 361 tAngle = (Math.toDegrees(Math.atan2(t.y - origin.y, t.x - origin.x)) % 360.0 + 360.0) % 360.0; 362// tStartAngle = Math.abs((tAngle - span / 2.0) % 360.0); 363// tEndAngle = Math.abs((tAngle + span / 2.0) % 360.0); 364 tmpfov = fov.calculateFOV(map, origin.x, origin.y, radius, radiusType, tAngle, span); 365 for (int x = 0; x < dungeon.length; x++) { 366 for (int y = 0; y < dungeon[x].length; y++) { 367 tempPt = Coord.get(x, y); 368 dungeonCopy[x][y] = (tmpfov[x][y] > 0.0 || !AreaUtils.verifyLimit(reach.limit, origin, tempPt)) ? '!' : dungeonCopy[x][y]; 369 } 370 } 371 } 372 373 t = pts[0]; 374 375 DijkstraMap.Measurement dmm = DijkstraMap.Measurement.MANHATTAN; 376 if(radiusType == Radius.SQUARE || radiusType == Radius.CUBE) dmm = DijkstraMap.Measurement.CHEBYSHEV; 377 else if(radiusType == Radius.CIRCLE || radiusType == Radius.SPHERE) dmm = DijkstraMap.Measurement.EUCLIDEAN; 378 379 for (int i = 0; i < pts.length; ++i) { 380 DijkstraMap dm = new DijkstraMap(dungeon, dmm); 381 t = pts[i]; 382 tAngle = (Math.toDegrees(Math.atan2(t.y - origin.y, t.x - origin.x)) % 360.0 + 360.0) % 360.0; 383// tStartAngle = Math.abs((tAngle - span / 2.0) % 360.0); 384// tEndAngle = Math.abs((tAngle + span / 2.0) % 360.0); 385 386 tmpfov = fov.calculateFOV(map, origin.x, origin.y, radius, radiusType, tAngle, span); 387 388 for (int x = 0; x < dungeon.length; x++) { 389 for (int y = 0; y < dungeon[x].length; y++) { 390 if (tmpfov[x][y] > 0.0){ 391 compositeMap[i][x][y] = dm.physicalMap[x][y]; 392 dungeonPriorities[x][y] = dungeon[x][y]; 393 } 394 else compositeMap[i][x][y] = DijkstraMap.WALL; 395 } 396 } 397 if(compositeMap[i][pts[i].x][pts[i].y] > DijkstraMap.FLOOR) 398 { 399 for (int x = 0; x < dungeon.length; x++) { 400 Arrays.fill(compositeMap[i][x], 399999.0); 401 } 402 continue; 403 } 404 405 406 407 dm.initialize(compositeMap[i]); 408 dm.setGoal(t); 409 dm.scan(null); 410 for (int x = 0; x < dungeon.length; x++) { 411 for (int y = 0; y < dungeon[x].length; y++) { 412 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!') ? dm.gradientMap[x][y] : 399999.0; 413 } 414 } 415 } 416 417 t = lts[0]; 418 419 for (int i = pts.length; i < totalTargets; ++i) { 420 DijkstraMap dm = new DijkstraMap(dungeon, dmm); 421 t = lts[i - pts.length]; 422 tAngle = (Math.toDegrees(Math.atan2(t.y - origin.y, t.x - origin.x)) % 360.0 + 360.0) % 360.0; 423// tStartAngle = Math.abs((tAngle - span / 2.0) % 360.0); 424// tEndAngle = Math.abs((tAngle + span / 2.0) % 360.0); 425 426 tmpfov = fov.calculateFOV(map, origin.x, origin.y, radius, radiusType, tAngle, span); 427 428 for (int x = 0; x < dungeon.length; x++) { 429 for (int y = 0; y < dungeon[x].length; y++) { 430 if (tmpfov[x][y] > 0.0){ 431 compositeMap[i][x][y] = dm.physicalMap[x][y]; 432 } 433 else compositeMap[i][x][y] = DijkstraMap.WALL; 434 } 435 } 436 if(compositeMap[i][lts[i - pts.length].x][lts[i - pts.length].y] > DijkstraMap.FLOOR) 437 { 438 for (int x = 0; x < dungeon.length; x++) 439 { 440 Arrays.fill(compositeMap[i][x], 99999.0); 441 } 442 continue; 443 } 444 445 446 dm.initialize(compositeMap[i]); 447 dm.setGoal(t); 448 dm.scan(null); 449 for (int x = 0; x < dungeon.length; x++) { 450 for (int y = 0; y < dungeon[x].length; y++) { 451 compositeMap[i][x][y] = (dm.gradientMap[x][y] < DijkstraMap.FLOOR && dungeonCopy[x][y] != '!' && dungeonPriorities[x][y] != '#') ? dm.gradientMap[x][y] : 99999.0; 452 } 453 } 454 } 455 double bestQuality = 99999 * lts.length + 399999 * pts.length; 456 double[][] qualityMap = new double[dungeon.length][dungeon[0].length]; 457 for (int x = 0; x < qualityMap.length; x++) { 458 for (int y = 0; y < qualityMap[x].length; y++) { 459 qualityMap[x][y] = 0.0; 460 long pbits = 0, lbits = 0; 461 for (int i = 0; i < pts.length; ++i) { 462 qualityMap[x][y] += compositeMap[i][x][y]; 463 if(compositeMap[i][x][y] < 399999.0 && i < 63) 464 pbits |= 1 << i; 465 } 466 for (int i = pts.length; i < totalTargets; ++i) { 467 qualityMap[x][y] += compositeMap[i][x][y]; 468 if(compositeMap[i][x][y] < 99999.0 && i < 63) 469 lbits |= 1 << i; 470 } 471 if(qualityMap[x][y] < bestQuality) 472 { 473 ArrayList<Coord> ap = new ArrayList<>(); 474 475 for (int i = 0; i < pts.length && i < 63; ++i) { 476 if((pbits & (1 << i)) != 0) 477 ap.add(pts[i]); 478 } 479 for (int i = pts.length; i < totalTargets && i < 63; ++i) { 480 if((lbits & (1 << i)) != 0) 481 ap.add(lts[i - pts.length]); 482 } 483 484 if(ap.size() > 0) { 485 bestQuality = qualityMap[x][y]; 486 bestPoints.clear(); 487 bestPoints.put(Coord.get(x, y), ap); 488 } 489 490 } 491 else if(qualityMap[x][y] == bestQuality) { 492 ArrayList<Coord> ap = new ArrayList<>(); 493 494 for (int i = 0; i < pts.length && i < 63; ++i) { 495 if ((pbits & (1 << i)) != 0) { 496 ap.add(pts[i]); 497 ap.add(pts[i]); 498 ap.add(pts[i]); 499 ap.add(pts[i]); 500 } 501 } 502 for (int i = pts.length; i < totalTargets && i < 63; ++i) { 503 if ((lbits & (1 << i)) != 0) 504 ap.add(lts[i - pts.length]); 505 } 506 if (ap.size() > 0) { 507 bestPoints.put(Coord.get(x, y), ap); 508 } 509 } 510 } 511 } 512 513 return bestPoints; 514 } 515 516 /* 517 @Override 518 public ArrayList<ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 519 int totalTargets = targets.size() + 1; 520 int maxEffect = (int)(radiusType.volume2D(radius) * Math.max(5, span) / 360.0); 521 double allowed = Math.toRadians(span / 2.0); 522 ArrayList<ArrayList<Coord>> locs = new ArrayList<ArrayList<Coord>>(totalTargets); 523 524 for(int i = 0; i < totalTargets; i++) 525 { 526 locs.add(new ArrayList<Coord>(maxEffect)); 527 } 528 if(totalTargets == 1) 529 return locs; 530 531 int ctr = 0; 532 if(radius < 1) 533 { 534 locs.get(totalTargets - 2).addAll(targets); 535 return locs; 536 } 537 538 double tmpAngle, ang; 539 boolean[][] tested = new boolean[dungeon.length][dungeon[0].length]; 540 for (int x = 1; x < dungeon.length - 1; x += radius) { 541 BY_POINT: 542 for (int y = 1; y < dungeon[x].length - 1; y += radius) { 543 ang = Math.atan2(y - origin.y, x - origin.x); // between -pi and pi 544 545 for(Coord ex : requiredExclusions) { 546 if (radiusType.radius(x, y, ex.x, ex.y) <= radius) { 547 tmpAngle = Math.abs(ang - Math.atan2(ex.y - origin.y, ex.x - origin.x)); 548 if(tmpAngle > Math.PI) tmpAngle = PI2 - tmpAngle; 549 if(tmpAngle < allowed) 550 continue BY_POINT; 551 } 552 } 553 ctr = 0; 554 for(Coord tgt : targets) { 555 if (radiusType.radius(x, y, tgt.x, tgt.y) <= radius) { 556 tmpAngle = Math.abs(ang - Math.atan2(tgt.y - origin.y, tgt.x - origin.x)); 557 if(tmpAngle > Math.PI) tmpAngle = PI2 - tmpAngle; 558 if(tmpAngle < allowed) 559 ctr++; 560 } 561 } 562 if(ctr > 0) 563 locs.get(totalTargets - ctr).add(Coord.get(x, y)); 564 } 565 } 566 Coord it; 567 for(int t = 0; t < totalTargets - 1; t++) 568 { 569 if(locs.get(t).size() > 0) { 570 int numPoints = locs.get(t).size(); 571 for (int i = 0; i < numPoints; i++) { 572 it = locs.get(t).get(i); 573 for (int x = Math.max(1, it.x - (int)(radius) / 2); x < it.x + (radius + 1) / 2 && x < dungeon.length - 1; x++) { 574 BY_POINT: 575 for (int y = Math.max(1, it.y - (int)(radius) / 2); y <= it.y + (radius - 1) / 2 && y < dungeon[0].length - 1; y++) 576 { 577 if(tested[x][y]) 578 continue; 579 tested[x][y] = true; 580 ang = Math.atan2(y - origin.y, x - origin.x); // between -pi and pi 581 for(Coord ex : requiredExclusions) { 582 if (radiusType.radius(x, y, ex.x, ex.y) <= radius) { 583 tmpAngle = Math.abs(ang - Math.atan2(ex.y - origin.y, ex.x - origin.x)); 584 if(tmpAngle > Math.PI) tmpAngle = PI2 - tmpAngle; 585 if(tmpAngle < allowed) 586 continue BY_POINT; 587 } 588 } 589 590 ctr = 0; 591 for(Coord tgt : targets) { 592 if (radiusType.radius(x, y, tgt.x, tgt.y) <= radius) { 593 tmpAngle = Math.abs(ang - Math.atan2(tgt.y - origin.y, tgt.x - origin.x)); 594 if(tmpAngle > Math.PI) tmpAngle = PI2 - tmpAngle; 595 if(tmpAngle < allowed) 596 ctr++; 597 } 598 } 599 if(ctr > 0) 600 locs.get(totalTargets - ctr).add(Coord.get(x, y)); 601 } 602 } 603 } 604 } 605 } 606 return locs; 607 } 608*/ 609 @Override 610 public void setMap(char[][] map) { 611 this.map = DungeonUtility.generateResistances(map); 612 dungeon = map; 613 } 614 615 @Override 616 public LinkedHashMap<Coord, Double> findArea() { 617 LinkedHashMap<Coord, Double> r = AreaUtils.arrayToHashMap(fov.calculateFOV(map, origin.x, origin.y, radius, 618 radiusType, angle, span)); 619 r.remove(origin); 620 return r; 621 } 622 623 /** 624 * If you use FOVCache to pre-compute FOV maps for a level, you can share the speedup from using the cache with 625 * some AOE implementations that rely on FOV. Not all implementations need to actually make use of the cache, but 626 * those that use FOV for calculations should benefit. The cache parameter this receives should have completed its 627 * calculations, which can be confirmed by calling awaitCache(). Ideally, the FOVCache will have done its initial 628 * calculations in another thread while the previous level or menu was being displayed, and awaitCache() will only 629 * be a formality. 630 * 631 * @param cache The FOVCache for the current level; can be null to stop using the cache 632 */ 633 @GwtIncompatible 634 @Override 635 public void setCache(FOVCache cache) { 636 fov = cache; 637 } 638 639}