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