View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.module.sensor;
2   
3   import java.util.logging.Level;
4   import java.util.logging.Logger;
5   
6   import cz.cuni.amis.pogamut.base.agent.module.SensorModule;
7   import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
8   import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
9   import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
10  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
11  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
12  import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils;
13  import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated;
14  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
15  import cz.cuni.amis.pogamut.base3d.worldview.object.Rotation;
16  import cz.cuni.amis.pogamut.base3d.worldview.object.Velocity;
17  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
18  import cz.cuni.amis.pogamut.ut2004.agent.module.sensomotoric.Weaponry;
19  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
20  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotKilled;
23  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ConfigChange;
24  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.InitedMessage;
25  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Item;
26  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.NavPoint;
27  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
28  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerKilled;
29  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerScore;
30  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
31  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.VolumeChanged;
32  import cz.cuni.amis.utils.NullCheck;
33  
34  /**
35   * Memory module specialized on general info about the agent whereabouts.
36   * <p><p>
37   * It is designed to be initialized inside {@link IUT2004BotController#prepareBot(UT2004Bot)} method call
38   * and may be used since first {@link Self} message is received, i.e, since the first {@link IUT2004BotController#botFirstSpawn(cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo, ConfigChange, InitedMessage, Self)} 
39   * is called.
40   *
41   * @author Juraj 'Loque' Simlovic
42   * @author Jimmy
43   */
44  public class AgentInfo extends SensorModule<UT2004Bot>
45  {
46  	public static final String NONE_WEAPON_ID = "None";
47  
48  	/**
49  	 * Retreives a unique ID of the agent in the game.
50  	 *
51  	 * <p>Note: This ID does not change and can be relied upon during entire
52  	 * match. However, be aware that the ID may change between different matches
53  	 * and/or sessions.
54  	 *
55  	 * @return ID of the agent in the game.
56  	 */
57  	public UnrealId getId()
58  	{
59  		// retreive from self object
60      if (self == null) return null;
61      if (self.getBotId() != null) return self.getBotId();
62  		return self.getId();
63  	}
64  
65  	/**
66  	 * Retreives current name of the agent in the game.
67  	 *
68  	 * <p>Note: The agent may choose and change it's name during a match and it
69  	 * does not need to be unique among players. Even an empty string might be
70  	 * a valid name.
71  	 *
72  	 * @return Name of the agent in the game.
73  	 */
74  	public String getName()
75  	{
76  		// retreive from self object
77                  if (self == null) return null;
78  		return self.getName();
79  	}
80  
81  	/*========================================================================*/
82  
83  	/** Red team number. */
84  	public static final int TEAM_RED = 0;
85  	/** Blue team number. */
86  	public static final int TEAM_BLUE = 1;
87  	/** Green team number. */
88  	public static final int TEAM_GREEN = 2;
89  	/** Gold team number. */
90  	public static final int TEAM_GOLD = 3;
91  	/** No-team number. */
92  	public static final int TEAM_NONE = 255;
93  
94  	/**
95  	 * Retreives team number the agent is on.
96  	 *
97  	 * @return Team number the player is on.
98  	 *
99  	 * @see #TEAM_RED
100 	 * @see #TEAM_BLUE
101 	 * @see #TEAM_GREEN
102 	 * @see #TEAM_GOLD
103 	 * @see #TEAM_NONE
104 	 *
105 	 * @see isEnemy(int)
106 	 * @see isEnemy(Player)
107 	 * @see isFriend(int)
108 	 * @see isFriend(Player)
109 	 */
110 	public Integer getTeam()
111 	{
112 		// retreive from self object
113                 if (self == null) return null;
114 		return self.getTeam();
115 	}
116 
117 	/**
118 	 * Tells, whether a given team is an enemy team to the agent.
119 	 *
120 	 * @param team Team number to be tested.
121 	 * @return True, if the given team is an enemy team.
122 	 *
123 	 * @see getTeam()
124 	 * @see isFriend(int)
125 	 */
126 	public boolean isEnemy(int team)
127 	{
128 		// freelancers' team or different team
129 		return (team == TEAM_NONE) || (team != getTeam());
130 	}
131 
132 	/**
133 	 * Tells, whether a given player is an enemy to the agent.
134 	 *
135 	 * @param player Player to be tested.
136 	 * @return True, if the given player is an enemy.
137 	 *
138 	 * @see getTeam()
139 	 * @see isFriend(Player)
140 	 */
141 	public boolean isEnemy(Player player)
142 	{
143 		// test the enemy team number
144 		return isEnemy(player.getTeam());
145 	}
146 
147 	/**
148 	 * Tells, whether a given team is a friend team to the agent.
149 	 *
150 	 * @param team Team number to be tested.
151 	 * @return True, if the given team is a friend team.
152 	 *
153 	 * @see getTeam()
154 	 * @see isEnemy(int)
155 	 */
156 	public boolean isFriend(int team)
157 	{
158 		// same team only
159 		return (team == getTeam());
160 	}
161 
162 	/**
163 	 * Tells, whether a given player is a friend to the agent.
164 	 *
165 	 * @param player Player to be tested.
166 	 * @return True, if the given player is a friend.
167 	 *
168 	 * @see getTeam()
169 	 * @see isEnemy(Player)
170 	 */
171 	public boolean isFriend(Player player)
172 	{
173 		// test the friend team number
174 		return isFriend(player.getTeam());
175 	}
176 
177 	/*========================================================================*/
178 
179 	/**
180 	 * Which distance to a location is considered the same as specified location. Note
181 	 * that UT units are rather small.
182 	 */
183 	public static final double AT_LOCATION_EPSILON = 120;
184 
185     /**
186      * What angle is considered to be maximum facing angle by default (in degrees).
187      */
188     public static final double IS_FACING_ANGLE = 6;
189     
190     /**
191      * EXACT at location (in UT units).
192      */
193 	public static final double CLOSE_ENOUGH_EPSILON = 50;
194 
195 	/**
196 	 * Retreives absolute location of the agent within the map.
197 	 *
198 	 * @return Location of the agent within the map.
199 	 *
200 	 * @see getDistance(Location)
201 	 * @see Location#getDistance(Location)
202 	 * @see Location#getDistanceL1(Location)
203 	 * @see Location#getDistanceLinf(Location)
204 	 * @see Location#getDistancePlane(Location)
205 	 * @see Location#getDistanceSquare(Location)
206 	 */
207 	public Location getLocation()
208 	{
209 		// retreive from self object
210                 if (self == null) return null;
211 		return self.getLocation();
212 	}
213 	
214 	/**
215 	 * Tells whether the bot is at navpoint/item/... (anything {@link ILocated}) of id 'objectId'. Note that IDs are case sensitive! UT2004 is usually using camel-case.
216 	 * @param objectId
217 	 * @return
218 	 */
219 	public boolean atLocation(String objectId) {
220 		Object obj = agent.getWorldView().get(UnrealId.get(objectId));
221 		if (obj == null) {
222 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("atLocation(): Object with id '" + objectId + "' does not exist in the worldview!"); 
223 			return false;
224 		}
225 		if (!(obj instanceof ILocated)) {
226 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("atLocation(): Object with id '" + objectId + "' is not implementing ILocated, it is " + obj.getClass().getSimpleName() + ".");
227 			return false;
228 		}
229 		return atLocation((ILocated)obj);
230 	}
231 	
232 	/**
233 	 * Tells whether the bot is at navpoint/item/... (anything {@link ILocated}) of id 'objectId'. Note that IDs are case sensitive! UT2004 is usually using camel-case.
234 	 * @param objectId
235 	 * @param epsilon
236 	 * @return
237 	 */
238 	public boolean atLocation(String objectId, double epsilon) {
239 		Object obj = agent.getWorldView().get(UnrealId.get(objectId));
240 		if (obj == null) {
241 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("atLocation(): Object with id '" + objectId + "' does not exist in the worldview!"); 
242 			return false;
243 		}
244 		if (!(obj instanceof ILocated)) {
245 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("atLocation(): Object with id '" + objectId + "' is not implementing ILocated, it is " + obj.getClass().getSimpleName() + ".");
246 			return false;
247 		}
248 		return atLocation((ILocated)obj, epsilon);
249 	}
250 	
251 	/**
252 	 * Returns whether the bot is at 'location'.
253 	 * <p><p>
254 	 * Synonym for {@link AgentInfo#atLocation(Location)}.
255 	 * 
256 	 * @return bot is at lcoation
257 	 */
258 	public boolean isAtLocation(ILocated location) {
259 		return atLocation(location);
260 	}
261 
262 	/**
263 	 * Returns whether the bot is at 'location', using {@link AgentInfo#AT_LOCATION_EPSILON}.
264 	 * 
265 	 * @return bot is at lcoation
266 	 */
267 	public boolean atLocation(ILocated location) {		
268 		return atLocation(location, AT_LOCATION_EPSILON);
269 	}
270 
271 	/**
272 	 * Returns whether the bot is at 'location', using 'epsilon' as a distance tolerance
273 	 * @param location
274 	 * @param epsilon
275 	 * @return bot is at lcoation with desired epsilon tolerance
276 	 */
277 	public boolean atLocation(ILocated location, double epsilon) {
278 		if (location == null || getLocation() == null) return false;
279 		return getLocation().getPoint3d().distance(location.getLocation().getPoint3d()) < epsilon;
280 	}
281 
282 	/**
283 	 * Computes crow-fly distance of the agent from given location.
284 	 *
285 	 * @param location Location within the map.
286 	 * @return Crow-fly distance of the agent and the location.
287 	 *
288 	 * @see getLocation()
289 	 */
290 	public Double getDistance(ILocated location)
291 	{
292 		// retreive from self object
293                 if (self == null) return null;
294                 if (location == null) return null;
295 		return self.getLocation().getDistance(location.getLocation());
296 	}
297 
298 	/*========================================================================*/
299 
300 	/**
301 	 * Retreives absolute rotation of the agent within the map.
302 	 *
303 	 * @return Rotation of the agent within the map.
304 	 */
305 	public Rotation getRotation()
306 	{
307 		// retreive from self object
308                 if (self == null) return null;
309 		return self.getRotation();
310 	}
311 	
312 	/**
313 	 * Retreives absolute rotation of the agent within the map.
314 	 *
315 	 * @return Rotation of the agent within the map.
316 	 */
317 	public Rotation getHorizontalRotation()
318 	{
319 		// retreive from self object
320         if (self == null) return null;
321         Rotation rot = new Rotation(self.getRotation());
322         rot.pitch = 0;
323         rot.roll = 0;
324 		return rot;
325 	}
326 
327 	/*========================================================================*/
328 
329 	/**
330 	 * Retreives current velocity of the agent as a vector of movement.
331 	 *
332 	 * @return Current velocity of the agent in the map.
333 	 *
334 	 * @see isMoving()
335 	 */
336 	public Velocity getVelocity()
337 	{
338 		// retreive from self object
339                 if (self == null) return null;
340 		return self.getVelocity();
341 	}
342 
343 	/**
344 	 * Tells, whether the agent is moving. The agent is moving, when his
345 	 * actual velocity is non-zero.
346 	 *
347 	 * @return True, if the agent is moving.
348 	 *
349 	 * @see getVelocity()
350 	 */
351 	public Boolean isMoving()
352 	{
353 		// check the size of the velocity
354                 if (getVelocity() == null) return null;
355 		return !getVelocity().isZero();
356 	}
357 
358 	/*========================================================================*/
359 
360 	/**
361 	 * Tells, whether the agent is crouched. When crouched, the height of the
362 	 * agent is smaller and thus harder to spot/hit.
363 	 *
364 	 * @return True, if the agent is crouched.
365 	 */
366 	public Boolean isCrouched()
367 	{
368 		// retreive from self object
369                 if (self == null) return null;
370 		return self.isCrouched();
371 	}
372 
373 	/**
374 	 * Tells, whether the agent is walking. When walking, the agent does not
375 	 * fall off the edges before notification about such edge can be sent to
376 	 * the agent. The agent's movement is, however, much slower.
377 	 *
378 	 * @return True, if the agent is walking.
379 	 */
380 	public Boolean isWalking()
381 	{
382 		// retreive from self object
383                 if (self == null) return null;
384 		return self.isWalking();
385 	}
386         /*========================================================================*/
387 
388 	/**
389 	 * Tells if the agent is currently facing input location.
390          *
391          * @param location input location.
392 	 * @return True, if the bot is facing input location.
393 	 */
394 	public Boolean isFacing(ILocated location)
395 	{
396                 if (location == null || getRotation() == null) return null;
397                 Location directionVector = location.getLocation().sub(this.getLocation()).getNormalized();
398                 Location agentFaceVector = this.getRotation().toLocation().getNormalized();
399 
400                 if (Math.acos(directionVector.dot(agentFaceVector)) <= Math.toRadians(IS_FACING_ANGLE))
401                     return true;
402 
403                 return false;
404 	}
405 
406 
407 	/**
408 	 * Tells if the agent is currently facing input location.
409          *
410          * @param location input location.
411          * @param angle specifies maximum angle (in degrees) that will be still considered as facing angle.
412 	 * @return True, if the angle between agent facing vector and input location is smaller or equal to input angle.
413 	 */
414 	public Boolean isFacing(ILocated location, double angle)
415 	{
416                 if (location == null || getRotation() == null) return null;
417                 Location directionVector = location.getLocation().sub(this.getLocation()).getNormalized();
418                 Location agentFaceVector = this.getRotation().toLocation().getNormalized();
419 
420                 if (Math.acos(directionVector.dot(agentFaceVector)) <= Math.toRadians(angle))
421                     return true;
422 
423                 return false;
424 	}
425 
426 	/*========================================================================*/
427 
428 	/**
429 	 * Retreives location of the nearest map geometry directly beneath the
430 	 * agent. This can be used to determine how far the agent is above the
431 	 * ground, etc.
432 	 *
433 	 * @return Location of <i>the ground</i> beneath the agent.
434 	 */
435 	public Location getFloorLocation()
436 	{
437                 if (self == null) return null;
438 		// retreive from self object
439 		return self.getFloorLocation();
440 	}
441 
442 	/**
443 	 * Tells, whether the agent is currently touching the groud with his feets.
444 	 * When not touching ground, the agent might be either jumping, or falling,
445 	 * or hanging from a ledge, or otherwise flying above the ground.
446 	 *
447 	 * @return True, if the agent is touching ground with his feets.
448 	 */
449 	public Boolean isTouchingGround()
450 	{
451 		// compare locations of agent and floor (beware of being crouched)
452 		// FIXME[jimmy]: Test the values..
453                 if (getLocation() == null || getFloorLocation() == null) return null;
454 		return (getLocation().z - getFloorLocation().z)
455 			   < (isCrouched() ? 50 : 80);
456 	}
457 
458 	/*========================================================================*/
459 
460 	/**
461 	 * Tells whether the agent has the damage multiplier (UDamage) bonus boost
462 	 * activated and how long will the UDamage boost remain active.
463 	 *
464 	 * <p>When UDamage is activated, the agent is  causing double (or tripple,
465 	 * or even more) damage to other players. The multiplying factor depends
466 	 * on game settings and mutators.
467 	 *
468 	 * @return Time remaining for UDamage bonus boost. When this value is
469 	 * positive, the agent has the UDamage bonus boost currently activated.
470 	 * When this value is negative, the agent does not have UDamage activated.
471 	 *
472 	 * @see hasUDamage()
473 	 */
474 	public Double getRemainingUDamageTime()
475 	{
476 		// calculate remaining time by substracting current time
477                 if (self == null) return null;
478 		return self.getUDamageTime() - getTime();
479 	}
480 
481 	/**
482 	 * Tells whether the agent has the damage multiplier (UDamage) bonus boost
483 	 * activated.
484 	 *
485 	 * <p>When UDamage is activated, the agent is  causing double (or tripple,
486 	 * or even more) damage to other players. The multiplying factor depends
487 	 * on game settings and mutators.
488 	 *
489 	 * @return True, if the agent has damage multiplier bonus action activated.
490 	 *
491 	 * @see getRemainingUDamageTime()
492 	 */
493 	public Boolean hasUDamage()
494 	{
495         if (getRemainingUDamageTime() == null) return null;
496 		// is there any remaining time?
497 		return getRemainingUDamageTime() > 0;
498 	}
499 
500 	/*========================================================================*/
501 
502 	/**
503 	 * Tells, whether the agent has special bonus action activated: the
504 	 * invisibility. When invisibility is activated, the agent is almost
505 	 * invisible to other players. When moving, outline of the agent is
506 	 * glittering a bit. When standing still, he is very hard to spot.
507 	 *
508 	 * <p>To learn, for how long the bonus action will remain activated, check
509 	 * the remaining amount of adrenaline. When level of adrenaline reaches
510 	 * zero, the bonus action is deactivated. See {@link getAdrenaline()}.
511 	 *
512 	 * @return True, if the agent has invisibility bonus action activated.
513 	 *
514 	 * @see getAdrenaline()
515 	 */
516 	public Boolean hasInvisibility()
517 	{
518 		// check with the self object
519                 if (self == null) return null;
520 		return self.getCombo().equals("xGame.ComboInvis");
521 	}
522 
523 	/**
524 	 * Tells, whether the agent has special bonus action activated: the
525 	 * fast firing rate. When fast firing rate is activated, the agent is
526 	 * firing his weapon at a faster rate, eating more ammo, but launching
527 	 * more projectiles into air.
528 	 *
529 	 * <p>To learn, for how long the bonus action will remain activated, check
530 	 * the remaining amount of adrenaline. When level of adrenaline reaches
531 	 * zero, the bonus action is deactivated. See {@link getAdrenaline()}.
532 	 *
533 	 * @return True, if the agent has fast firing rate bonus action activated.
534 	 *
535 	 * @see getAdrenaline()
536 	 */
537 	public Boolean hasFastFire()
538 	{
539 		// check with the self object
540                 if (self == null) return null;
541 		return self.getCombo().equals("xGame.ComboBerserk");
542 	}
543 
544 	/**
545 	 * Tells, whether the agent has special bonus action activated: the
546 	 * regenration, which is also called booster. When booster is activated,
547 	 * the agent regenerates health slowly. Note: The agent's health never
548 	 * rises above the maximum health level.
549 	 *
550 	 * <p>To learn, for how long the bonus action will remain activated, check
551 	 * the remaining amount of adrenaline. When level of adrenaline reaches
552 	 * zero, the bonus action is deactivated. See {@link getAdrenaline()}.
553 	 *
554 	 * @return True, if the agent has regenration bonus action activated.
555 	 *
556 	 * @see getAdrenaline()
557 	 */
558 	public Boolean hasRegeneration()
559 	{
560 		// check with the self object
561                 if (self == null) return null;
562 		return self.getCombo().equals("xGame.ComboDefensive");
563 	}
564 
565 	/**
566 	 * Tells, whether the agent has special bonus action activated: the
567 	 * speed. When speed is activated, the agent can move much faster than
568 	 * other players. Note: Firing rate does not change with speed.
569 	 *
570 	 * <p>To learn, for how long the bonus action will remain activated, check
571 	 * the remaining amount of adrenaline. When level of adrenaline reaches
572 	 * zero, the bonus action is deactivated. See {@link getAdrenaline()}.
573 	 *
574 	 * @return True, if the agent has speed bonus action activated.
575 	 *
576 	 * @see getAdrenaline()
577 	 */
578 	public Boolean hasSpeed()
579 	{
580 		// check with the self object
581                 if (self == null) return null;
582 		return self.getCombo().equals("xGame.ComboSpeed");
583 	}
584 
585 	/*========================================================================*/
586 
587 	/**
588 	 * Tells, how much health the agent has.
589 	 *
590 	 * <p>The health usually starts at 100, and ranges from 0 to 199. These
591 	 * values, however, can be changed by various mutators.
592 	 *
593 	 * @return Current health status.
594 	 *
595 	 * @see isHealthy()
596 	 * @see isSuperHealthy()
597 	 */
598 	public Integer getHealth()
599 	{
600 		// retreive from self object
601                 if (self == null) return null;
602 		return self.getHealth();
603 	}
604 
605 	/**
606 	 * Tells, whether the agent is healthy, i.e. not wounded.
607 	 *
608 	 * @return True, if the agent has at least standard amount of health.
609 	 *
610 	 * @see getHealth()
611 	 * @see isSuperHealthy()
612 	 */
613 	public Boolean isHealthy()
614 	{
615 		// compare self object and game info
616                 if (getHealth() == null || game == null) return null;
617 		return (getHealth() >= game.getFullHealth());
618 	}
619 
620 	/**
621 	 * Tells, whether the agent is healthy to the maximum boostable extent.
622 	 *
623 	 * @return True, if the agent has maximum amount of health.
624 	 *
625 	 * @see getHealth()
626 	 * @see isHealthy()
627 	 */
628 	public Boolean isSuperHealthy()
629 	{
630 		// compare self object and game info
631                 if (getHealth() == null || game == null) return null;
632 		return (getHealth() >= game.getMaxHealth());
633 	}
634 
635 	/*========================================================================*/
636 
637 	/**
638 	 * Tells, how much of combined armor the agent is wearing.
639 	 *
640 	 * <p>The combined armor usually starts at 0, and ranges from 0 to 150.
641 	 * These values, however, can be changed by various mutators.
642 	 *
643 	 * <p>Note: The armor consist of two parts, which are summed together into
644 	 * combined armor value. However, each part is powered-up by different item
645 	 * (low armor by <i>small shield</i>; high armor by <i>super-shield</i>).
646 	 *
647 	 * @return Current armor status.
648 	 *
649 	 * @see hasArmor()
650 	 * @see getLowArmor()
651 	 * @see getHighArmor()
652 	 */
653 	public Integer getArmor()
654 	{
655 		// retreive from self object
656                 if (self == null) return null;
657 		return self.getArmor();
658 	}
659 
660 	/**
661 	 * Tells, whether the agent is armored to the maximum extent.
662 	 *
663 	 * @return True, if the agent has maximum amount of armor.
664 	 *
665 	 * @see getArmor()
666 	 * @see hasLowArmor()
667 	 * @see hasHighArmor()
668 	 */
669 	public Boolean hasArmor()
670 	{
671 		// compare self object and game info
672                 if (getArmor() == null) return null;
673 		return (getArmor() >= game.getMaxArmor());
674 	}
675 
676 	/**
677 	 * Tells, how much of low armor the agent is wearing.
678 	 *
679 	 * <p>The low armor usually starts at 0, and ranges from 0 to 50.
680 	 * These values, however, can be changed by various mutators.
681 	 *
682 	 * <p>Note: The armor consist of two parts, which are summed together into
683 	 * combined armor value. However, each part is powered-up by different item
684 	 * (low armor by <i>small shield</i>; high armor by <i>super-shield</i>).
685 	 *
686 	 * @return Current low armor status.
687 	 *
688 	 * @see hasLowArmor()
689 	 * @see getArmor()
690 	 * @see getHighArmor()
691 	 */
692 	public Integer getLowArmor()
693 	{
694 		// retreive from self object
695                 if (self == null) return null;
696 		return self.getSmallArmor();
697 	}
698 
699 	/**
700 	 * Tells, whether the agent is armored to the maximum of low-armor extent.
701 	 *
702 	 * @return True, if the agent has maximum amount of low-armor.
703 	 *
704 	 * @see getLowArmor()
705 	 * @see hasArmor()
706 	 * @see hasHighArmor()
707 	 */
708 	public Boolean hasLowArmor()
709 	{
710 		// compare self object and game info
711                 if (getLowArmor() == null || game == null) return null;
712 		return (getLowArmor() >= game.getMaxLowArmor());
713 	}
714 
715 	/**
716 	 * Tells, how much of high armor the agent is wearing.
717 	 *
718 	 * <p>The high armor usually starts at 0, and ranges from 0 to 100.
719 	 * These values, however, can be changed by various mutators.
720 	 *
721 	 * <p>Note: The armor consist of two parts, which are summed together into
722 	 * combined armor value. However, each part is powered-up by different item
723 	 * (low armor by <i>small shield</i>; high armor by <i>super-shield</i>).
724 	 *
725 	 * @return Current high armor status.
726 	 *
727 	 * @see hasHighArmor()
728 	 * @see getArmor()
729 	 * @see getLowArmor()
730 	 */
731 	public Integer getHighArmor()
732 	{
733 		// calculate from armor and small armor in self object
734                 if (self == null) return null;
735 		return self.getArmor() - self.getSmallArmor();
736 	}
737 
738 	/**
739 	 * Tells, whether the agent is armored to the maximum of high-armor extent.
740 	 *
741 	 * @return True, if the agent has maximum amount of high-armor.
742 	 *
743 	 * @see getHighArmor()
744 	 * @see hasArmor()
745 	 * @see hasLowArmor()
746 	 */
747 	public Boolean hasHighArmor()
748 	{
749 		// compare self object and game info
750                 if (getHighArmor() == null) return null;
751 		return (getHighArmor() >= game.getMaxHighArmor());
752 	}
753 
754 	/*========================================================================*/
755 
756 	/**
757 	 * Tells, how much adrenaline the agent has.
758 	 *
759 	 * <p>Adrenaline can be gained through fulfilling various game tasks, such
760 	 * as killing opponents, capturing flags, controlling domination points,
761 	 * picking up adrenaline pills, etc. Note: More adrenaline is gained when
762 	 * the agent fulfill these tasks in combos (e.g. by scoring a double-kill
763 	 * the agent receives significantly more adrenaline than by scoring two
764 	 * single-kills).
765 	 *
766 	 * <p>Once the adrenaline reaches a designated level, it can be used to
767 	 * start special bonus actions like <i>booster</i>, <i>invisibility</i>,
768 	 * <i>speed</i>, etc. The adrenaline is then spent on the action. The more
769 	 * adrenaline the agent has, the longer the action lasts. Note: The agent
770 	 * may gain new adrenaline during the bonus action, which prolongs the
771 	 * action duration. See {@link isAdrenalineFull() } to determine, when the
772 	 * necessary adrenaline level is reached.
773 	 *
774 	 * <p>The adrenaline usually starts at 0, and ranges from 0 to 100. These
775 	 * values, however, can be changed by various mutators.
776 	 *
777 	 * @return Current armor status.
778 	 *
779 	 * @see isAdrenalineFull()
780 	 */
781 	public Integer getAdrenaline()
782 	{
783 		// retreive from self object
784                 if (self == null) return null;
785 		return self.getAdrenaline();
786 	}
787 
788 	/**
789 	 * Tells, whether the agent gained enough adrenaline to use it for special
790 	 * adrenaline-based action, e.g. <i>booster</i>, <i>invisibility</i>, etc.
791 	 *
792 	 * @return True, if the adrenaline level is high enough for bonus action.
793 	 *
794 	 * @see getAdrenaline()
795 	 */
796 	public Boolean isAdrenalineSufficient()
797 	{
798 		// compare self object and game info
799                 if (getAdrenaline() == null) return null;
800 		return (getAdrenaline() >= game.getTargetAdrenaline());
801 	}
802 
803 	/**
804 	 * Tells, whether the agent has full adrenaline.
805 	 *
806 	 * @return True, if the adrenaline level is at maximum.
807 	 *
808 	 * @see getAdrenaline()
809 	 */
810 	public Boolean isAdrenalineFull()
811 	{
812 		// compare self object and game info
813                 if (getAdrenaline() == null) return null;
814 		return (getAdrenaline() >= game.getMaxAdrenaline());
815 	}
816 
817 	/*========================================================================*/
818 
819 	/**
820 	 * Retreives UnrealId of the weapon the agent is currently holding. This
821 	 * UnrealId is a unique identifier of weapon from the agent's inventory.
822 	 * Note that this UnrealId is different from UnrealId of item the agent
823 	 * seen or picked up from the ground earlier.
824 	 *
825 	 * <p>The UnrealId might contains a substring, which identifies the type
826 	 * of the weapon. However, this is not guaranteed by definition. Therefore,
827 	 * you shoud use inventory to retreive the appropriate weapon object, to
828 	 * further retreive correct type of weapon.
829 	 *
830 	 * @return UnrealId of the weapon the agent is currently holding in hands.
831 	 *
832 	 * @see getCurrentAmmo()
833 	 * @see getCurrentSecondaryAmmo()
834 	 * @see Weaponry#getCurrentWeapon()
835 	 * @see Weaponry#getWeapon(UnrealId)
836 	 */
837 	public UnrealId getCurrentWeapon()
838 	{
839 		// retreive from self object
840         if (self == null) return null;
841 		return UnrealId.get(self.getWeapon());
842 	}
843 	
844 	/**
845 	 * Returns name of the currently wielded weapon (or null if no such weapon exists). 
846 	 * 
847 	 * @see getCurrentWeapon
848 	 * @return
849 	 */
850 	public String getCurrentWeaponName() {
851 		if (getCurrentWeapon() == null) return null;
852 		return self.getWeapon().substring(self.getWeapon().indexOf(".")+1);
853 	}
854 	
855 	/**
856 	 * Returns type of the weapon the agent is currently holding (or null if no such weapon exists).
857 	 * @return
858 	 */
859 	public ItemType getCurrentWeaponType() {
860 		if (self == null) return null;
861 		return ItemType.getItemType(getCurrentWeaponName());
862 	}
863 	
864 	/**
865 	 * Tells whether the bot is holding some weapon or not.
866 	 * <p><p>
867 	 * Note that {@link AgentInfo#getCurrentWeapon()} always returns some id. But there is a special id that marks 'no weapon'
868 	 * @return
869 	 */
870 	public Boolean hasWeapon() {
871                 if (self == null) return null;
872 		return !self.getWeapon().equalsIgnoreCase(NONE_WEAPON_ID);	
873 	}
874 
875 	/**
876 	 * Tells, how much ammunition the agent has left for the current weapon
877 	 * in its primary firing mode.
878 	 *
879 	 * @return Amount of ammunition for the primary firing mode.
880 	 *
881 	 * @see getCurrentSecondaryAmmo()
882 	 * @see Inventory#getCurrentPrimaryAmmo()
883 	 */
884 	public Integer getCurrentAmmo()
885 	{
886 		// retreive from self object
887                 if (self == null) return null;
888 		return self.getPrimaryAmmo();
889 	}
890 
891 	/**
892 	 * Tells, how much ammunition the agent has left for the current weapon
893 	 * in its alternate (secondary) firing mode. Note that many weapons use primary ammo
894 	 * for the alternate (secondary) firing mode as well. In such cases, the amount of
895 	 * ammo for primary mode is returned.
896 	 *
897 	 * @return Amount of ammunition for the secondary firing mode.
898 	 *
899 	 * @see getCurrentAmmo()
900 	 * @see Inventory#getCurrentSecondaryAmmo()
901 	 */
902 	public Integer getCurrentSecondaryAmmo()
903 	{
904 		// retreive from self object
905                 if (self == null) return null;
906 		return self.getSecondaryAmmo();
907 	}
908 
909 	/*========================================================================*/
910 
911 	/**
912 	 * Tells, whether the agent is shooting or not.
913 	 *
914 	 * <p>This method reports shooting with either primary or secondary fire
915 	 * mode. To distinguish between the fire modes, see {@link isPriShooting()},
916 	 * {@link isAltShooting()}.
917 	 *
918 	 * @return Returns true, if the agent is shooting his weapon.
919 	 *
920 	 * @see isPrimaryShooting()
921 	 * @see isSecondaryShooting()
922 	 */
923 	public Boolean isShooting()
924 	{
925 		// retreive from self object
926                 if (self == null) return null;
927 		return self.isShooting();
928 	}
929 
930 	/**
931 	 * Tells, whether the agent is shooting with primary fire mode.
932 	 *
933 	 * <p>This method reports shooting with primary fire mode only. See
934 	 * {@link isAltShooting()} method to determine, whether the agent shoots
935 	 * with alternate firing mode. See {@link isShooting()} to determine,
936 	 * whether the agent shoots with either primary or alternate firing mode.
937 	 *
938 	 * @return True, if the agent is shooting weapon in primary firing mode.
939 	 *
940 	 * @see isShooting()
941 	 * @see isSecondaryShooting()
942 	 */
943 	public Boolean isPrimaryShooting()
944 	{
945 		// shooting but not in altrenate fire mode
946                 if (self == null) return null;
947 		return isShooting() && !isSecondaryShooting();
948 	}
949 
950 	/**
951 	 * Tells, whether the agent is shooting with alternate (secondary) fire mode.
952 	 *
953 	 * <p>This method reports shooting with alternate (secondary) fire mode only. See
954 	 * {@link isPriShooting()} method to determine, whether the agent shoots
955 	 * with primary firing mode. See {@link isShooting()} to determine,
956 	 * whether the agent shoots with either primary or alternate (secondary) firing mode.
957 	 *
958 	 * @return True, if the agent is shooting his weapon in alternate (secondary) firing
959 	 * mode.
960 	 *
961 	 * @see isShooting()
962 	 * @see isPrimaryShooting()
963 	 */
964 	public Boolean isSecondaryShooting()
965 	{
966 		// retreive from self object
967         if (self == null) return null;
968 		return self.isAltFiring();
969 	}
970 
971 	/*========================================================================*/
972 
973 	/**
974 	 * Retreives number of kills the agent scored.
975 	 *
976 	 * <p>A kill is counted, whenever the agent kills an opponent.
977 	 *
978 	 * @return Number of kills the agent scored.
979 	 */
980 	public int getKills()
981 	{
982 		// returns number of kills, counted in PlayerKilledListener
983 		return kills;
984 	}
985 
986 	/**
987 	 * Retreives number of deaths the agent took.
988 	 *
989 	 * <p>A death is counted, whenever the agent dies.
990 	 * 
991 	 * <p><p>
992 	 * Note that if {@link Integer#MIN_VALUE} is returned, than the number of deaths is unknown. This happens only
993 	 * if you call this method before the first logic iteration of the agent (i.e., before first {@link PlayerScore} messages
994 	 * are exported by GameBots).
995 	 *
996 	 * @return Number of deaths the agent took.
997 	 */
998 	public int getDeaths()
999 	{
1000 		if (self == null) return Integer.MIN_VALUE;
1001 		// retreive from PlayerScore object
1002 		return game.getPlayerDeaths(getId());
1003 	}
1004 
1005 	/**
1006 	 * Retreives number of suicides the agent commited.
1007 	 *
1008 	 * <p>A suicide is counted, whenever the agent dies by his own weapon, or
1009 	 * by damaging himself by falling into pits, lava, acid, etc.
1010 	 *
1011 	 * <p>It can also be said that suicide is every agent's death, which could
1012 	 * not be credited to any other player in the map.
1013 	 *
1014 	 * <p>Each suicide is also counted as death. See {@link getDeaths()}.
1015 	 *
1016 	 * @return Number of suicides the agent commited.
1017 	 */
1018 	public int getSuicides()
1019 	{
1020 		// returns number of suicides, counted in BotKilledListener
1021 		return suicides;
1022 	}
1023 
1024 	/**
1025 	 * Retreives current agent score.
1026 	 *
1027 	 * <p>Agent score is usually rising by achieving some goals, e.g. killing
1028 	 * opponents, capturing flags, controlling domination points, etc. Note:
1029 	 * Agent score might decrease upon suicides, based on map, game type and
1030 	 * game settings.
1031 	 * 
1032 	 * <p><p>
1033 	 * Note that if {@link Integer#MIN_VALUE} is returned, than the score is unknown. This happens only
1034 	 * if you call this method before the first logic iteration of the agent (i.e., before first {@link PlayerScore} messages
1035 	 * are exported by GameBots).
1036 	 *
1037 	 * @return Current agent score.
1038 	 */
1039 	public int getScore()
1040 	{
1041 		if (self == null) return Integer.MIN_VALUE;
1042 		return game.getPlayerScore(getId());
1043 	}
1044 
1045 	/**
1046 	 * Retreives current agent's team score.
1047 	 *
1048 	 * <p>Agent's team score is usually rising by achieving team goals, e.g.
1049 	 * killing opponents, capturing flags, controlling domination points, etc.
1050 	 * Note: Agent's team score might decrease, when oposing teams score points
1051 	 * themselves, based on map, game type and game settings.
1052 	 *
1053 	 * @return Current agent's team score.
1054 	 */
1055 	public int getTeamScore()
1056 	{
1057 		// retreive from TeamScore object
1058 		return game.getTeamScore(getTeam());
1059 	}
1060 
1061 	/*========================================================================*/
1062 
1063 	/**
1064 	 * Pulling velocity in this map zone. Such pulling velocity effectively
1065 	 * draws the player towards a specific direction or even lifts him upwards.
1066 	 *
1067 	 * @return Pulling velocity in this zone.
1068 	 */
1069 	public Velocity getCurrentZoneVelocity()
1070 	{
1071 		// retreive from VolumeChanged object
1072                 if (lastVolumeChanged == null) return null;
1073 		return lastVolumeChanged.getZoneVelocity();
1074 	}
1075 
1076 	/**
1077 	 * Gravity in this map zone. Gravity might differ throughout different
1078 	 * parts of the map. The gravity is expressed as a velocity vector. This
1079 	 * vector is used an acceleration. The fall speed may ramp up, to as much
1080 	 * as {@link getFallSpeed()}.
1081 	 *
1082 	 * @return Gravity in this zone.
1083 	 */
1084 	public Velocity getCurrentZoneGravity()
1085 	{
1086 		// retreive from VolumeChanged object
1087                 if (lastVolumeChanged == null) return null;
1088 		return lastVolumeChanged.getZoneGravity();
1089 	}
1090 
1091 	/**
1092 	 * Friction of the floor in this map volume. Friction of the floor works
1093 	 * towards movement, slowing down the acceleration and speed of the agent
1094 	 * in any direction.
1095 	 *
1096 	 * @return Friction of the floor.
1097 	 */
1098 	public Double getCurrentVolumeGroundFriction()
1099 	{
1100 		// retreive from VolumeChanged object
1101                 if (lastVolumeChanged == null) return null;
1102 		return lastVolumeChanged.getGroundFriction();
1103 	}
1104 
1105 	/**
1106 	 * Friction of the fluid in this map volume. Friction of the fluid works
1107 	 * towards movement, slowing down the acceleration and speed of the agent
1108 	 * in any direction.
1109 	 *
1110 	 * @return Friction of the fluid.
1111 	 */
1112 	public Double getCurrentVolumeFluidFriction()
1113 	{
1114 		// retreive from VolumeChanged object
1115                 if (lastVolumeChanged == null) return null;
1116 		return lastVolumeChanged.getFluidFriction();
1117 	}
1118 
1119 	/**
1120 	 * FIXME[js]: What the hell is this good for?
1121 	 *
1122 	 * @return TerminalVelocity of the CurrentVolume.
1123 	 */
1124 	public Double _getCurrentVolumeTerminalVelocity()
1125 	{
1126 		// retreive from VolumeChanged object
1127                 if (lastVolumeChanged == null) return null;
1128 		return lastVolumeChanged.getTerminalVelocity();
1129 	}
1130 
1131 	/**
1132 	 * Tells, whether the current volume is water. When the agent is in water,
1133 	 * {@link getCurrentVolumeFluidFriction()} and {@link getWaterSpeed()} can
1134 	 * help to determine changes to movement and speed of the agent. Also note
1135 	 * that {@link getCurrentZoneVelocity()}, {@link getCurrentZoneGravity()},
1136 	 * and others may change (and usually does) in water.
1137 	 *
1138 	 * @return True, if the current volume is water.
1139 	 */
1140 	public Boolean isCurrentVolumeWater()
1141 	{
1142 		// retreive from VolumeChanged object
1143                 if (lastVolumeChanged == null) return null;
1144 		return lastVolumeChanged.isWaterVolume();
1145 	}
1146 
1147 	/**
1148 	 * Tells, whether the current volume is causing damage. Such damage is
1149 	 * applied to the agent's health every second. The amount of damage taken
1150 	 * per each second spent in this volume can be determined by
1151 	 * {@link getCurrentVolumeDamagePerSec()}. When the volume damages the
1152 	 * agent to the death, the death is counted as a suicide.
1153 	 *
1154 	 * @return True, if the current volume is causing damage.
1155 	 *
1156 	 * @see isCurrentVolumeDestructive()
1157 	 * @see getCurrentVolumeDamagePerSec()
1158 	 */
1159 	public Boolean isCurrentVolumePainCausing()
1160 	{
1161 		// retreive from VolumeChanged object
1162                 if (lastVolumeChanged == null) return null;
1163 		return lastVolumeChanged.isPainCausing();
1164 	}
1165 
1166 	/**
1167 	 * Amount of damage taken for spending time in the current volume. Such
1168 	 * damage is applied to the agent's health every second. When the volume
1169 	 * damages the agent to the death, the death is counted as a suicide.
1170 	 *
1171 	 * @return Amount of damage taken for spending time in the current volume.
1172 	 *
1173 	 * @see isCurrentVolumePainCausing()
1174 	 */
1175 	public Double getCurrentVolumeDamagePerSec()
1176 	{
1177 		// retreive from VolumeChanged object
1178                 if (lastVolumeChanged == null) return null;
1179 		return lastVolumeChanged.getDamagePerSec();
1180 	}
1181 
1182 	/**
1183 	 * Tells, whether the current volume kills the actors (almost) instantly.
1184 	 * Death in such destructive volume is counted as a suicide.
1185 	 *
1186 	 * @return True, if the current volume kills (almost) instantly.
1187 	 *
1188 	 * @see isCurrentVolumePainCausing()
1189 	 */
1190 	public Boolean isCurrentVolumeDestructive()
1191 	{
1192 		// retreive from VolumeChanged object
1193                 if (lastVolumeChanged == null) return null;
1194 		return lastVolumeChanged.isDestructive();
1195 	}
1196 
1197 	/**
1198 	 * Retreives type of damage the current volume inflicts to the agent while
1199 	 * he spends time in this volume.
1200 	 *
1201 	 * <p>FIXME[js]: Is is possible to provide an enum here?
1202 	 *
1203 	 * @return Type of the damage the current volume inflicts to the agent.
1204 	 */
1205 	public String getCurrentVolumeDamageType()
1206 	{
1207 		// retreive from VolumeChanged object
1208                 if (lastVolumeChanged == null) return null;
1209 		return lastVolumeChanged.getDamageType();
1210 	}
1211 
1212 	/**
1213 	 * Tells, whether the current volume (the one the agent is within) forbids
1214 	 * usage of the inventory. If so, no weapons or items can be used, changed,
1215 	 * or picked up.
1216 	 *
1217 	 * @return True, if the current volume forbids usage of the inventory.
1218 	 */
1219 	public Boolean isCurrentVolumeBanningInventory()
1220 	{
1221 		// retreive from VolumeChanged object
1222                 if (lastVolumeChanged == null) return null;
1223 		return lastVolumeChanged.isNoInventory();
1224 	}
1225 
1226 	/**
1227 	 * Tells, whether the current volume imparts its velocity to projectiles.
1228 	 * E.g. A volume might impart velocity to players to emulate <i>wind</i>.
1229 	 * This settings tells, whether the same applies to projectiles. If so,
1230 	 * Their trajectory will be affected by this volume velocity.
1231 	 *
1232 	 * @return True, if the current volume imparts its velocity to projectiles.
1233 	 */
1234 	public Boolean isCurrentVolumeAffectingProjectiles()
1235 	{
1236 		// retreive from VolumeChanged object
1237                 if (lastVolumeChanged == null) return null;
1238 		return lastVolumeChanged.isMoveProjectiles();
1239 	}
1240 
1241 	/**
1242 	 * Tells, whether the current zone is a neutral zone. In neutral zone,
1243 	 * players can't take damage.
1244 	 *
1245 	 * @return True, if the current zone is a neutral zone.
1246 	 */
1247 	public Boolean isCurrentZoneNeutral()
1248 	{
1249 		// retreive from VolumeChanged object
1250                 if (lastVolumeChanged == null) return null;
1251 		return lastVolumeChanged.isNeutralZone();
1252 	}
1253 
1254 	/*========================================================================*/
1255 
1256 	/**
1257 	 * Retreives scaling factor for damage dealt by the agent. All damage
1258 	 * dealt by the agent is reduced (or increased) by this value.
1259 	 *
1260 	 * @return Scaling factor for damage dealt by the agent.
1261 	 */
1262 	public Double getDamageScaling()
1263 	{
1264 		// retreive from InitedMessage object
1265                 if (lastInitedMessage == null) return null;
1266 		return lastInitedMessage.getDamageScaling();
1267 	}
1268 
1269 	/*========================================================================*/
1270 
1271 	/**
1272 	 * Retreives maximum base speed of the agent.
1273 	 *
1274 	 * @return Maximum base speed of the agent.
1275 	 */
1276 	public Double getBaseSpeed()
1277 	{
1278 		// retreive from InitedMessage object
1279                 if (lastInitedMessage == null) return null;
1280 		return lastInitedMessage.getGroundSpeed();
1281 	}
1282 
1283 	/**
1284 	 * Retreives maximum speed of the agent while moving in the air.
1285 	 *
1286 	 * @return Maximum speed of the agent while moving in the air.
1287 	 */
1288 	public Double getAirSpeed()
1289 	{
1290 		// retreive from InitedMessage object
1291                 if (lastInitedMessage == null) return null;
1292 		return lastInitedMessage.getAirSpeed();
1293 	}
1294 
1295 	/**
1296 	 * Retreives maximum speed of the agent while moving on a ladder.
1297 	 *
1298 	 * @return Maximum speed of the agent while moving on a ladder.
1299 	 */
1300 	public Double getLadderSpeed()
1301 	{
1302 		// retreive from InitedMessage object
1303                 if (lastInitedMessage == null) return null;
1304 		return lastInitedMessage.getLadderSpeed();
1305 	}
1306 
1307 	/**
1308 	 * Retreives maximum speed of the agent while moving in water.
1309 	 *
1310 	 * @return Maximum speed of the agent while moving in water.
1311 	 */
1312 	public Double getWaterSpeed()
1313 	{
1314 		// retreive from InitedMessage object
1315                 if (lastInitedMessage == null) return null;
1316 		return lastInitedMessage.getWaterSpeed();
1317 	}
1318 
1319 	/**
1320 	 * Retreives maximum speed of the agent while falling.
1321 	 *
1322 	 * @return Maximum speed of the agent while falling.
1323 	 */
1324 	public Double getFallSpeed()
1325 	{
1326 		// retreive from InitedMessage object
1327                 if (lastInitedMessage == null) return null;
1328 		return lastInitedMessage.getMaxFallSpeed();
1329 	}
1330 
1331 	/**
1332 	 * Retreives maximum speed of the agent while using dodge.
1333 	 *
1334 	 * <p>FIXME[js]: Check about the name depending on the meaning/value.
1335 	 *
1336 	 * @return Maximum speed of the agent while using dodge.
1337 	 */
1338 	public Double getDodgeSpeedFactor()
1339 	{
1340 		// retreive from InitedMessage object
1341                 if (lastInitedMessage == null) return null;
1342 		return lastInitedMessage.getDodgeSpeedFactor();
1343 	}
1344 
1345 	/**
1346 	 * Retreives acceleration rate of the agent.
1347 	 *
1348 	 * @return Acceleration rate of the agent.
1349 	 */
1350 	public Double getAccelerationRate()
1351 	{
1352 		// retreive from InitedMessage object
1353                 if (lastInitedMessage == null) return null;
1354 		return lastInitedMessage.getAccelRate();
1355 	}
1356 
1357 	/**
1358 	 * Retreives agent's control of movement while in the air.
1359 	 * This value ranges from 0 (none) to 1 (full control).
1360 	 *
1361 	 * @return Agent's control of movement while in the air.
1362 	 */
1363 	public Double getAirControl()
1364 	{
1365 		// retreive from InitedMessage object
1366                 if (lastInitedMessage == null) return null;
1367 		return lastInitedMessage.getAirControl();
1368 	}
1369 
1370 	/**
1371 	 * Retreives boost of the agent in the Z axis while jumping.
1372 	 *
1373 	 * @return Jumping boost of the agent in the Z axis.
1374 	 */
1375 	public Double getJumpZBoost()
1376 	{
1377 		// retreive from InitedMessage object
1378                 if (lastInitedMessage == null) return null;
1379 		return lastInitedMessage.getJumpZ();
1380 	}
1381 
1382 	/**
1383 	 * Retreives boost of the agent in the Z axis while using dodge.
1384 	 *
1385 	 * @return Dodge boost of the agent in the Z axis.
1386 	 */
1387 	public Double getDodgeZBoost()
1388 	{
1389 		// retreive from InitedMessage object
1390                 if (lastInitedMessage == null) return null;
1391 		return lastInitedMessage.getDodgeSpeedZ();
1392 	}
1393 
1394 	/*========================================================================*/
1395 
1396 	/**
1397 	 * Retreives current game time, since the game started.
1398 	 *
1399 	 * @return Current game timestamp. IN SECONDS!
1400 	 */
1401 	public double getTime()
1402 	{
1403                 if (game == null) return 0;
1404 		return game.getTime();
1405 	}
1406 
1407 	/*========================================================================*/
1408 	
1409 	/**
1410 	 * Retrieves the configuration of the bot inside UT2004.
1411 	 * @return Configuration of the bot.
1412 	 */
1413 	public ConfigChange getConfig() {
1414 		return lastConfig;
1415 	}
1416 	
1417 	/*========================================================================*/
1418 	
1419 	
1420 	/**
1421 	 * Retrieves nearest known navpoint to current agent location.
1422 	 * <p><p>
1423 	 * WARNING: O(n) complexity.
1424 	 * 
1425 	 * @return nearest navpoint
1426 	 */
1427 	public NavPoint getNearestNavPoint() {
1428 		if (getLocation() == null) return null;
1429 		return DistanceUtils.getNearest(agent.getWorldView().getAll(NavPoint.class).values(), getLocation());
1430 	}
1431 	
1432 	/**
1433 	 * Retrieve nearest known navpoint to some location.
1434 	 * @param location
1435 	 * @return
1436 	 */
1437 	public NavPoint getNearestNavPoint(ILocated location) {
1438 		if (location == null) return null;
1439     	if (location instanceof NavPoint) return (NavPoint)location;
1440     	if (location instanceof Item) {
1441     		if (((Item)location).getNavPoint() != null) return ((Item)location).getNavPoint();
1442     	}
1443     	return DistanceUtils.getNearest(agent.getWorldView().getAll(NavPoint.class).values(), location);        
1444     }
1445 	
1446 	/**
1447 	 * Check whether the bot is on navigation graph, more precisly, near some navigation point.
1448 	 * <p><p>
1449 	 * WARNING: O(n) complexity.
1450 	 * 
1451 	 * @return
1452 	 */
1453 	public boolean isOnNavGraph() {
1454 		return self.getLocation().getDistance(getNearestNavPoint().getLocation()) < CLOSE_ENOUGH_EPSILON;
1455 	}
1456 	
1457 	/**
1458 	 * Retrieves nearest visible navpoint to current agent location.
1459 	 * <p><p>
1460 	 * WARNING: O(n) complexity.
1461 	 * 
1462 	 * @return nearest visible navpoint
1463 	 */
1464 	public NavPoint getNearestVisibleNavPoint() {
1465                 if (getLocation() == null) return null;
1466 		return DistanceUtils.getNearestVisible(agent.getWorldView().getAll(NavPoint.class).values(), getLocation());
1467 	}
1468 	
1469 	/**
1470 	 * Retrieves nearest known item to current agent location.
1471 	 * <p><p>
1472 	 * WARNING: O(n) complexity.
1473 	 * 
1474 	 * @return nearest item
1475 	 */
1476 	public Item getNearestItem() {
1477                 if (getLocation() == null) return null;
1478 		return DistanceUtils.getNearest(agent.getWorldView().getAll(Item.class).values(), getLocation());
1479 	}
1480 	
1481 	/**
1482 	 * Retrieves nearest visible item to current agent location.
1483 	 * <p><p>
1484 	 * WARNING: O(n) complexity.
1485 	 * 
1486 	 * @return nearest visible item
1487 	 */
1488 	public Item getNearestVisibleItem() {
1489                 if (getLocation() == null) return null;
1490 		return DistanceUtils.getNearestVisible(agent.getWorldView().getAll(Item.class).values(), getLocation());
1491 	}
1492 	
1493 	/**
1494 	 * Retrieves nearest known player to current agent location.
1495 	 * <p><p>
1496 	 * WARNING: O(n) complexity.
1497 	 * 
1498 	 * @return nearest player
1499 	 */
1500 	public Player getNearestPlayer() {
1501                 if (getLocation() == null) return null;
1502 		return DistanceUtils.getNearest(agent.getWorldView().getAll(Player.class).values(), getLocation());
1503 	}
1504 	
1505 	/**
1506 	 * Retrieves nearest visible player to current agent location.
1507 	 * <p><p>
1508 	 * WARNING: O(n) complexity.
1509 	 * 
1510 	 * @return nearest visible player
1511 	 */
1512 	public Player getNearestVisiblePlayer() {
1513         if (getLocation() == null) return null;
1514 		return DistanceUtils.getNearestVisible(agent.getWorldView().getAll(Player.class).values(), getLocation());
1515 	}
1516 	
1517 	/*========================================================================*/
1518 	
1519 	/** Most rescent message containing info about the agent. */
1520 	Self self = null;
1521 	
1522 	/** How many suicides the bot commited. */
1523 	int suicides = 0;
1524 	
1525 	/** How many player we have killed so far. */
1526 	int kills = 0;
1527 	
1528 	/** Most rescent message containing info about the game frame. */
1529 	InitedMessage lastInitedMessage = null;
1530 
1531 	/** Most rescent message containing info about the volume the player is in. */
1532 	VolumeChanged lastVolumeChanged = null;
1533 	
1534 	/** Configuration of the bot inside UT2004. */
1535 	ConfigChange lastConfig = null;
1536 
1537 	/*========================================================================*/
1538 	
1539 	/**
1540 	 * {@link BotKilled} listener counting the number of suicides.
1541 	 */
1542 	private class BotKilledListener implements IWorldEventListener<BotKilled> {
1543 
1544 		public BotKilledListener(IWorldView worldView) {
1545 			worldView.addEventListener(BotKilled.class, this);
1546 		}
1547 		
1548 		@Override
1549 		public void notify(BotKilled event) {
1550 			if (self == null) return;
1551 			// TODO: [jimmy] is it correct?
1552 			if (event.getKiller() == null || event.getKiller().equals(getId())) {
1553 				++suicides;
1554 			}
1555 		}
1556 		
1557 	}
1558 	
1559 	/** {@link BotKilled} listener. */
1560 	BotKilledListener botKilledListener;
1561 	
1562 	/*========================================================================*/
1563 	
1564 	private class PlayerKilledListener implements IWorldEventListener<PlayerKilled> {
1565 
1566 		public PlayerKilledListener(IWorldView worldView) {
1567 			worldView.addEventListener(PlayerKilled.class, this);
1568 		}
1569 		
1570 		@Override
1571 		public void notify(PlayerKilled event) {
1572 			if (self == null) return;
1573 			if (event.getKiller() != null && event.getKiller().equals(getId())) {
1574 				++kills;
1575 			}		
1576 		}
1577 		
1578 	}
1579 	
1580 	/** {@link PlayerKilled} listener. */
1581 	PlayerKilledListener playerKilledListener;
1582 	
1583 	/*========================================================================*/
1584 
1585 	/**
1586 	 * InitedMessage listener.
1587 	 */
1588 	private class InitedMessageListener implements IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>>
1589 	{
1590 		@Override
1591 		public void notify(WorldObjectUpdatedEvent<InitedMessage> event)
1592 		{
1593 			lastInitedMessage = event.getObject();
1594 		}
1595 
1596 		/**
1597 		 * Constructor. Registers itself on the given WorldView object.
1598 		 * @param worldView WorldView object to listent to.
1599 		 */
1600 		public InitedMessageListener(IWorldView worldView)
1601 		{
1602 			worldView.addObjectListener(InitedMessage.class, WorldObjectUpdatedEvent.class, this);
1603 		}
1604 	}
1605 
1606 	/** InitedMessage listener */
1607 	private InitedMessageListener initedMessageListener;
1608 
1609 	/*========================================================================*/
1610 
1611 	/**
1612 	 * VolumeChanged listener.
1613 	 */
1614 	private class VolumeChangedListener implements IWorldEventListener<VolumeChanged>
1615 	{
1616 		@Override
1617 		public void notify(VolumeChanged event)
1618 		{
1619 			lastVolumeChanged = event;
1620 		}
1621 
1622 		/**
1623 		 * Constructor. Registers itself on the given WorldView object.
1624 		 * @param worldView WorldView object to listent to.
1625 		 */
1626 		public VolumeChangedListener(IWorldView worldView)
1627 		{
1628 			worldView.addEventListener(VolumeChanged.class, this);
1629 		}
1630 	}
1631 
1632 	/** VolumeChanged listener */
1633 	private VolumeChangedListener volumeChangedListener;
1634 
1635 	/*========================================================================*/
1636 
1637 	/**
1638 	 * {@link Self} listener.
1639 	 */
1640 	private class SelfListener implements IWorldObjectEventListener<Self, WorldObjectUpdatedEvent<Self>>
1641 	{
1642 		private IWorldView worldView;
1643 
1644 		/**
1645 		 * Constructor. Registers itself on the given WorldView object.
1646 		 * @param worldView WorldView object to listent to.
1647 		 */
1648 		public SelfListener(IWorldView worldView)
1649 		{
1650 			this.worldView = worldView;
1651 			worldView.addObjectListener(Self.class, WorldObjectUpdatedEvent.class, this);
1652 		}
1653 
1654 		@Override
1655 		public void notify(WorldObjectUpdatedEvent<Self> event) {
1656 			self = event.getObject();			
1657 		}
1658 	}
1659 
1660 	/** {@link Self} listener */
1661 	private SelfListener selfListener;
1662 
1663 
1664 	/*========================================================================*/
1665 	
1666 	/**
1667 	 * {@link ConfigChange} listener.
1668 	 */
1669 	private class ConfigChangeListener implements IWorldObjectEventListener<ConfigChange, IWorldObjectEvent<ConfigChange>>
1670 	{
1671 		private IWorldView worldView;
1672 
1673 		/**
1674 		 * Constructor. Registers itself on the given WorldView object.
1675 		 * @param worldView WorldView object to listent to.
1676 		 */
1677 		public ConfigChangeListener(IWorldView worldView)
1678 		{
1679 			worldView.addObjectListener(ConfigChange.class, WorldObjectUpdatedEvent.class, this);
1680 			this.worldView = worldView;
1681 		}
1682 
1683 		@Override
1684 		public void notify(IWorldObjectEvent<ConfigChange> event) {
1685 			lastConfig = event.getObject();			
1686 		}
1687 	}
1688 
1689 	/** {@link ConfigChange} listener */
1690 	private ConfigChangeListener configChangeListener;
1691 
1692 
1693 	/*========================================================================*/
1694 
1695 	public Self getSelf() {
1696 		return self;
1697 	}
1698 
1699 	/** Game memory module. */
1700 	public Game game;
1701 	
1702 	/**
1703 	 * Constructor. Setups the memory module based on bot's world view.
1704 	 * @param game game info module
1705 	 */
1706 	public AgentInfo(UT2004Bot bot) {
1707 		this(bot, new Game(bot), null);
1708 	}
1709 	
1710 	/**
1711 	 * Constructor. Setups the memory module based on bot's world view.
1712 	 * @param bot owner of the module
1713 	 * @param game game info module
1714 	 */
1715 	public AgentInfo(UT2004Bot bot, Game game) {
1716 		this(bot, game, null);
1717 	}
1718 
1719 	/**
1720 	 * Constructor. Setups the memory module based on bot's world view.
1721 	 * @param bot owner of the module
1722 	 * @param game game info module
1723 	 * @param log Logger to be used for logging runtime/debug info. Note: If <i>null</i> is provided,
1724 	 * this memory module creates it's own logger.
1725 	 */
1726 	public AgentInfo(UT2004Bot bot, Game game, Logger log)
1727 	{
1728 		super(bot, log);
1729 
1730 		this.game = game;
1731 		NullCheck.check(this.game, "game");
1732 		
1733 		// create listeners
1734 		selfListener          = new SelfListener(worldView);
1735 		initedMessageListener = new InitedMessageListener(worldView);	
1736 		volumeChangedListener = new VolumeChangedListener(worldView);
1737 		botKilledListener     = new BotKilledListener(worldView);
1738 		playerKilledListener  = new PlayerKilledListener(worldView);
1739 		configChangeListener  = new ConfigChangeListener(worldView);
1740 		
1741 		cleanUp();
1742 	}
1743 	
1744 	@Override
1745 	protected void cleanUp() {
1746 		super.cleanUp();
1747 		self = null;
1748 		suicides = 0;
1749 		kills = 0;
1750 		lastInitedMessage = null;
1751 		lastVolumeChanged = null;
1752 		lastConfig = null;
1753 	}
1754 
1755 }