001package squidpony.squidai; 002 003import squidpony.squidmath.Coord; 004 005import java.util.ArrayList; 006import java.util.LinkedHashMap; 007import java.util.Set; 008 009/** 010 * A simple struct-like class that stores various public fields which describe the targeting properties of a skill, 011 * spell, tech, or any other game-specific term for a targeted (typically offensive) ability we call a Technique. 012 * 013 * The typical usage of a Technique is: 014 * <ul> 015 * <li>Construct any AOE implementation the Technique would use (if the Technique affects multiple grid cells).</li> 016 * <li>Construct the Technique (passing the AOE as a parameter if needed).</li> 017 * <li>Call setMap() before considering the Technique if it has not been called yet, if the physical map (including 018 * doors and destructible objects) has changed since setMap() was last called, or simply on every Technique every time 019 * the map changes if there are few enemies with few Techniques. PERFORMING ANY SUBSEQUENT STEPS WITHOUT SETTING THE 020 * MAP TO THE CURRENT ACTUAL PHYSICAL MAP WILL HAVE BAD CONSEQUENCES FOR LOGIC AND MAY CAUSE CRASHING BUGS DUE TO 021 * ARRAY BOUNDS VIOLATIONS IF YOU HAVEN'T SET THE MAP ARRAY IN THE FIRST PLACE. The map should be bounded by wall chars 022 * ('#'), which is done automatically by squidpony.squidgrid.mapping.DungeonGenerator .</li> 023 * <li>When the Technique is being considered by an AI, call idealLocations() with the values of targets, 024 * lesserTargets, and/or priorityTargets set to beings that the AI can see (likely using FOV) and wants to affect with 025 * this Technique (enemies for offensive Techniques, allies for supporting ones), and requiredExclusions typically set 026 * to allies for offensive Techniques that can cause friendly-fire damage, or to null for supporting ones or Techniques 027 * that don't affect allies.</li> 028 * <li>When an ideal location has been determined from the previous step, and the AI decides (possibly randomly, by an 029 * aggression ("aggro") level tracked per-enemy, or by weights on Techniques for different situations) to use this 030 * Technique on a specific target point, call apply() with the user position as a Coord and the chosen Coord, and 031 * proceed to process the effects of the Technique as fitting for your game on the returned Map of Coord keys to Double 032 * values that describe the amount of effect (from 0.0 for none to 1.0 for full) that Coord receives.</li> 033 * </ul> 034 * 035 * A Technique always has an AOE implementation that it uses to determine which cells it actually affects, and 036 * Techniques that do not actually affect an area use the default single-cell "Area of Effect" implementation, PointAOE. 037 * You typically will need to construct the implementing class of the AOE interface in a different way for each 038 * implementation; BeamAOE, LineAOE, and ConeAOE depend on the user's position, BurstAOE and BlastAOE treat radii 039 * differently from BeamAOE and LineAOE, and CloudAOE has a random component that can be given a seed. 040 * 041 * A Technique has a String name, which typically should be in a form that can be presented to a user, and a 042 * String id, which defaults to the same value as name but can be given some value not meant for users that records 043 * any additional identifying characteristics the game needs for things like comparisons. 044 * 045 * Created by Tommy Ettinger on 7/27/2015. 046 */ 047public class Technique { 048 public String name; 049 public String id; 050 public AOE aoe; 051 protected char[][] dungeon; 052 protected static final Coord DEFAULT_POINT = Coord.get(0, 0); 053 054 /** 055 * Creates a Technique that can target any adjacent single Coord, using 056 * Chebyshev (8-way square) distance. 057 */ 058 public Technique() { 059 name = "Default Technique"; 060 id = name; 061 aoe = new PointAOE(DEFAULT_POINT); 062 } 063 064 /** 065 * Creates a Technique that can target any adjacent single Coord, 066 * using Chebyshev (8-way square) distance. 067 * @param name An identifier that may be displayed to the user. Also used for id. 068 */ 069 public Technique(String name) { 070 this.name = name; 071 id = name; 072 aoe = new PointAOE(DEFAULT_POINT); 073 } 074 075 /** 076 * Creates a Technique that can target a Coord at a range specified by the given AOE's minRange and maxRange, 077 * using a distance metric from the AOE, and use that target Coord for the given AOE. 078 * @param name An identifier that may be displayed to the user. Also used for id. 079 * @param aoe An implementation of the AOE interface; typically needs construction beforehand. 080 */ 081 public Technique(String name, AOE aoe) { 082 this.name = name; 083 id = name; 084 this.aoe = aoe; 085 } 086 087 088 /** 089 * Creates a Technique that can target a Coord at a range specified by the given AOE's minRange and maxRange, 090 * using a distance metric from the AOE, and use that target Coord for the given AOE. Takes an id parameter. 091 * @param name An identifier that may be displayed to the user. 092 * @param id An identifier that should always be internal, and will probably never be shown to the user. 093 * @param aoe An implementation of the AOE interface; typically needs construction beforehand. 094 */ 095 public Technique(String name, String id, AOE aoe) { 096 this.name = name; 097 this.id = id; 098 this.aoe = aoe; 099 } 100 101 102 /** 103 * VITAL: Call this method before any calls to idealLocations() or apply(), and call it again if the map changes. 104 * 105 * This simple method sets the map that this Technique can find targets in to a given char 2D array with '#' for 106 * walls and any other character (including characters for open and closed doors) treated as a floor for most 107 * purposes (certain AOE implementations may treat open and closed doors differently, specifically any that use 108 * FOV internally and can yield values other than 1.0 from their findArea() method, like BlastAOE and ConeAOE). 109 * @param map A char 2D array like one generated by squidpony.squidgrid.mapping.DungeonGenerator, with '#' for walls and bounded edges. 110 */ 111 public void setMap(char[][] map) 112 { 113 dungeon = map; 114 aoe.setMap(map); 115 } 116 117 /** 118 * Get a mapping of Coord keys representing locations to apply this Technique to, to ArrayList of Coord values 119 * representing which targets (by their location) are effected by choosing that Coord. All targets with this method 120 * are valued equally, and the ideal location affects as many as possible without hitting any requiredExclusions. 121 * 122 * YOU MUST CALL setMap() with the current map status at some point before using this method, and call it again if 123 * the map changes. Failure to do so can cause serious bugs, from logic errors where monsters consider a door 124 * closed when it is open or vice versa, to an ArrayIndexOutOfBoundsException being thrown if the player moved to a 125 * differently-sized map and the Technique tries to use the previous map with coordinates from the new one. 126 * 127 * @param user The location of the user of this Technique 128 * @param targets Set of Coord of desirable targets to include in the area of this Technique, as many as possible. 129 * @param requiredExclusions Set of Coord where each value is something this Technique will really try to avoid. 130 * @return LinkedHashMap of Coord keys representing target points to pass to apply, to ArrayList of Coord values representing what targets' locations will be affected. 131 */ 132 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Coord user, Set<Coord> targets, Set<Coord> requiredExclusions) { 133 aoe.setOrigin(user); 134 return aoe.idealLocations(targets, requiredExclusions); 135 136 } 137 138 /** 139 * Get a mapping of Coord keys representing locations to apply this Technique to, to ArrayList of Coord values 140 * representing which targets (by their location) are effected by choosing that Coord. This method will strongly 141 * prefer including priorityTargets in its area, especially multiple one if possible, and primarily uses 142 * lesserTargets as a tiebreaker if two locations have the same number of included priorityTargets but one has more 143 * lesserTargets in its area. 144 * 145 * YOU MUST CALL setMap() with the current map status at some point before using this method, and call it again if 146 * the map changes. Failure to do so can cause serious bugs, from logic errors where monsters consider a door 147 * closed when it is open or vice versa, to an ArrayIndexOutOfBoundsException being thrown if the player moved to a 148 * differently-sized map and the Technique tries to use the previous map with coordinates from the new one. 149 * 150 * @param user The location of the user of this Technique 151 * @param priorityTargets Set of Coord of important targets to include in the area of this Technique, preferring to target a single priorityTarget over four lesserTargets. 152 * @param lesserTargets Set of Coord of desirable targets to include in the area of this Technique, as many as possible without excluding priorityTargets. 153 * @param requiredExclusions Set of Coord where each value is something this Technique will really try to avoid. 154 * @return LinkedHashMap of Coord keys representing target points to pass to apply, to ArrayList of Coord values representing what targets' locations will be affected. 155 */ 156 public LinkedHashMap<Coord, ArrayList<Coord>> idealLocations(Coord user, Set<Coord> priorityTargets, Set<Coord> lesserTargets, Set<Coord> requiredExclusions) { 157 aoe.setOrigin(user); 158 return aoe.idealLocations(priorityTargets, lesserTargets, requiredExclusions); 159 } 160 161 /** 162 * This does one last validation of the location aimAt (checking that it is within the valid range for this 163 * Technique) before getting the area affected by the AOE targeting that cell. It considers the origin of the AOE 164 * to be the Coord parameter user, for purposes of directional limitations and for AOE implementations that need 165 * the user's location, such as ConeAOE and LineAOE. 166 * 167 * YOU MUST CALL setMap() with the current map status at some point before using this method, and call it again if 168 * the map changes. Failure to do so can cause serious bugs, from logic errors where monsters consider a door 169 * closed when it is open or vice versa, to an ArrayIndexOutOfBoundsException being thrown if the player moved to a 170 * differently-sized map and the Technique tries to use the previous map with coordinates from the new one. 171 * 172 * @param user The position of the Technique's user, x first, y second. 173 * @param aimAt A target Coord typically obtained from idealLocations that determines how to position the AOE. 174 * @return a HashMap of Coord keys to Double values from 1.0 (fully affected) to 0.0 (unaffected). 175 */ 176 public LinkedHashMap<Coord, Double> apply(Coord user, Coord aimAt) 177 { 178 aoe.setOrigin(user); 179 aoe.shift(aimAt); 180 return aoe.findArea(); 181 } 182}