001package squidpony.squidai; 002 003import squidpony.annotation.GwtIncompatible; 004import squidpony.squidgrid.FOVCache; 005import squidpony.squidgrid.Radius; 006import squidpony.squidmath.Coord; 007 008import java.util.ArrayList; 009import java.util.LinkedHashMap; 010import java.util.LinkedHashSet; 011import java.util.Set; 012 013/** 014 * An AOE type that has a center Coord only and only affects that single Coord. Useful if you need an AOE implementation 015 * for something that does not actually affect an area. 016 * This will produce doubles for its findArea() method which are equal to 1.0. 017 * 018 * This class doesn't use any other SquidLib class to create its area of effect. 019 * Created by Tommy Ettinger on 7/13/2015. 020 */ 021public class PointAOE implements AOE { 022 private Coord center, origin = null; 023 private int mapWidth, mapHeight; 024 private Reach reach = new Reach(1, 1, Radius.SQUARE, null); 025 026 public PointAOE(Coord center) 027 { 028 this.center = center; 029 } 030 public PointAOE(Coord center, int minRange, int maxRange) 031 { 032 this.center = center; 033 reach.minDistance = minRange; 034 reach.maxDistance = maxRange; 035 } 036 037 public Coord getCenter() { 038 return center; 039 } 040 041 042 public void setCenter(Coord center) { 043 044 if (center.isWithin(mapWidth, mapHeight) && 045 AreaUtils.verifyReach(reach, origin, center)) 046 { 047 this.center = center; 048 } 049 } 050 051 @Override 052 public void shift(Coord aim) { 053 setCenter(aim); 054 } 055 056 @Override 057 public boolean mayContainTarget(Set<Coord> targets) { 058 for (Coord p : targets) 059 { 060 if(center.x == p.x && center.y == p.y) 061 return true; 062 } 063 return false; 064 } 065 066 @Override 067 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 068 if(targets == null) 069 return new LinkedHashMap<>(); 070 071 int totalTargets = targets.size(); 072 LinkedHashMap<Coord, ArrayList<Coord>> bestPoints = new LinkedHashMap<>(totalTargets); 073 074 if(totalTargets == 0) 075 return bestPoints; 076 077 078 double dist = 0.0; 079 for(Coord p : targets) { 080 if (AreaUtils.verifyReach(reach, origin, p)) { 081 082 dist = reach.metric.radius(origin.x, origin.y, p.x, p.y); 083 if (dist <= reach.maxDistance && dist >= reach.minDistance) { 084 ArrayList<Coord> ap = new ArrayList<>(); 085 ap.add(p); 086 bestPoints.put(p, ap); 087 } 088 } 089 } 090 return bestPoints; 091 } 092 093 094 @Override 095 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Set<Coord> priorityTargets, Set<Coord> lesserTargets, Set<Coord> requiredExclusions) { 096 if(priorityTargets == null) 097 return idealLocations(lesserTargets, requiredExclusions); 098 if(requiredExclusions == null) requiredExclusions = new LinkedHashSet<>(); 099 100 int totalTargets = priorityTargets.size() + lesserTargets.size(); 101 LinkedHashMap<Coord, ArrayList<Coord>> bestPoints = new LinkedHashMap<>(totalTargets * 4); 102 103 if(totalTargets == 0) 104 return bestPoints; 105 106 double dist = 0.0; 107 108 for(Coord p : priorityTargets) { 109 if (AreaUtils.verifyReach(reach, origin, p)) { 110 111 dist = reach.metric.radius(origin.x, origin.y, p.x, p.y); 112 if (dist <= reach.maxDistance && dist >= reach.minDistance) { 113 ArrayList<Coord> ap = new ArrayList<>(); 114 ap.add(p); 115 ap.add(p); 116 ap.add(p); 117 ap.add(p); 118 bestPoints.put(p, ap); 119 } 120 } 121 } 122 if(bestPoints.isEmpty()) { 123 for (Coord p : lesserTargets) { 124 if (AreaUtils.verifyReach(reach, origin, p)) { 125 126 dist = reach.metric.radius(origin.x, origin.y, p.x, p.y); 127 if (dist <= reach.maxDistance && dist >= reach.minDistance) { 128 ArrayList<Coord> ap = new ArrayList<>(); 129 ap.add(p); 130 bestPoints.put(p, ap); 131 } 132 } 133 } 134 } 135 return bestPoints; 136 137 } 138 139 /* 140 @Override 141 public ArrayList<ArrayList<Coord>> idealLocations(Set<Coord> targets, Set<Coord> requiredExclusions) { 142 int totalTargets = targets.size() + 1; 143 int maxEffect = (int)radiusType.volume2D(radius); 144 ArrayList<ArrayList<Coord>> locs = new ArrayList<ArrayList<Coord>>(totalTargets); 145 146 for(int i = 0; i < totalTargets; i++) 147 { 148 locs.add(new ArrayList<Coord>(maxEffect)); 149 } 150 if(totalTargets == 1) 151 return locs; 152 153 int ctr = 0; 154 if(radius < 1) 155 { 156 locs.get(totalTargets - 2).addAll(targets); 157 return locs; 158 } 159 160 boolean[][] tested = new boolean[dungeon.length][dungeon[0].length]; 161 for (int x = 1; x < dungeon.length - 1; x += radius) { 162 BY_POINT: 163 for (int y = 1; y < dungeon[x].length - 1; y += radius) { 164 for(Coord ex : requiredExclusions) 165 { 166 if(radiusType.radius(x, y, ex.x, ex.y) <= radius) 167 continue BY_POINT; 168 } 169 ctr = 0; 170 for(Coord tgt : targets) 171 { 172 if(radiusType.radius(x, y, tgt.x, tgt.y) <= radius) 173 ctr++; 174 } 175 if(ctr > 0) 176 locs.get(totalTargets - ctr).add(Coord.get(x, y)); 177 } 178 } 179 Coord it; 180 for(int t = 0; t < totalTargets - 1; t++) 181 { 182 if(locs.get(t).size() > 0) { 183 int numPoints = locs.get(t).size(); 184 for (int i = 0; i < numPoints; i++) { 185 it = locs.get(t).get(i); 186 for (int x = Math.max(1, it.x - radius / 2); x < it.x + (radius + 1) / 2 && x < dungeon.length - 1; x++) { 187 BY_POINT: 188 for (int y = Math.max(1, it.y - radius / 2); y <= it.y + (radius - 1) / 2 && y < dungeon[0].length - 1; y++) 189 { 190 if(tested[x][y]) 191 continue; 192 tested[x][y] = true; 193 194 for(Coord ex : requiredExclusions) 195 { 196 if(radiusType.radius(x, y, ex.x, ex.y) <= radius) 197 continue BY_POINT; 198 } 199 200 ctr = 0; 201 for(Coord tgt : targets) 202 { 203 if(radiusType.radius(x, y, tgt.x, tgt.y) <= radius) 204 ctr++; 205 } 206 if(ctr > 0) 207 locs.get(totalTargets - ctr).add(Coord.get(x, y)); 208 } 209 } 210 } 211 } 212 } 213 return locs; 214 } 215*/ 216 @Override 217 public void setMap(char[][] map) { 218 if (map != null && map.length > 0) { 219 mapWidth = map.length; 220 mapHeight = map[0].length; 221 } 222 } 223 224 @Override 225 public LinkedHashMap<Coord, Double> findArea() { 226 LinkedHashMap<Coord, Double> ret = new LinkedHashMap<>(1); 227 ret.put(Coord.get(center.x, center.y), 1.0); 228 return ret; 229 } 230 231 232 @Override 233 public Coord getOrigin() { 234 return origin; 235 } 236 237 @Override 238 public void setOrigin(Coord origin) { 239 this.origin = origin; 240 241 } 242 243 @Override 244 public AimLimit getLimitType() { 245 return reach.limit; 246 } 247 248 @Override 249 public int getMinRange() { 250 return reach.minDistance; 251 } 252 253 @Override 254 public int getMaxRange() { 255 return reach.maxDistance; 256 } 257 258 @Override 259 public Radius getMetric() { 260 return reach.metric; 261 } 262 263 /** 264 * Gets the same values returned by getLimitType(), getMinRange(), getMaxRange(), and getMetric() bundled into one 265 * Reach object. 266 * 267 * @return a non-null Reach object. 268 */ 269 @Override 270 public Reach getReach() { 271 return reach; 272 } 273 274 @Override 275 public void setLimitType(AimLimit limitType) { 276 reach.limit = limitType; 277 278 } 279 280 @Override 281 public void setMinRange(int minRange) { 282 reach.minDistance = minRange; 283 } 284 285 @Override 286 public void setMaxRange(int maxRange) { 287 reach.maxDistance = maxRange; 288 289 } 290 291 @Override 292 public void setMetric(Radius metric) { 293 reach.metric = metric; 294 } 295 296 /** 297 * Sets the same values as setLimitType(), setMinRange(), setMaxRange(), and setMetric() using one Reach object. 298 * 299 * @param reach a non-null Reach object. 300 */ 301 @Override 302 public void setReach(Reach reach) { 303 304 } 305 306 /** 307 * If you use FOVCache to pre-compute FOV maps for a level, you can share the speedup from using the cache with 308 * some AOE implementations that rely on FOV. Not all implementations need to actually make use of the cache, but 309 * those that use FOV for calculations should benefit. The cache parameter this receives should have completed its 310 * calculations, which can be confirmed by calling awaitCache(). Ideally, the FOVCache will have done its initial 311 * calculations in another thread while the previous level or menu was being displayed, and awaitCache() will only 312 * be a formality. 313 * 314 * @param cache The FOVCache for the current level; can be null to stop using the cache 315 */ 316 @GwtIncompatible 317 @Override 318 public void setCache(FOVCache cache) { 319 320 } 321}