View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.bot.command;
2   
3   import java.util.logging.Logger;
4   
5   import cz.cuni.amis.pogamut.base.communication.command.ICommandListener;
6   import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
7   import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectListener;
8   import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
9   import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
10  import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.Weapon;
11  import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.Weaponry;
12  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.WeaponPref;
13  import cz.cuni.amis.pogamut.ut2004.agent.module.sensor.WeaponPrefs;
14  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
15  import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
16  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.ChangeWeapon;
17  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Shoot;
18  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StopShooting;
19  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
20  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
21  import cz.cuni.amis.pogamut.ut2004.communication.translator.itemdescriptor.WeaponDescriptor;
22  import cz.cuni.amis.utils.NullCheck;
23  
24  /**
25   * This shooting will allow you to define with which {@link Weapon} / {@ItemType} / {@link WeaponPrefs} you want to shoot.
26   * It will also automatically handles rearming to the desired weapon if needed and watches over which weapon/firing mode
27   * you've used in previous logic iteration (if any).
28   * <p><p>
29   * Further more it allows you to define cool down that specify how often the bot may change its weapon via {@link ImprovedShooting#setChangeWeaponCooldown(long)}.
30   * But rest assured that this can be overriden as there are two types of method here:
31   * <ol>
32   * <li>methods that DOES NOT OBEY the cooldown - they have suffix 'Now', e.g., {@link ImprovedShooting#shootNow(WeaponPrefs, Player, ItemType...)}</li>
33   * <li>methods that OBEY the cooldown - they DO NOT HAVE suffix 'Now', e.g., {@link ImprovedShooting#shoot(WeaponPrefs, Player, ItemType...)}</li>
34   * </ol>
35   * 
36   * @author Jimmy
37   */
38  public class ImprovedShooting extends AdvancedShooting {
39  
40  	private Weaponry weaponry;
41  	
42  	private ItemType currentWeapon = null;
43  	
44  	private Self self;
45  	
46  	private IWorldObjectListener<Self> selfListener = new IWorldObjectListener<Self>() {
47  
48  		@Override
49  		public void notify(IWorldObjectEvent<Self> event) {
50  			self = event.getObject();
51  			if (weaponry.getCurrentWeapon() == null) {
52  				currentWeapon = null;
53  			} else {
54  				currentWeapon = weaponry.getCurrentWeapon().getType();
55  			}
56  		}
57  		
58  	};
59  	
60  	/**
61  	 * {@link System#currentTimeMillis()} when the bot changed the weapon for the last time.
62  	 */
63  	private long lastChangeWeapon = 0;
64  	
65  	/**
66  	 * Minimum time (in millis) between {@link ChangeWeapon} commands. Default: 1000 (1 second).
67  	 */
68  	private long changeWeaponCooldown = 1000;
69  	
70  	private ICommandListener<ChangeWeapon> changeWeaponListener = new ICommandListener<ChangeWeapon>() {
71  
72  		@Override
73  		public void notify(ChangeWeapon event) {
74  			WeaponDescriptor weapon = weaponry.getDescriptorForId(UnrealId.get(event.getId()));
75  			if (weapon == null) {
76  				log.warning("Unknown weapon descriptor for item of inventory ID: " + event.getId());
77  			}
78  			currentWeapon = weapon.getPickupType();
79  			lastChangeWeapon = System.currentTimeMillis();
80  		}
81  		
82  	};
83  
84  	private WeaponPref lastShooting = null;
85  	
86  	private ICommandListener<Shoot> shootListener = new ICommandListener<Shoot>() {
87  
88  		@Override
89  		public void notify(Shoot event) {
90  			if (currentWeapon == null) {
91  				lastShooting = null;
92  			} else {
93  				lastShooting = new WeaponPref(currentWeapon, event.isAlt() == null || !event.isAlt());
94  			}
95  		}
96  		
97  	};
98  	
99  	private ICommandListener<StopShooting> stopShootingListener = new ICommandListener<StopShooting>() {
100 
101 		@Override
102 		public void notify(StopShooting event) {
103 			lastShooting = null;
104 		}
105 		
106 	};
107 
108 	public ImprovedShooting(Weaponry weaponry, UT2004Bot agent, Logger log) {
109 		super(agent, log);
110 		NullCheck.check(weaponry, "weaponry");
111 		this.weaponry = weaponry;
112 		
113 		agent.getWorldView().addObjectListener(Self.class, selfListener);
114 		agent.getAct().addCommandListener(Shoot.class, shootListener);
115 		agent.getAct().addCommandListener(StopShooting.class, stopShootingListener);
116 		agent.getAct().addCommandListener(ChangeWeapon.class, changeWeaponListener);
117 	}
118 	
119 	/**
120 	 * Whether bot may change weapon (change weapon cooled down as defined by {@link ImprovedShooting#setChangeWeaponCooldown(long)}). 
121 	 * @return
122 	 */
123 	public boolean mayChangeWeapon() {
124 		return System.currentTimeMillis() - lastChangeWeapon > changeWeaponCooldown;
125 	}
126 	
127 	/**
128 	 * Get {@link WeaponPref} for actually used weapon.
129 	 * @return
130 	 */
131 	public WeaponPref getActualWeaponPref() {
132 		if (lastShooting != null && lastShooting.getWeapon() == currentWeapon) return lastShooting;
133 		if (currentWeapon != null) return new WeaponPref(currentWeapon, true);
134 		return null;
135 	}
136 	
137 	/**
138 	 * Get {@link WeaponPref} for actually used weapon.
139 	 * @return
140 	 */
141 	protected ItemType getActualWeaponType() {
142 		if (currentWeapon != null) return currentWeapon;
143 		return null;
144 	}
145 	
146 	//
147 	//
148 	// CHANGE WEAPON SECTION
149 	//
150 	//
151 	
152 	/**
153 	 * Changes the weapon the bot is currently holding (if the bot has the weapon and its ammo > 0).
154 	 * <p><p>
155 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
156 	 * 
157 	 * @param weaponType
158 	 * @return which weapon we have chosen, null == no suitable weapon found
159 	 */
160 	public boolean changeWeapon(ItemType weaponType) {
161 		if (weaponType == null) return false;
162 		if (currentWeapon == weaponType) return true;
163 		if (!mayChangeWeapon()) return false;
164 		return changeWeaponNow(weaponType);
165 	}
166 
167 	/**
168 	 * Changes the weapon the bot is currently holding (if the bot has the weapon and its ammo > 0).
169 	 * <p><p>
170 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
171 	 * 
172 	 * @param weapon
173 	 * @return which weapon we have chosen, null == no suitable weapon found
174 	 */
175 	public boolean changeWeapon(Weapon weapon) {
176 		if (weapon == null) return false;
177 		if (currentWeapon == weapon.getType()) return true;
178 		if (!mayChangeWeapon()) return false;
179 		return changeWeaponNow(weapon);
180 	}
181 	
182 	/**
183 	 * Changes the weapon the bot is currently holding (if the bot has the weapon and its primary/secondary (according to 'pref') ammo > 0).
184 	 * <p><p>
185 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
186 	 * 
187 	 * @param pref
188 	 * @return which weapon we have chosen, null == no suitable weapon found
189 	 */
190 	public boolean changeWeapon(WeaponPref pref) {
191 		if (pref == null) return false;
192 		if (currentWeapon == pref.getWeapon()) return true;
193 		if (!mayChangeWeapon()) return false;
194 		return changeWeaponNow(pref);
195 	}
196 	
197 	/**
198 	 * Arms the best weapon according to general preferences in 'weaponPrefs'.
199 	 * <p><p>
200 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
201 	 * 
202 	 * @param weaponPrefs
203 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
204 	 * @return which weapon we have chosen, null == no suitable weapon found
205 	 */
206 	public WeaponPref changeWeapon(WeaponPrefs weaponPrefs, ItemType... forbiddenWeapons) {
207 		WeaponPref chosen = weaponPrefs.getWeaponPreference(forbiddenWeapons);
208 		if (changeWeapon(chosen)) return chosen;
209 		return getActualWeaponPref();
210 	}
211 	
212 	/**
213 	 * Arms the best weapon according to distance from 'weaponPrefs'.
214 	 * <p><p>
215 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
216 	 * 
217 	 * @param weaponPrefs
218 	 * @param distance choose weapon according to distance
219 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
220 	 * @return which weapon we have chosen, null == no suitable weapon found
221 	 */
222 	public WeaponPref changeWeapon(WeaponPrefs weaponPrefs, double distance, ItemType... forbiddenWeapons) {
223 		WeaponPref chosen = weaponPrefs.getWeaponPreference(distance, forbiddenWeapons);
224 		if (changeWeapon(chosen)) return chosen;
225 		return getActualWeaponPref();
226 	}
227 	
228 	/**
229 	 * Arms the best weapon according to distance from 'weaponPrefs'.
230 	 * <p><p>
231 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
232 	 * 
233 	 * @param weaponPrefs
234 	 * @param target choose weapon according to distance to the target (from the bot)
235 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
236 	 * @return which weapon we have chosen, null == no suitable weapon found
237 	 */
238 	public WeaponPref changeWeapon(WeaponPrefs weaponPrefs, ILocated target, ItemType... forbiddenWeapons) {
239 		WeaponPref chosen = weaponPrefs.getWeaponPreference(target, forbiddenWeapons);
240 		if (changeWeapon(chosen)) return chosen;
241 		return getActualWeaponPref();
242 	}
243 	
244 	//
245 	//
246 	// CHANGE WEAPON NOW SECTION
247 	//
248 	//
249 	
250 	/**
251 	 * Changes the weapon the bot is currently holding (if the bot has the weapon and its ammo > 0).
252 	 * <p><p>
253 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
254 	 * 
255 	 * @param weaponType
256 	 * @return whether we have changed the weapon (i.e., we have some ammo for it), or we already have it prepared (i.e., is our current weapon)
257 	 */
258 	public boolean changeWeaponNow(ItemType weaponType) {
259 		if (weaponType == null) return false;
260 		return weaponry.changeWeapon(weaponType);
261 	}
262 
263 	/**
264 	 * Changes the weapon the bot is currently holding (if the bot has the weapon and its ammo > 0).
265 	 * <p><p>
266 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
267 	 * 
268 	 * @param weapon
269 	 * @return whether we have changed the weapon (i.e., we have some ammo for it), or we already have it prepared (i.e., is our current weapon)
270 	 */
271 	public boolean changeWeaponNow(Weapon weapon) {
272 		if (weapon == null) return false;
273 		return weaponry.changeWeapon(weapon);
274 	}
275 	
276 	/**
277 	 * Changes the weapon the bot is currently holding (if the bot has the weapon and its primary/secondary (according to 'pref') ammo > 0).
278 	 * <p><p>
279 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
280 	 * 
281 	 * @param pref
282 	 * @return whether we have changed the weapon (i.e., we have some ammo for required firing mode), or we already have it prepared (i.e., is our current weapon and we have enough ammo)
283 	 */
284 	public boolean changeWeaponNow(WeaponPref pref) {
285 		if (pref == null) return false;
286 		if (pref.isPrimary()) {
287 			if (weaponry.hasPrimaryLoadedWeapon(pref.getWeapon())) {
288 				return changeWeaponNow(pref.getWeapon());
289 			} else {
290 				log.info("Can't change to weapon-primary " + pref.getWeapon() + " as we do not have the weapon/or ammo for doing so.");
291 				return false;
292 			}
293 		} else {
294 			if (weaponry.hasSecondaryLoadedWeapon(pref.getWeapon())) {
295 				return changeWeaponNow(pref.getWeapon());
296 			} else {
297 				log.info("Can't change to weapon-secondary " + pref.getWeapon() + " as we do not have the weapon/or ammo for doing so.");
298 				return false;
299 			}				
300 		}
301 	}
302 
303 	/**
304 	 * Arms the best weapon according to general preferences in 'weaponPrefs'.
305 	 * <p><p>
306 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
307 	 * 
308 	 * @param weaponPrefs
309 	 */
310 	public WeaponPref changeWeaponNow(WeaponPrefs weaponPrefs, ItemType... forbiddenWeapons) {
311 		WeaponPref chosen = weaponPrefs.getWeaponPreference(forbiddenWeapons);
312 		if (changeWeaponNow(chosen)) return chosen;
313 		return getActualWeaponPref();
314 	}
315 	
316 	/**
317 	 * Arms the best weapon according to distance from 'weaponPrefs'.
318 	 * <p><p>
319 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
320 	 * 
321 	 * @param weaponPrefs
322 	 * @param distance choose weapon according to distance
323 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
324 	 * @return which weapon we have chosen, null == no suitable weapon found
325 	 */
326 	public WeaponPref changeWeaponNow(WeaponPrefs weaponPrefs, double distance, ItemType... forbiddenWeapons) {		
327 		WeaponPref chosen = weaponPrefs.getWeaponPreference(distance, forbiddenWeapons);
328 		if (changeWeaponNow(chosen)) return chosen;
329 		return getActualWeaponPref();
330 	}
331 	
332 	/**
333 	 * Arms the best weapon according to distance from 'weaponPrefs'.
334 	 * <p><p>
335 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
336 	 * 
337 	 * @param weaponPrefs
338 	 * @param target choose weapon according to distance to the target (from the bot)
339 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER})
340 	 * @return which weapon we have chosen, null == no suitable weapon found
341 	 */
342 	public WeaponPref changeWeaponNow(WeaponPrefs weaponPrefs, ILocated target, ItemType... forbiddenWeapons) {
343 		WeaponPref chosen = weaponPrefs.getWeaponPreference(target, forbiddenWeapons);
344 		if (changeWeaponNow(chosen)) return chosen;
345 		return getActualWeaponPref();
346 	}
347 	
348 	//
349 	//
350 	// SHOOT SECTION
351 	//
352 	//
353 	
354 	/**
355 	 * Will start shooting with {@link WeaponPref#getWeapon()} primary/secondary (as specified) at 'target'.
356 	 * <p><p>
357 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}. If cooldown has not been reached yet, it will keep firing with
358 	 * current weapon.
359 	 * 
360 	 * @param pref
361 	 * @param target
362 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
363 	 */
364 	public boolean shoot(WeaponPref pref, UnrealId target) {
365 		if (pref == null) return false;
366 		if (currentWeapon == pref.getWeapon()) {
367 			if (pref.isPrimary()) shoot(target);
368 			else shootSecondary(target);
369 			return true;
370 		}
371 		if (!mayChangeWeapon()) {
372 			if (lastShooting != null && currentWeapon == lastShooting.getWeapon()) {
373 				if (lastShooting.isPrimary()) shoot(target);
374 				else shootSecondary(target);
375 			} else {
376 				shoot(target);
377 			}
378 			return true;
379 		}
380 		return shootNow(pref, target);
381 	}
382 	
383 	/**
384 	 * Will start shooting with {@link WeaponPref#getWeapon()} primary/secondary (as specified) at 'target'.
385 	 * <p><p>
386 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}. If cooldown has not been reached yet, it will keep firing with
387 	 * current weapon.
388 	 * 
389 	 * @param pref
390 	 * @param target
391 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
392 	 */
393 	public boolean shoot(WeaponPref pref, ILocated target) {
394 		if (pref == null) return false;
395 		if (currentWeapon == pref.getWeapon()) {
396 			if (pref.isPrimary()) shoot(target);
397 			else shootSecondary(target);
398 			return true;
399 		}
400 		if (!mayChangeWeapon()) {
401 			if (lastShooting != null && currentWeapon == lastShooting.getWeapon()) {
402 				if (lastShooting.isPrimary()) shoot(target);
403 				else shootSecondary(target);
404 			} else {
405 				shoot(target);
406 			}
407 			return true;
408 		}
409 		return shootNow(pref, target);
410 	}
411 	
412 	/**
413 	 * Will start shooting with {@link Weapon#getType()} primary/secondary (as specified) at 'target'.
414 	 * <p><p>
415 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
416 	 * 
417 	 * @param weapon
418 	 * @param usePrimaryMode
419 	 * @param target
420 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
421 	 */
422 	public boolean shoot(Weapon weapon, boolean usePrimaryMode, UnrealId target) {
423 		if (weapon == null) return false;
424 		if (target == null) return false;
425 		return shoot(new WeaponPref(weapon.getType(), usePrimaryMode), target);
426 	}
427 	
428 	/**
429 	 * Will start shooting with {@link Weapon#getType()} primary/secondary (as specified) at 'target'.
430 	 * <p><p>
431 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
432 	 * 
433 	 * @param weapon
434 	 * @param usePrimaryMode
435 	 * @param target
436 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
437 	 */
438 	public boolean shoot(Weapon weapon, boolean usePrimaryMode, ILocated target) {
439 		if (weapon == null) return false;
440 		if (target == null) return false;
441 		return shoot(new WeaponPref(weapon.getType(), usePrimaryMode), target);
442 	}
443 	
444 	/**
445 	 * Will start shooting with the best weapon (according to 'prefs') at target.
446 	 * <p><p>
447 	 * Obeys {@link ImprovedShooting#getChangeWeaponCooldown()}.
448 	 * 
449 	 * @param prefs
450 	 * @param target
451 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER}).
452 	 * @return what the bot is shooting with
453 	 */
454 	public WeaponPref shoot(WeaponPrefs prefs, ILocated target, ItemType... forbiddenWeapon) {
455 		NullCheck.check(prefs, "prefs");
456 		if (target == null) return null;
457 		WeaponPref chosen = prefs.getWeaponPreference(target, forbiddenWeapon);
458 		if (shoot(chosen, target)) return chosen;
459 		return getActualWeaponPref();
460 	}
461 	
462 	//
463 	//
464 	// SHOOT NOW SECTION
465 	//
466 	//
467 		
468 	/**
469 	 * Will start shooting with {@link WeaponPref#getWeapon()} primary/secondary (as specified) at 'target'.
470 	 * <p><p>
471 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
472 	 * 
473 	 * @param pref
474 	 * @param target
475 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
476 	 */
477 	public boolean shootNow(WeaponPref pref, UnrealId target) {
478 		if (pref == null) return false;
479 		if (target == null) return false;
480 		if (currentWeapon == pref.getWeapon() || changeWeaponNow(pref)) {
481 			if (pref.isPrimary()) shoot(target);
482 			else shootSecondary(target);
483 		} else
484 		if (lastShooting != null && currentWeapon == lastShooting.getWeapon()) {
485 			if (lastShooting.isPrimary()) shoot(target);
486 			else shootSecondary(target);
487 		} else {
488 			shoot(target);
489 		}
490 		return true;
491 	}
492 	
493 	/**
494 	 * Will start shooting with {@link WeaponPref#getWeapon()} primary/secondary (as specified) at 'target'.
495 	 * <p><p>
496 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
497 	 * 
498 	 * @param pref
499 	 * @param target
500 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
501 	 */
502 	public boolean shootNow(WeaponPref pref, ILocated target) {
503 		if (pref == null) return false;
504 		if (target == null) return false;
505 		if (currentWeapon == pref.getWeapon() || changeWeaponNow(pref)) {
506 			if (pref.isPrimary()) shoot(target);
507 			else shootSecondary(target);
508 			return true;
509 		}
510 		if (lastShooting != null && currentWeapon == lastShooting.getWeapon()) {
511 			if (lastShooting.isPrimary()) shoot(target);
512 			else shootSecondary(target);
513 			return true;
514 		}
515 		shoot(target);
516 		return true;
517 	}
518 	
519 	/**
520 	 * Will start shooting with {@link Weapon#getType()} primary/secondary (as specified) at 'target'.
521 	 * <p><p>
522 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
523 	 * 
524 	 * @param weapon
525 	 * @param usePrimaryMode
526 	 * @param target
527 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
528 	 */
529 	public boolean shootNow(Weapon weapon, boolean usePrimaryMode, UnrealId target) {
530 		if (weapon == null) return false;
531 		if (target == null) return false;
532 		return shootNow(new WeaponPref(weapon.getType(), usePrimaryMode), target);
533 	}
534 	
535 	/**
536 	 * Will start shooting with {@link Weapon#getType()} primary/secondary (as specified) at 'target'.
537 	 * <p><p>
538 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
539 	 * 
540 	 * @param weapon
541 	 * @param usePrimaryMode
542 	 * @param target
543 	 * @return whether the bot is shooting (i.e., has enough ammo to do so)
544 	 */
545 	public boolean shootNow(Weapon weapon, boolean usePrimaryMode, ILocated target) {
546 		if (weapon == null) return false;
547 		if (target == null) return false;
548 		return shootNow(new WeaponPref(weapon.getType(), usePrimaryMode), target);
549 	}
550 	
551 	/**
552 	 * Will start shooting with the best weapon (according to 'prefs') at target.
553 	 * <p><p>
554 	 * DOES NOT OBEY {@link ImprovedShooting#getChangeWeaponCooldown()}.
555 	 * 
556 	 * @param prefs
557 	 * @param target
558 	 * @param forbiddenWeapons optionally, you may define weapons which bot should not choose (i.e. {@link ItemType#ROCKET_LAUNCHER}).
559 	 * @return what weapon the bot is shooting with
560 	 */
561 	public WeaponPref shootNow(WeaponPrefs prefs, ILocated target, ItemType... forbiddenWeapon) {
562 		NullCheck.check(prefs, "prefs");
563 		if (target == null) return null;
564 		WeaponPref chosen = prefs.getWeaponPreference(target, forbiddenWeapon);
565 		if (shootNow(chosen, target)) return chosen;
566 		return getActualWeaponPref();
567 	}
568 	
569 	//
570 	//
571 	// UTILITY METHODS
572 	//
573 	//
574 	
575 	/**
576 	 * Return last type of weapon / mode of firing you've used (or null, if you have issued {@link StopShooting} command.
577 	 * <p><p>
578 	 * May be null!
579 	 * 
580 	 * @return
581 	 */
582 	public WeaponPref getLastShooting() {
583 		return lastShooting;
584 	}
585 
586 	/**
587 	 * {@link System#currentTimeMillis()} when the bot changed the weapon for the last time.
588 	 * @return
589 	 */
590 	public long getLastChangeWeapon() {
591 		return lastChangeWeapon;
592 	}
593 
594 	/**
595 	 * Returns how often the bot may change weapon. Default: 1000 (1 second).
596 	 * <p><p>
597 	 * DOES NOT APPLY GENERALLY TO ALL {@link ChangeWeapon} COMMANDS! Just for methods that are NOT SUFFIXED with 'Now' in this class!
598 	 * 
599 	 * @return
600 	 */
601 	public long getChangeWeaponCooldown() {
602 		return changeWeaponCooldown;
603 	}
604 
605 	/**
606 	 * Sets how often the bot may change weapon. Default: 1000 (1 second).
607 	 * <p><p>
608 	 * DOES NOT APPLY GENERALLY TO ALL {@link ChangeWeapon} COMMANDS! Just for {@link ImprovedShooting#changeWeapon(ItemType)} methods
609 	 * defined here!
610 	 * 
611 	 * @return
612 	 */
613 	public void setChangeWeaponCooldown(long changeWeaponCooldownMillis) {
614 		this.changeWeaponCooldown = changeWeaponCooldownMillis;
615 	}
616 	
617 }