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