001package squidpony.squidai; 002 003import squidpony.squidmath.Coord; 004import squidpony.squidmath.OrderedMap; 005 006 007/** 008 * Static utilities for use in AOE and anything else that might need HashMaps of Coord keys to Double values. 009 * Created by Tommy Ettinger on 7/13/2015. 010 */ 011public class AreaUtils { 012 /** 013 * This takes a 2D boolean array and returns a HashMap of Coord keys to Double values, but will only use the value 014 * 1.0, and only for positions in map that have as their boolean element true. 015 * @param map width by height, commonly generated by FOV methods 016 * @return a HashMap of Coord keys to Double values, but the only value used is 1.0 017 */ 018 public static OrderedMap<Coord, Double> arrayToHashMap(boolean[][] map) 019 { 020 OrderedMap<Coord, Double> ret = new OrderedMap<>(); 021 for(int i = 0; i < map.length; i++) 022 { 023 for(int j = 0; j < map[i].length; j++) 024 { 025 if(map[i][j]) 026 ret.put(Coord.get(i, j), 1.0); 027 } 028 } 029 return ret; 030 } 031 032 /** 033 * This takes a 2D double array called map and returns a HashMap of Coord keys to Double values, and will have a key 034 * for every position in map that is greater than 0.0, with values equal to those in map. 035 * @param map width by height, commonly generated by FOV methods 036 * @return a HashMap of Coord keys to Double values, with values all greater than 0.0 037 */ 038 public static OrderedMap<Coord, Double> arrayToHashMap(double[][] map) 039 { 040 OrderedMap<Coord, Double> ret = new OrderedMap<>(); 041 for(int i = 0; i < map.length; i++) 042 { 043 for(int j = 0; j < map[i].length; j++) 044 { 045 if(map[i][j] > 0.0) 046 ret.put(Coord.get(i, j), map[i][j]); 047 } 048 } 049 return ret; 050 } 051 052 /** 053 * This takes a 2D double array and returns a HashMap of Coord keys to Double values, but will only use the value 054 * 1.0, and only does this if the passed double[][] has a value at that position that is greater than cutoff. 055 * For example, a cutoff of 0.3 will make all elements in the 2D array that are 0.3 or less be ignored and not put 056 * into the HashMap, but all elements that are greater than 0.3 will be placed in as 1.0. 057 * @param map width by height, commonly generated by FOV methods 058 * @param cutoff any elements greater than this will be 1.0 in the return, anything else will be ignored 059 * @return a HashMap of Coord keys to Double values, but the only value used is 1.0 060 */ 061 public static OrderedMap<Coord, Double> arrayToHashMap(double[][] map, double cutoff) 062 { 063 OrderedMap<Coord, Double> ret = new OrderedMap<>(); 064 for(int i = 0; i < map.length; i++) 065 { 066 for(int j = 0; j < map[i].length; j++) 067 { 068 if(map[i][j] > cutoff) 069 ret.put(Coord.get(i, j), 1.0); 070 } 071 } 072 return ret; 073 } 074 075 /** 076 * This takes a DijkstraMap that has already completed a scan() and returns a HashMap of Coord keys to Double 077 * values, and will have a key for every position that was reached in the DijkstraMap, with 1.0 as the only value. 078 * @param map a double[][] returned by a DijkstraMap running its scan() 079 * @return a HashMap of Coord keys to Double values, with values of 1.0 only 080 */ 081 public static OrderedMap<Coord, Double> dijkstraToHashMap(double[][] map) 082 { 083 OrderedMap<Coord, Double> ret = new OrderedMap<>(); 084 for(int i = 0; i < map.length; i++) 085 { 086 for(int j = 0; j < map[i].length; j++) 087 { 088 if(map[i][j] < DijkstraMap.WALL) 089 ret.put(Coord.get(i, j), 1.0); 090 } 091 } 092 return ret; 093 } 094 095 /** 096 * Checks that the given end Coord can be targeted from the given origin Coord given the directional targeting 097 * rules specified by limit. If any of the arguments are null, returns true (it assumes that any limits are not 098 * valid and don't restrict anything). The following AimLimit enum values for limit have the following meanings: 099 * 100 * <ul> 101 * <li>AimLimit.FREE makes no restrictions; it is equivalent here to passing null for limit.</li> 102 * <li>AimLimit.EIGHT_WAY will only consider Points to be valid targets 103 * if they are along a straight line with an angle that is a multiple of 45 degrees, relative to the positive x 104 * axis. Essentially, this limits the points to those a queen could move to in chess.</li> 105 * <li>AimLimit.ORTHOGONAL will cause the AOE to only consider Points to be valid targets if 106 * they are along a straight line with an angle that is a multiple of 90 degrees, relative to the positive x 107 * axis. Essentially, this limits the points to those a rook could move to in chess.</li> 108 * <li>AimLimit.DIAGONAL will cause the AOE to only consider Points to be valid targets if they are along a 109 * straight line with an angle that is 45 degrees greater than a multiple of 90 degrees, relative to the 110 * positive x axis. Essentially, this limits the points to those a bishop could move to in chess.</li> 111 * </ul> 112 * 113 * @param limit an AimLimit enum that restricts valid points unless it is AimLimit.FREE or null 114 * @param origin where the user is 115 * @param end where the point we want to verify is 116 * @return true if the point is a valid target or if the limits are invalid (non-restricting), false otherwise 117 */ 118 public static boolean verifyLimit(AimLimit limit, Coord origin, Coord end) 119 { 120 if (limit != null && origin != null && end != null) { 121 switch (limit) { 122 case EIGHT_WAY: 123 if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y) || 124 end.x == origin.x || end.y == origin.y) 125 { 126 return true; 127 } 128 break; 129 case DIAGONAL: 130 if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y)) 131 { 132 return true; 133 } 134 break; 135 case ORTHOGONAL: 136 if(end.x == origin.x || end.y == origin.y) 137 { 138 return true; 139 } 140 break; 141 case FREE: return true; 142 } 143 return false; 144 } 145 return true; 146 } 147 148 /** 149 * Checks that the given end Coord can be targeted from the given origin Coord given the complete targeting rules 150 * specified by reach. If any of the arguments are null, returns true (it assumes that any limits are not 151 * valid and don't restrict anything). If reach.limit is null, it treats it as equivalent to {@link AimLimit#FREE}. 152 * Otherwise, it uses the metric, minDistance, and maxDistance from reach to calculate if end is target-able from 153 * origin assuming an unobstructed playing field. 154 * 155 * @param reach a Reach object that, if non-null, gives limits for how targeting can proceed. 156 * @param origin where the user is 157 * @param end where the point we want to verify is 158 * @return true if the point is a valid target or if the limits are invalid (non-restricting), false otherwise 159 */ 160 public static boolean verifyReach(Reach reach, Coord origin, Coord end) 161 { 162 if(reach == null) 163 return true; 164 AimLimit limit = reach.limit; 165 if(limit == null) limit = AimLimit.FREE; 166 if (origin != null && end != null) { 167 switch (limit) { 168 case EIGHT_WAY: 169 if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y) || 170 end.x == origin.x || end.y == origin.y) 171 { 172 return reach.metric.inRange(origin.x, origin.y, end.x, end.y, 173 reach.minDistance, reach.maxDistance); 174 } 175 break; 176 case DIAGONAL: 177 if(Math.abs(end.x - origin.x) == Math.abs(end.y - origin.y)) 178 { 179 return reach.metric.inRange(origin.x, origin.y, end.x, end.y, 180 reach.minDistance, reach.maxDistance); 181 } 182 break; 183 case ORTHOGONAL: 184 if(end.x == origin.x || end.y == origin.y) 185 { 186 return reach.metric.inRange(origin.x, origin.y, end.x, end.y, 187 reach.minDistance, reach.maxDistance); 188 } 189 break; 190 case FREE: return reach.metric.inRange(origin.x, origin.y, end.x, end.y, 191 reach.minDistance, reach.maxDistance); 192 } 193 return false; 194 } 195 return true; 196 } 197}