View Javadoc

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