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}