View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.module.sensor;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.Comparator;
6   import java.util.HashSet;
7   import java.util.List;
8   import java.util.Set;
9   
10  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
11  import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.Weapon;
12  import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.Weaponry;
13  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
14  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
15  import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
16  
17  /**
18   * Class that allows you to easily define weapon preferences for your bot as well as time how often you may change your weapon.
19   * <p><p>
20   * Use this class in {@link IUT2004BotController#prepareBot(UT2004Bot)}, i.e., use methods such as {@link WeaponPrefs#addGeneralPref(ItemType, boolean)},
21   * {@link WeaponPrefs#newPrefsRange(double)}, {@link WeaponPrefsRange#add(ItemType, boolean)}.
22   * <p><p>
23   * Preferences are never automatically wiped out!
24   * 
25   * @author Jimmy
26   */
27  public class WeaponPrefs {
28  
29  	protected List<WeaponPrefsRange> prefs = new ArrayList<WeaponPrefsRange>();
30  	protected Weaponry weaponry;
31  	protected UT2004Bot bot;
32  	protected WeaponPrefsRange generalPrefs;
33  	protected WeaponPrefs onlyGeneral;
34  	
35  	public WeaponPrefs(Weaponry weaponry, UT2004Bot bot) {
36  		this.weaponry = weaponry;
37  		this.bot = bot;
38  		this.generalPrefs = new WeaponPrefsRange(this, 0);
39  		this.onlyGeneral = new WeaponPrefs(weaponry, bot, new WeaponPrefsRange(this, 0)) {
40  			@Override
41  			public WeaponPrefsRange newPrefsRange(double minDistance) {
42  				throw new IllegalStateException("Can't invoke the method on 'generalOnly' preferences!");
43  			}
44  			@Override
45  			public WeaponPrefs addGeneralPref(ItemType weapon, boolean usePrimaryMode) {
46  				throw new IllegalStateException("Can't invoke the method on 'generalOnly' preferences!");
47  			}
48  			@Override
49  			public WeaponPrefs addGeneralPref(Weapon weapon, boolean usePrimaryMode) {
50  				throw new IllegalStateException("Can't invoke the method on 'generalOnly' preferences!");
51  			}
52  		};
53  	}
54  	
55  	protected WeaponPrefs(Weaponry weaponry, UT2004Bot bot, WeaponPrefsRange generalPrefs) {
56  		this.weaponry = weaponry;
57  		this.bot = bot;
58  		this.generalPrefs = new WeaponPrefsRange(this, generalPrefs);
59  		this.onlyGeneral = this;
60  	}
61  	
62  	/**
63  	 * Return weapon preferences that has only "general" weapon preferences, might come in handy.
64  	 * <p><p>
65  	 * WARNING: returned prefs are IMMUTABLE! You can't invoke method {@link WeaponPrefs#newPrefsRange(double)},  {@link WeaponPrefs#addGeneralPref(ItemType, boolean)} or {@link WeaponPrefs#addGeneralPref(Weapon, boolean)}.
66  	 * @return 
67  	 */
68  	public WeaponPrefs asGeneralOnly() {
69  		return this.onlyGeneral;
70  	}
71  	
72  	/**
73  	 * Removes all weapon preferences.
74  	 */
75  	public void clearAllPrefs() {
76  		prefs.clear();
77  		generalPrefs.clear();
78  		onlyGeneral.generalPrefs.clear();
79  	}
80  	
81  	/**
82  	 * Returns general weapon preferences.
83  	 * 
84  	 * @return
85  	 */
86  	public WeaponPrefsRange getGeneralPrefs() {
87  		return generalPrefs;
88  	}
89  	
90  	/**
91  	 * Adds another weapon as "the least preferable" one into general-preferences (used if no weapons are found for a given range)
92  	 * You may define weapons from the most preferred to the least preferred by sequentially calling this method.
93  	 * 
94  	 * @param weapon weapon to be used
95  	 * @param usePrimaryMode true == use primary firing mode, false == use secondary firing mode
96  	 */
97  	public WeaponPrefs addGeneralPref(ItemType weapon, boolean usePrimaryMode) {
98  		generalPrefs.add(weapon, usePrimaryMode);
99  		onlyGeneral.generalPrefs.add(weapon, usePrimaryMode);
100 		return this;
101 	}
102 	
103 	/**
104 	 * Adds another weapon as "the least preferable" one into general-preferences (used if no weapons are found for a given range)
105 	 * You may define weapons from the most preferred to the least preferred by sequentially calling this method.
106 	 * 
107 	 * @param weapon weapon to be used
108 	 * @param usePrimaryMode true == use primary firing mode, false == use secondary firing mode
109 	 */
110 	public WeaponPrefs addGeneralPref(Weapon weapon, boolean usePrimaryMode) {
111 		generalPrefs.add(weapon, usePrimaryMode);
112 		onlyGeneral.generalPrefs.add(weapon, usePrimaryMode);
113 		return this;
114 	}
115 
116 	/**
117 	 * Creates new {@link WeaponPrefsRange}, these weapon will be used when the target is at "maxDistance" afar. Lower bound (minDistance)
118 	 * is then define by previous WeaponPrefsRange object (if such exist, otherwise it is 0).
119 	 * 
120 	 * @param maxDistance
121 	 * @return
122 	 */
123 	public WeaponPrefsRange newPrefsRange(double maxDistance) {
124 		WeaponPrefsRange newPrefs = new WeaponPrefsRange(this, maxDistance);
125 		this.prefs.add(newPrefs);
126 		Collections.sort(this.prefs, new Comparator<WeaponPrefsRange>() {
127 			@Override
128 			public int compare(WeaponPrefsRange o1, WeaponPrefsRange o2) {
129 				double diff = o1.getMaxDistance() - o2.getMaxDistance();
130 				if (diff > 0) return 1;
131 				if (diff < 0) return -1;
132 				return 0;
133 			}
134 		});
135 		return newPrefs;
136 	}
137 	
138 	/**
139 	 * Get preferences for a given distance. 
140 	 * <p><p>
141 	 * Distance may be negative == will choose only from the general preferences.
142 	 * <p><p>
143 	 * If no "ranges" are defined (no {@link WeaponPrefs#newPrefsRange(double)} has been used), it returns {@link WeaponPrefs#generalPrefs}.
144 	 * 
145 	 * @param distance
146 	 * @return
147 	 */
148 	public WeaponPrefsRange getWeaponPreferences(double distance) {
149 		if (distance < 0) {
150 			return generalPrefs;
151 		}
152 		if (prefs.size() == 0) return generalPrefs;
153 		int i = 0;
154 		for (WeaponPrefsRange pref : prefs) {
155 			double minDistance = pref.getMinDistance();
156 			if (minDistance > distance) {
157 				if (i == 0) return null;
158 				return prefs.get(i-1);
159 			}
160 			++i;
161 		}
162 		return prefs.get(prefs.size()-1);
163 	}
164 
165 	/**
166 	 * Get range that is right after "weaponPrefsRange".
167 	 * @param weaponPrefsRange
168 	 * @return
169 	 */
170 	protected WeaponPrefsRange getPreviousRange(WeaponPrefsRange weaponPrefsRange) {
171 		if (weaponPrefsRange == generalPrefs) return null;
172 		int index = prefs.indexOf(weaponPrefsRange);
173 		if (index < 1) return null;
174 		return prefs.get(index-1);
175 	}
176 	
177 	/**
178 	 * Return the best weapon the bot has for a given distance (choosing right weapon preferances for a given distance).
179 	 * <p><p>
180 	 * May return null if "general preferences are not defined" or the bot does not have ammo to any of defined weapons.
181 	 * <p><p>
182 	 * Note that it may actually return "forbiddenWeapon" in the case that the bot does not have ammo for any other "more preferred" weapon
183 	 * but has ammo for some of forbiddenWeapon.
184 	 * <p><p>
185 	 * If distance < 0, only general preferences will be used.
186 	 * 
187 	 * @param distance
188 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
189 	 * @return
190 	 */
191 	public WeaponPref getWeaponPreference(double distance, ItemType... forbiddenWeapons) {
192 		WeaponPref pref = null;
193 		if (distance >= 0 && prefs.size() != 0) {
194 			WeaponPrefsRange range = getWeaponPreferences(distance); 
195 			pref = range.getWeaponPreference(forbiddenWeapons);
196 			if (pref != null) {
197 				return pref;
198 			}
199 		}
200 		pref = generalPrefs.getWeaponPreference(forbiddenWeapons);
201 		if (pref != null) return pref;
202 		pref = generalPrefs.getWeaponPreference();
203 		if (pref != null) return pref;
204 		if (weaponry.getCurrentWeapon() != null) {
205 			return new WeaponPref(weaponry.getCurrentWeapon().getType(), true);
206 		}
207 		return null;
208 	}
209 	
210 	/**
211 	 * Return the best weapon the bot has to shoot at given location (choosing right weapon preferances for a given distance).
212 	 * <p><p>
213 	 * May return null if "general preferences are not defined" or the bot does not have ammo to any of defined weapons.
214 	 * <p><p>
215 	 * If target is null, only general preferences will be used.
216 	 * 
217 	 * @param target
218 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
219 	 * @return
220 	 */
221 	public WeaponPref getWeaponPreference(ILocated target, ItemType... forbiddenWeapons) {
222 		if (target == null) {
223 			return getWeaponPreference(-1, forbiddenWeapons);
224 		} else {
225 			return getWeaponPreference(bot.getLocation().getDistance(target.getLocation()), forbiddenWeapons);
226 		}
227 	}
228 	
229 	/**
230 	 * Return the best weapon according ONLY general preferences.
231 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
232 	 * @return
233 	 */
234 	public WeaponPref getWeaponPreference(ItemType... forbiddenWeapons) {
235 		return getWeaponPreference(-1, forbiddenWeapons);
236 	}
237 	
238 	/**
239 	 * Return the best weapon the bot has for a given distance (choosing right weapon preferances for a given distance).
240 	 * <p><p>
241 	 * May return null if "general preferences are not defined" or the bot does not have ammo to any of defined weapons.
242 	 * <p><p>
243 	 * Note that it may actually return "forbiddenWeapon" in the case that the bot does not have ammo for any other "more preferred" weapon
244 	 * but has ammo for some of forbiddenWeapon.
245 	 * <p><p>
246 	 * If distance < 0, only general preferences will be used.
247 	 * 
248 	 * @param distance
249 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link WeaponPref#ROCKET_LAUNCHER})
250 	 * @return
251 	 */
252 	public WeaponPref getWeaponPreference(double distance, WeaponPref... forbiddenWeapons) {
253 		WeaponPref pref = null;
254 		if (distance >= 0 && prefs.size() != 0) {
255 			WeaponPrefsRange range = getWeaponPreferences(distance); 
256 			pref = range.getWeaponPreference(forbiddenWeapons);
257 			if (pref != null) {
258 				return pref;
259 			}
260 		}
261 		pref = generalPrefs.getWeaponPreference(forbiddenWeapons);
262 		if (pref != null) return pref;
263 		pref = generalPrefs.getWeaponPreference();
264 		if (pref != null) return pref;
265 		if (weaponry.getCurrentWeapon() != null) {
266 			return new WeaponPref(weaponry.getCurrentWeapon().getType(), true);
267 		}
268 		return null;
269 	}
270 	
271 	/**
272 	 * Return the best weapon the bot has to shoot at given location (choosing right weapon preferances for a given distance).
273 	 * <p><p>
274 	 * May return null if "general preferences are not defined" or the bot does not have ammo to any of defined weapons.
275 	 * <p><p>
276 	 * If target is null, only general preferences will be used.
277 	 * 
278 	 * @param target
279 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose.
280 	 * @return
281 	 */
282 	public WeaponPref getWeaponPreference(ILocated target, WeaponPref... forbiddenWeapons) {
283 		if (target == null) {
284 			return getWeaponPreference(-1, forbiddenWeapons);
285 		} else {
286 			return getWeaponPreference(bot.getLocation().getDistance(target.getLocation()), forbiddenWeapons);
287 		}
288 	}
289 	
290 	/**
291 	 * Return the best weapon according ONLY general preferences.
292 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose.
293 	 * @return
294 	 */
295 	public WeaponPref getWeaponPreference(WeaponPref... forbiddenWeapons) {
296 		return getWeaponPreference(-1, forbiddenWeapons);
297 	}
298 	
299 	/**
300 	 * Return the best weapon the bot has for a given distance (choosing right weapon preferances for a given distance).
301 	 * <p><p>
302 	 * May return null if "general preferences are not defined" or the bot does not have ammo to any of defined weapons.
303 	 * <p><p>
304 	 * If distance < 0, only general preferences will be used.
305 	 * 
306 	 * @param distance
307 	 * @return
308 	 */
309 	public WeaponPref getWeaponPreference(double distance) {
310 		return getWeaponPreference(distance, (ItemType[])null);
311 	}
312 	
313 	/**
314 	 * Return the best weapon the bot has to shoot at given location (choosing right weapon preferances for a given distance).
315 	 * <p><p>
316 	 * May return null if "general preferences are not defined" or the bot does not have ammo to any of defined weapons.
317 	 * <p><p>
318 	 * If target is null, only general preferences will be used.
319 	 * 
320 	 * @param target
321 	 * @return
322 	 */
323 	public WeaponPref getWeaponPreference(ILocated target) {
324 		return getWeaponPreference(target, (ItemType[])null);
325 	}
326 	
327 	/**
328 	 * Return the best weapon according ONLY general preferences.
329 	 * @return
330 	 */
331 	public WeaponPref getWeaponPreference() {
332 		return getWeaponPreference(-1, (ItemType[])null);
333 	}
334 	
335 	/**
336 	 * Returns all {@link WeaponPref} defined in general preferences + all range preferences, i.e.,
337 	 * all weapons your bot might be interested in. 
338 	 *
339 	 * @return
340 	 */
341 	public Set<WeaponPref> getPreferredWeapons() {
342 		Set<WeaponPref> result = new HashSet<WeaponPref>();
343 		
344 		result.addAll(generalPrefs.getPrefs());
345 		for (WeaponPrefsRange range : prefs) {
346 			result.addAll(range.getPrefs());
347 		}
348 		
349 		return result;
350 	}
351 	
352 }