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