View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.agent.module.sensor;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   import java.util.logging.Logger;
6   
7   import javax.vecmath.Vector3d;
8   
9   import cz.cuni.amis.pogamut.base.agent.module.SensorModule;
10  import cz.cuni.amis.pogamut.base.communication.messages.InfoMessage;
11  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
12  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
13  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
14  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
15  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
16  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
17  import cz.cuni.amis.pogamut.base3d.worldview.object.Rotation;
18  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
19  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
20  import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
21  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.AdrenalineGained;
22  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BeginMessage;
23  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotDamaged;
24  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotKilled;
25  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Bumped;
26  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.FallEdge;
27  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.HearNoise;
28  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.HearPickup;
29  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.IncomingProjectile;
30  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ItemPickedUp;
31  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
32  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerDamaged;
33  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerKilled;
34  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.WallCollision;
35  import cz.cuni.amis.utils.NullCheck;
36  
37  /**
38   * Memory module specialized on agent's senses.
39   * <p><p>
40   * <b>THIS MODULE IS BUGGY AS HELL! You can expect its source code to see what classes / functions it uses, BUT PLEASE, DO NOT USE IT!</b>
41   * <p><p>
42   * This module hooks up a LOT OF LISTENERS and provide you with lot of methods to query current state of bot's sensors.
43   * <p><p>
44   * There are two types of methods:
45   * <ol>
46   * <li>general - providing sensors 500ms back to the history</li>
47   * <li>once - providing sensors 500ms back to the history + queriable only once (first call may return true, next will report false until another sense arrive),
48   *     this allows you to create simple if-then rules that won't fire twice.</li>
49   * </ol>
50   * <p><p>
51   * If you are missing some methods that you think should be incorporated to the module, 
52   * post to <a href=http://diana.ms.mff.cuni.cz/main/tiki-forums.php">Pogamut 3 forum</a> and
53   * we may then discuss the implementation.
54   * 
55   * <p><p>
56   * It is designed to be initialized inside {@link IUT2004BotController#prepareBot(UT2004Bot)} method call
57   * and may be used since {@link IUT2004BotController#botInitialized(cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo, cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ConfigChange, cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.InitedMessage)}
58   * is called.
59   *
60   * @author Jimmy
61   */
62  public class Senses extends SensorModule<UT2004Bot>
63  {
64  	
65  	/**
66  	 * Specifies amount of time after which the sense (event) is considered to be invalid/old/discarded.
67  	 */
68  	public static final double SENSE_THRESHOLD = 0.5;
69  	
70  	/**
71  	 * Tells, whether the agent is colliding with map geometry.
72  	 *
73  	 * <p><b>Note: This method clears the collision flag upon invocation.</b>
74  	 * This is to prevent taking more action because of one collision.
75  	 *
76  	 * @return True, if the agent is colliding with map geometry.
77  	 */
78  	public boolean isCollidingOnce()
79  	{
80  		if (!lastWallCollisionFlag) return false;
81  		// TODO: [jimmy] what about the constant? is it OK?
82  		boolean col = agentInfo.getLocation().getDistance(lastWallCollision.getLocation()) < 100;
83  		lastWallCollisionFlag = false;
84  		return col;
85  	}
86  	
87  	/**
88  	 * Tells, whether the agent is colliding with map geometry.
89  	 *
90  	 * @return True, if the agent is colliding with map geometry.
91  	 */
92  	public boolean isColliding()
93  	{
94  		if (lastWallCollision == null) return false;
95  		// TODO: [jimmy] what about the constant? is it OK?
96  		return agentInfo.getTime() - lastWallCollisionTime < SENSE_THRESHOLD && 
97  		       agentInfo.getLocation().getDistance(lastWallCollision.getLocation()) < 100;
98  	}
99  	
100 	/**
101 	 * Tells where the agent has collided.
102 	 * @return location of the last collision
103 	 */
104 	public Location getCollisionLocation() {
105 		if (lastWallCollision == null) return null;
106 		return lastWallCollision.getLocation();
107 	}
108 
109 	/**
110 	 * Tells the normal of last agent's collision.
111 	 * @return normal vector of the triangle the bot has collided with
112 	 */
113 	public Vector3d getCollisionNormal() {
114 		if (lastWallCollision == null) return null;
115 		return lastWallCollision.getNormal();
116 	}
117 
118 	/*========================================================================*/
119 
120 	/**
121 	 * Tells whether the bot is bumping another player/other map geometry.
122 	 * @return True, if the bot bumped another player/other map geometry.
123 	 */
124 	public boolean isBumping ()
125 	{
126 		if (lastBumped == null) return false;
127 		return agentInfo.getTime() - lastBumpedTime < SENSE_THRESHOLD && agentInfo.getLocation().getDistance(lastBumped.getLocation()) < 100;
128 	}
129 	
130 	/**
131 	 * Tells, whether the agent is bumping with another player/other map geometry.
132 	 *
133 	 * <p><b>Note: This method clears the bumping flag upon invocation.</b>
134 	 * This is to prevent taking more action because of one bumping.
135 	 *
136 	 * @return True, if the agent is bumping another player/other map geometry.
137 	 */
138 	public boolean isBumpingOnce() {
139 		if (!lastBumpedFlag) return false;
140 		boolean result = isBumping();
141 		lastBumpedFlag = false;
142 		return result;
143 	}
144 	
145 	/**
146 	 * Tells whether the bot is bumping another player (bot or human).
147 	 */
148 	public boolean isBumpingPlayer() {
149 		if (!isBumping()) return false;
150 		return players.getPlayer(lastBumped.getId()) != null;
151 	}
152 	
153 	/**
154 	 * Tells whether the bot is bumping another player (bot or human).
155 	 * 
156 	 * <p><b>Note: This method clears the bumping flag upon invocation.</b>
157 	 * This is to prevent taking more action because of one collision.
158 	 * 
159 	 * @return True, if the agent is bumping to a player
160 	 */
161 	public boolean isBumpingPlayerOnce() {
162 		if (!lastBumpedFlag) return false;
163 		boolean result = isBumpingPlayer();
164 		lastBumpedFlag = false;
165 		return result;
166 	}
167 	
168 	/**
169 	 * Returns tha {@link Player} object of the player the bot has bumped into (if it was a bot).
170 	 * @return player the bot has bumped into (if it bumped the player)
171 	 */
172 	public Player getBumpingPlayer() {
173 		if (lastBumped == null) return null;
174 		return players.getPlayer(lastBumped.getId());
175 	}
176 	
177 	/**
178 	 * Returns location where bumping occurred.
179 	 * @return location where the bumping has occurred
180 	 */
181 	public Location getBumpLocation() {
182 		if (lastBumped == null) return null;
183 		return lastBumped.getLocation();
184 	}
185 
186 	/*========================================================================*/
187 
188 	/**
189 	 * Tells whether the bot has just fall of the ledge
190 	 * @return whether the bot has just fall of the ledge
191 	 */
192 	public boolean isFallEdge()
193 	{
194 		if (lastFallEdge == null) return false;
195 		// TODO: [jimmy] is the constant 100 ok?
196 		return agentInfo.getTime() - lastFallEdgeTime < SENSE_THRESHOLD && agentInfo.getLocation().getDistance(lastFallEdge.getLocation()) < 100;
197 	}
198 	
199 	/**
200 	 * Tells whether the bot has just fall of the ledge
201 	 * 
202 	 * <p><b>Note: This method clears the fall-edge flag upon invocation.</b>
203 	 * This is to prevent taking more action because of one fall.
204 	 * 
205 	 * @return whether the bot has just fall of the ledge
206 	 */
207 	public boolean isFallEdgeOnce()
208 	{
209 		if (!lastFallEdgeFlag) return false;
210 		boolean result = isFallEdge();
211 		lastFallEdgeFlag = false;
212 		return result;
213 	}
214 
215 	/*========================================================================*/
216 
217 	/**
218 	 * Tells whether the bot is hearing noise.
219 	 * @return True, if the bot is hearing noise.
220 	 */
221 	public boolean isHearingNoise()
222 	{
223 		if (lastHearNoise == null) return false;
224 		return agentInfo.getTime() - lastHearNoiseTime < SENSE_THRESHOLD;
225 	}
226 	
227 	/**
228 	 * Tells whether the bot is hearing noise.
229 	 * 
230 	 * <p><b>Note: This method clears the hearing-noise flag upon invocation.</b>
231 	 * This is to prevent taking more action because of hearing the noise.
232 	 * 
233 	 * @return True, if the bot is hearing noise.
234 	 */
235 	public boolean isHearingNoiseOnce() {
236 		if (!lastHearNoiseFlag) return false;
237 		boolean result = isHearingNoise();
238 		lastHearNoiseFlag = false;
239 		return result;
240 	}
241 	
242 	/**
243 	 * Tells where the noise is coming from.
244 	 * @return way where the noise has happened  
245 	 */
246 	public Rotation getNoiseRotation() {
247 		if (lastHearNoise == null) return null;
248 		return lastHearNoise.getRotation();
249 	}
250 	
251 	/**
252 	 * Tells what has caused a noise (may be null).
253 	 * @return what has caused a noise
254 	 */
255 	public UnrealId getNoiseSource() {
256 		if (lastHearNoise == null) return null;
257 		return lastHearNoise.getSource();
258 	}
259 	
260 	/**
261 	 * Tells what type the noise is.
262 	 * @return noise type
263 	 */
264 	public String getNoiseType() {
265 		// TODO: is possible enum?
266 		if (lastHearNoise == null) return null;
267 		return lastHearNoise.getType();
268 	}
269 
270 	/*========================================================================*/
271 
272 	/**
273 	 * Tells whether the bot is hearing pickup.
274 	 * @return True, if the bot is hearing pickup.
275 	 */
276 	public boolean isHearingPickup()
277 	{
278 		if (lastHearPickup == null) return false;
279 		return agentInfo.getTime() - lastHearPickupTime < SENSE_THRESHOLD;
280 	}
281 	
282 	/**
283 	 * Tells whether the bot is hearing pickup.
284 	 * 
285 	 * <p><b>Note: This method clears the hearing-pickup flag upon invocation.</b>
286 	 * This is to prevent taking more action because of hearing the pickup.
287 	 * 
288 	 * @return True, if the bot is hearing pickup.
289 	 */
290 	public boolean isHearingPickupOnce() {
291 		if (!lastHearPickupFlag) return false;
292 		boolean result = isHearingPickup();
293 		lastHearPickupFlag = false;
294 		return result;
295 	}
296 	
297 	/**
298 	 * Tells where the pickup noise is coming from.
299 	 * @return way where the noise has happened  
300 	 */
301 	public Rotation getPickupNoiseRotation() {
302 		if (lastHearPickup == null) return null;
303 		return lastHearPickup.getRotation();
304 	}
305 	
306 	/**
307 	 * Tells what has caused the pickup noise (may be null).
308 	 * @return what has caused a noise
309 	 */
310 	public UnrealId getPickupNoiseSource() {
311 		if (lastHearPickup == null) return null;
312 		return lastHearPickup.getSource();
313 	}
314 	
315 	/**
316 	 * Tells what type the pickup noise is.
317 	 * @return noise type
318 	 */
319 	public String getPickupNoiseType() {
320 		// TODO: is possible enum?
321 		if (lastHearPickup == null) return null;
322 		return lastHearPickup.getType();
323 	}
324 
325 	/*========================================================================*/
326 
327 	/**
328 	 * Tells, whether the agent is being damaged.
329 	 *
330 	 * @return True, if the agent is being damaged.
331 	 */
332 	public boolean isBeingDamaged ()
333 	{
334 		if (lastBotDamaged == null) return false;
335 		return agentInfo.getTime() - lastBotDamagedTime < SENSE_THRESHOLD; 
336 	}
337 	
338 	/**
339 	 * Tells, whether the agent is being damaged.
340 	 * 
341 	 * <p><b>Note: This method clears the being-damaged flag upon invocation.</b>
342 	 * This is to prevent taking more action because of taking the damage.
343 	 *
344 	 * @return True, if the agent is being damaged.
345 	 */
346 	public boolean isBeingDamagedOnce ()
347 	{
348 		if (!isBeingDamaged()) return false;
349 		if (!lastBotDamagedFlag) return false;
350 		lastBotDamagedFlag = false;
351 		return true; 
352 	}
353 	
354 	/**
355 	 * Returns the description of the last damage done to the bot.
356 	 * @return
357 	 */
358 	public BotDamaged getLastDamage() {
359 		return lastBotDamaged;
360 	}
361 	
362 	/*========================================================================*/
363 	
364 	/**
365 	 * Tells, whether the agent is being damaged by another player (i.e. was shot).
366 	 *
367 	 * @return True, if the agent is being damaged.
368 	 */
369 	public boolean isShot() {
370 		if (lastBotShot == null) return false;
371 		return agentInfo.getTime() - lastBotShotTime < SENSE_THRESHOLD;
372 	}
373 	
374 	/**
375 	 * Tells, whether the agent is being damaged by another player (i.e. was shot).
376 	 * 
377 	 * <p><b>Note: This method clears the shot flag upon invocation.</b>
378 	 * This is to prevent taking more action because of taking the damage.
379 	 *
380 	 * @return True, if the agent is being damaged.
381 	 */
382 	public boolean isShotOnce() {
383 		if (!isShot()) return false;
384 		if (!lastBotShotFlag) return false;
385 		lastBotShotFlag = false;
386 		return true; 
387 	}
388 	
389 	/**
390 	 * Returns the description of the last shot that has hit the bot.
391 	 * @return last shot the bot has taken
392 	 */
393 	public BotDamaged getLastShot() {
394 		return lastBotShot;
395 	}
396 	
397 	// TODO: we probably need to provide better handling of different BotDamaged events (i.e., when you are hurt
398 	//       by acid + being shot by two different bots you see)
399 
400 	/*========================================================================*/
401 
402 	/**
403 	 * Tells whether the bot see any incoming projectiles.
404 	 * @return whether the bot see any incoming projectile
405 	 */
406 	public boolean seeIncomingProjectile ()
407 	{
408 		if (lastIncomingProjectile == null) return false;
409 		return agentInfo.getTime() - lastIncomingProjectileTime < SENSE_THRESHOLD;
410 	}
411 	
412 	/**
413 	 * Tells whether the bot see any incoming projectiles.
414 	 * @return whether the bot see any incoming projectile
415 	 */
416 	public boolean seeIncomingProjectileOnce() {
417 		if (!seeIncomingProjectile()) return false;
418 		if (!lastIncomingProjectileFlag) return false;
419 		lastIncomingProjectileFlag = false;
420 		return true;
421 	}
422 	
423 	/**
424 	 * Provides access to the last incoming-projectile object.
425 	 * @return incoming projectile object
426 	 */
427 	public IncomingProjectile getLastIncomingProjectile() {
428 		return lastIncomingProjectile;
429 	}
430 	
431 	//TODO: more advanced methods such as "give me a direction to run to to avoid the missile"
432 	//      or "time to impact"
433 	//TODO: proper handling of multiple incoming projectiles is needed
434 
435 	/*========================================================================*/
436 
437 	/**
438 	 * Tells, whether the agent is causing any damage.
439 	 *
440 	 * @return True, if the agent is causing any damage.
441 	 */
442 	public boolean isCausingDamage ()
443 	{
444 		if (lastPlayerDamaged == null) return false;
445 		return agentInfo.getTime() - lastPlayerDamagedTime < SENSE_THRESHOLD; 
446 	}
447 	
448 	/**
449 	 * Tells, whether the agent is causing any damage.
450 	 * 
451 	 * <p><b>Note: This method clears the causing-damage flag upon invocation.</b>
452 	 * This is to prevent taking more action because of causing the damage.
453 	 *
454 	 * @return True, if the agent is being damaged.
455 	 */
456 	public boolean isCausingDamageOnce ()
457 	{
458 		if (!isCausingDamage()) return false;
459 		if (!lastPlayerDamagedFlag) return false;
460 		lastPlayerDamagedFlag = false;
461 		return true; 
462 	}
463 	
464 	/**
465 	 * Returns the description of the last damage caused by the bot.
466 	 * @return
467 	 */
468 	public PlayerDamaged getLastCausedDamage() {
469 		return lastPlayerDamaged;
470 	}
471 	
472 	/*========================================================================*/
473 	
474 	/**
475 	 * Tells, whether the agent hit another player (i.e.. shot it).
476 	 *
477 	 * @return True, if the agent shot another player.
478 	 */
479 	public boolean isHitPlayer() {
480 		if (lastPlayerShot == null) return false;
481 		return agentInfo.getTime() - lastPlayerShotTime < SENSE_THRESHOLD;
482 	}
483 	
484 	/**
485 	 * Tells, whether the agent hit another player (i.e.. shot it).
486 	 * 
487 	 * <p><b>Note: This method clears the player-hit flag upon invocation.</b>
488 	 * This is to prevent taking more action because of hitting a player.
489 	 *
490 	 * @return True, if the agent hit another player.
491 	 */
492 	public boolean isHitPlayerOnce() {
493 		if (!isHitPlayer()) return false;
494 		if (!lastPlayerShotFlag) return false;
495 		lastPlayerShotFlag = false;
496 		return true; 
497 	}
498 	
499 	/**
500 	 * Returns the description of the last hit of another player.
501 	 * @return last hit the bot has dealt
502 	 */
503 	public PlayerDamaged getLastHitPlayer() {
504 		return lastPlayerShot;
505 	}
506 	
507 	// TODO: we probably need to provide better handling of different PlayerDamaged events 
508 	//       preferably categorizing them according to Ids of different players
509 
510 	/*========================================================================*/
511 	
512 	/**
513 	 * Tells whether some other player has just died (we do not care which one).
514 	 * @return True, if some other player has just died.
515 	 */
516 	public boolean isPlayerKilled() {
517 		for (UnrealId id : playerKilled.keySet()) {
518 			if (isPlayerKilled(id)) return true;
519 		}
520 		return false;
521 	}
522 	
523 	/**
524 	 * Tells whether some other player has just died (we do not care which one).
525 	 * 
526 	 * <p><b>Note: This method clears the arbitrary-player-killed flag upon invocation.</b>
527 	 * This is to prevent taking more action because of killing a player.
528 	 * 
529 	 * @return True, if some other player has just died.
530 	 */
531 	public boolean isPlayerKilledOnce() {
532 		if (!playerKilledGlobalFlag) return false;
533 		if (!isPlayerKilled()) return false;
534 		playerKilledGlobalFlag = false;
535 		return true;
536 	}
537 	
538 	/**
539 	 * Tells whether some other player of id 'playerId' has just died.
540 	 * @return True, the player of id 'playerId' has just died.
541 	 */
542 	public boolean isPlayerKilled(UnrealId playerId) {
543 		return playerKilled.get(playerId) != null && agentInfo.getTime() - playerKilled.get(playerId).time < SENSE_THRESHOLD;
544 	}
545 	
546 	/**
547 	 * Tells whether some other player has just died.
548 	 * @return True, the player  has just died.
549 	 */
550 	public boolean isPlayerKilled(Player player) {
551 		return isPlayerKilled(player.getId());
552 	}
553 	
554 	/**
555 	 * Tells whether some other player of id 'playerId' has just died.
556 	 * 
557 	 * <p><b>Note: This method clears the specific-player-killed flag upon invocation.</b>
558 	 * This is to prevent taking more action because of killing a player.
559 	 * 
560 	 * @param playerId
561 	 * @return True, the player of id 'playerId' has just died.
562 	 */
563 	public boolean isPlayerKilledOnce(UnrealId playerId) {
564 		if (!isPlayerKilled(playerId)) return false;
565 		if (playerKilled.get(playerId).queried) return false;
566 		playerKilled.get(playerId).queried = true;
567 		return true;
568 	}
569 	
570 	/**
571 	 * Tells whether some other player has just died.
572 	 * 
573 	 * <p><b>Note: This method clears the specific-player-killed flag upon invocation.</b>
574 	 * This is to prevent taking more action because of killing a player.
575 	 * 
576 	 * @param player
577 	 * @return True, the player  has just died.
578 	 */
579 	public boolean isPlayerKilledOnce(Player player) {
580 		return isPlayerKilledOnce(player.getId());
581 	}
582 	
583 	/**
584 	 * Returns detail information about the way the player of id 'playerId' has died.
585 	 * @param playerId
586 	 * @return detail info about player's death
587 	 */
588 	public PlayerKilled getPlayerKilled(UnrealId playerId) {
589 		if (playerKilled.get(playerId) == null) return null;
590 		return playerKilled.get(playerId).event;
591 	}
592 	
593 	/**
594 	 * Returns detail information about the way the player has died.
595 	 * @param player
596 	 * @return detail info about player's death
597 	 */
598 	public PlayerKilled getPlayerKilled(Player player) {
599 		return getPlayerKilled(player.getId());
600 	}
601 	
602 	/*========================================================================*/
603 	
604 	/**
605 	 * Tells whether the bot has recently got an adrenaline.
606 	 * @return whether the bot has recently got any adrenaline
607 	 */
608 	public boolean isAdrenalineGained() {
609 		return lastAdrenalineGained != null && agentInfo.getTime() - lastAdrenalineGainedTime < SENSE_THRESHOLD;
610 	}
611 	
612 	/**
613 	 * Tells whether the bot has recently got an adrenaline.
614 	 * 
615 	 * <p><b>Note: This method clears the adrenaline-gained flag upon invocation.</b>
616 	 * This is to prevent taking more action because of adrenaline gain.
617 	 * 
618 	 * @return whether the bot has recently got any adrenaline
619 	 */
620 	public boolean isAdrenalineGainedOnce() {
621 		if (!lastAdrenalineGainedFlag) return false;
622 		if (!isAdrenalineGained()) return false;
623 		lastAdrenalineGainedFlag = false;
624 		return true;
625 	}
626 	
627 	/*========================================================================*/
628 	
629 	/**
630 	 * Tells whether this bot has recently died.
631 	 * @return whether the bot has recently died.
632 	 */
633 	public boolean hasDied() {
634 		return lastBotKilled != null && agentInfo.getTime() - lastBotKilledTime < SENSE_THRESHOLD;
635 	}
636 	
637 	/**
638 	 * Tells whether this bot has recently died.
639 	 * 
640 	 * <p><b>Note: This method clears the bot-died flag upon invocation.</b>
641 	 * This is to prevent taking more action because of bot death.
642 	 * 
643 	 * @return whether the bot has recently got any adrenaline
644 	 */
645 	public boolean hasDiedOnce() {
646 		if (!lastBotKilledFlag) return false;
647 		if (!hasDied()) return false;
648 		lastBotKilledFlag = false;
649 		return true;
650 	}
651 	
652 	/**
653 	 * Provides information about the way this bot has died.
654 	 * @return info about last bot death
655 	 */
656 	public BotKilled getBotDeath() {
657 		return lastBotKilled;
658 	}
659 	
660 	/*========================================================================*/
661 	
662 	/**
663 	 * Tells whether this bot has picked up some item recently.
664 	 * @return whether the has picked up an item recently
665 	 */
666 	public boolean isItemPickedUp() {
667 		return lastItemPickedUp != null && agentInfo.getTime() - lastItemPickedUpTime < SENSE_THRESHOLD;
668 	}
669 	
670 	/**
671 	 * Tells whether this bot has picked up some item recently.
672 	 * 
673 	 * <p><b>Note: This method clears the pick-up flag upon invocation.</b>
674 	 * This is to prevent taking more action because of item pickup.
675 	 * 
676 	 * @return whether the bot has recently got any adrenaline
677 	 */
678 	public boolean isItemPickedUpOnce() {
679 		if (!lastItemPickedUpFlag) return false;
680 		if (!isItemPickedUp()) return false;
681 		lastItemPickedUpFlag = false;
682 		return true;
683 	}
684 	
685 	/**
686 	 * Provides information about the last item the bot has picked up.
687 	 * @return last picked item
688 	 */
689 	public ItemPickedUp getItemPickedUp() {
690 		return lastItemPickedUp;
691 	}
692 	
693 	/**
694 	 * Returns UT2004 time delta. Note that first logic tick it returns NULL as it does not have enough info to work with.
695 	 * @return
696 	 */
697 	public Double getTimeDelta() {
698 		if (previousBeginMessage == null) return null;
699 		return lastBeginMessage.getTime() - previousBeginMessage.getTime();
700 	}
701 	
702 	/*========================================================================*/
703 	
704 	Bumped lastBumped = null;
705 	double lastBumpedTime = -1;
706 	boolean lastBumpedFlag = false;
707 	
708 	WallCollision lastWallCollision = null;
709 	double lastWallCollisionTime = -1;
710 	boolean lastWallCollisionFlag = false;
711 	
712 	FallEdge lastFallEdge = null;
713 	double lastFallEdgeTime = -1;
714 	boolean lastFallEdgeFlag = false;
715 	
716 	HearNoise lastHearNoise = null;
717 	double lastHearNoiseTime = -1;
718 	boolean lastHearNoiseFlag = false;
719 
720 	HearPickup lastHearPickup = null;
721 	double lastHearPickupTime = -1;
722 	boolean lastHearPickupFlag = false;
723 
724 	BotDamaged lastBotDamaged = null;
725 	double lastBotDamagedTime = -1;
726 	boolean lastBotDamagedFlag = false;
727 	
728 	BotDamaged lastBotShot = null;
729 	double lastBotShotTime = -1;
730 	boolean lastBotShotFlag = false;
731 	
732 	IncomingProjectile lastIncomingProjectile = null;
733 	double lastIncomingProjectileTime = -1;
734 	boolean lastIncomingProjectileFlag = false;
735 	
736 	PlayerDamaged lastPlayerDamaged = null;
737 	double lastPlayerDamagedTime = -1;
738 	boolean lastPlayerDamagedFlag = false;
739 	
740 	PlayerDamaged lastPlayerShot = null;
741 	double lastPlayerShotTime = -1;
742 	boolean lastPlayerShotFlag = false;
743 	
744 	ItemPickedUp lastItemPickedUp = null;
745 	double lastItemPickedUpTime = -1;
746 	boolean lastItemPickedUpFlag = false;	
747 	
748 	private BeginMessage previousBeginMessage = null;
749 	private BeginMessage lastBeginMessage = null;
750 	
751 	private class Entry<EVENT extends InfoMessage> {
752 		
753 		private EVENT event;
754 		private boolean queried;
755 		private double time;
756 
757 		public Entry(EVENT event) {
758 			this.event = event;
759 			this.queried = false;
760 			this.time = agentInfo.getTime();
761 		}
762 		
763 	}
764 	
765 	private Map<UnrealId, Entry<PlayerKilled>> playerKilled = new HashMap<UnrealId, Entry<PlayerKilled>>();
766 	private boolean playerKilledGlobalFlag = false;
767 	
768 	AdrenalineGained lastAdrenalineGained = null;
769 	double lastAdrenalineGainedTime = -1;
770 	boolean lastAdrenalineGainedFlag = false;
771 	
772 	BotKilled lastBotKilled = null;
773 	double lastBotKilledTime = -1;
774 	boolean lastBotKilledFlag = false;
775 	
776 	
777 	/*========================================================================*/
778 
779 	/**
780 	 * Bumped listener.
781 	 */
782 	private class BumpedListener implements IWorldEventListener<Bumped>
783 	{
784 		@Override
785 		public void notify(Bumped event)
786 		{
787 			lastBumped = event;
788 			lastBumpedTime = agentInfo.getTime();
789 			lastBumpedFlag = true;
790 		}
791 
792 		/**
793 		 * Constructor. Registers itself on the given WorldView object.
794 		 * @param worldView WorldView object to listent to.
795 		 */
796 		public BumpedListener(IWorldView worldView)
797 		{
798 			worldView.addEventListener(Bumped.class, this);
799 		}
800 	}
801 
802 	/** Bumped listener */
803 	BumpedListener bumpedListener;
804 	
805 	/*========================================================================*/
806 
807 	/**
808 	 * {@link WallCollision} listener.
809 	 */
810 	private class WallCollisionListener implements IWorldEventListener<WallCollision>
811 	{
812 		@Override
813 		public void notify(WallCollision event)
814 		{
815 			lastWallCollision = event;
816 			lastWallCollisionTime = agentInfo.getTime();
817 			lastWallCollisionFlag = true;
818 		}
819 
820 		/**
821 		 * Constructor. Registers itself on the given WorldView object.
822 		 * @param worldView WorldView object to listen to.
823 		 */
824 		public WallCollisionListener(IWorldView worldView)
825 		{
826 			worldView.addEventListener(WallCollision.class, this);
827 		}
828 	}
829 
830 	/** {@link WallCollision} listener */
831 	WallCollisionListener wallCollisitonListener;
832 
833 	/*========================================================================*/
834 	
835 	/**
836 	 * {@link FallEdge} listener.
837 	 */
838 	private class FallEdgeListener implements IWorldEventListener<FallEdge>
839 	{
840 		@Override
841 		public void notify(FallEdge event)
842 		{
843 			lastFallEdge = event;
844 			lastFallEdgeTime = agentInfo.getTime();
845 			lastFallEdgeFlag = true;
846 		}
847 
848 		/**
849 		 * Constructor. Registers itself on the given WorldView object.
850 		 * @param worldView WorldView object to listen to.
851 		 */
852 		public FallEdgeListener(IWorldView worldView)
853 		{
854 			worldView.addEventListener(FallEdge.class, this);
855 		}
856 	}
857 
858 	/** {@link FallEdge} listener */
859 	FallEdgeListener fallEdgeListener;
860 
861 	/*========================================================================*/
862 	
863 	/**
864 	 * {@link HearNoise} listener.
865 	 */
866 	private class HearNoiseListener implements IWorldEventListener<HearNoise>
867 	{
868 		@Override
869 		public void notify(HearNoise event)
870 		{
871 			lastHearNoise = event;
872 			lastHearNoiseTime = agentInfo.getTime();
873 			lastHearNoiseFlag = true;
874 		}
875 
876 		/**
877 		 * Constructor. Registers itself on the given WorldView object.
878 		 * @param worldView WorldView object to listen to.
879 		 */
880 		public HearNoiseListener(IWorldView worldView)
881 		{
882 			worldView.addEventListener(HearNoise.class, this);
883 		}
884 	}
885 
886 	/** {@link HearNoise} listener */
887 	HearNoiseListener hearNoiseListener;
888 
889 	/*========================================================================*/
890 	
891 	/**
892 	 * {@link HearPickup} listener.
893 	 */
894 	private class HearPickupListener implements IWorldEventListener<HearPickup>
895 	{
896 		@Override
897 		public void notify(HearPickup event)
898 		{
899 			lastHearPickup = event;
900 			lastHearPickupTime = agentInfo.getTime();
901 			lastHearPickupFlag = true;
902 		}
903 
904 		/**
905 		 * Constructor. Registers itself on the given WorldView object.
906 		 * @param worldView WorldView object to listen to.
907 		 */
908 		public HearPickupListener(IWorldView worldView)
909 		{
910 			worldView.addEventListener(HearPickup.class, this);
911 		}
912 	}
913 
914 	/** {@link HearPickup} listener */
915 	HearPickupListener hearPickupListener;
916 
917 	/*========================================================================*/
918 	
919 	/**
920 	 * {@link BotDamaged} listener.
921 	 */
922 	private class BotDamagedListener implements IWorldEventListener<BotDamaged>
923 	{
924 		@Override
925 		public void notify(BotDamaged event)
926 		{
927 			lastBotDamaged = event;
928 			lastBotDamagedTime = agentInfo.getTime();
929 			lastBotDamagedFlag = true;
930 			
931 			if (lastBotDamaged.isBulletHit()) {
932 				lastBotShot = event;
933 				lastBotShotTime = agentInfo.getTime();
934 				lastBotShotFlag = true;
935 			}
936 		}
937 
938 		/**
939 		 * Constructor. Registers itself on the given WorldView object.
940 		 * @param worldView WorldView object to listen to.
941 		 */
942 		public BotDamagedListener(IWorldView worldView)
943 		{
944 			worldView.addEventListener(BotDamaged.class, this);
945 		}
946 	}
947 
948 	/** {@link BotDamaged} listener */
949 	BotDamagedListener botDamagedListener;
950 
951 	/*========================================================================*/
952 	
953 	/**
954 	 * {@link IncomingProjectile} listener.
955 	 */
956 	private class IncomingProjectileListener implements IWorldObjectEventListener<IncomingProjectile, IWorldObjectEvent<IncomingProjectile>>
957 	{
958 		@Override
959 		public void notify(IWorldObjectEvent<IncomingProjectile> event)
960 		{
961 			lastIncomingProjectile = event.getObject();
962 			lastIncomingProjectileTime = agentInfo.getTime();
963 			lastIncomingProjectileFlag = true;			
964 		}
965 
966 		/**
967 		 * Constructor. Registers itself on the given WorldView object.
968 		 * @param worldView WorldView object to listen to.
969 		 */
970 		public IncomingProjectileListener(IWorldView worldView)
971 		{
972 			worldView.addObjectListener(IncomingProjectile.class, WorldObjectUpdatedEvent.class, this);
973 		}
974 	}
975 
976 	/** {@link IncomingProjectile} listener */
977 	IncomingProjectileListener incomingProjectileListener;
978 
979 	/*========================================================================*/
980 	
981 	/**
982 	 * {@link PlayerDamaged} listener.
983 	 */
984 	private class PlayerDamagedListener implements IWorldEventListener<PlayerDamaged>
985 	{
986 		@Override
987 		public void notify(PlayerDamaged event)
988 		{
989 			lastPlayerDamaged = event;
990 			lastPlayerDamagedTime = agentInfo.getTime();
991 			lastPlayerDamagedFlag = true;
992 			
993 			if (lastPlayerDamaged.isBulletHit()) {
994 				lastPlayerShot = event;
995 				lastPlayerShotTime = agentInfo.getTime();
996 				lastPlayerShotFlag = true;
997 			}
998 		}
999 
1000 		/**
1001 		 * Constructor. Registers itself on the given WorldView object.
1002 		 * @param worldView WorldView object to listen to.
1003 		 */
1004 		public PlayerDamagedListener(IWorldView worldView)
1005 		{
1006 			worldView.addEventListener(PlayerDamaged.class, this);
1007 		}
1008 	}
1009 
1010 	/** {@link PlayerDamaged} listener */
1011 	PlayerDamagedListener playerDamagedListener;
1012 	
1013 	/*========================================================================*/
1014 
1015 	/**
1016 	 * {@link PlayerKilled} listener.
1017 	 */
1018 	private class PlayerKilledListener implements IWorldEventListener<PlayerKilled>
1019 	{
1020 		@Override
1021 		public void notify(PlayerKilled event)
1022 		{
1023 			if (event.getId() == null) return;
1024 			playerKilled.put(event.getId(), new Entry<PlayerKilled>(event));
1025 			playerKilledGlobalFlag = true;
1026 		}
1027 
1028 		/**
1029 		 * Constructor. Registers itself on the given WorldView object.
1030 		 * @param worldView WorldView object to listen to.
1031 		 */
1032 		public PlayerKilledListener(IWorldView worldView)
1033 		{
1034 			worldView.addEventListener(PlayerKilled.class, this);
1035 		}
1036 	}
1037 
1038 	/** {@link PlayerKilled} listener */
1039 	PlayerKilledListener playerKilledListener;
1040 	
1041 	/*========================================================================*/
1042 	
1043 	/**
1044 	 * {@link AdrenalineGained} listener.
1045 	 */
1046 	private class AdrenalineGainedListener implements IWorldEventListener<AdrenalineGained>
1047 	{
1048 		@Override
1049 		public void notify(AdrenalineGained event)
1050 		{
1051 			lastAdrenalineGained = event;
1052 			lastAdrenalineGainedFlag = true;
1053 			lastAdrenalineGainedTime = agentInfo.getTime();
1054 		}
1055 
1056 		/**
1057 		 * Constructor. Registers itself on the given WorldView object.
1058 		 * @param worldView WorldView object to listen to.
1059 		 */
1060 		public AdrenalineGainedListener(IWorldView worldView)
1061 		{
1062 			worldView.addEventListener(AdrenalineGained.class, this);
1063 		}
1064 	}
1065 
1066 	/** {@link AdrenalineGained} listener */
1067 	AdrenalineGainedListener adrenalineGainedListener;
1068 	
1069 	/*========================================================================*/
1070 	
1071 	/**
1072 	 * {@link BotKilled} listener.
1073 	 */
1074 	private class BotKilledListener implements IWorldEventListener<BotKilled>
1075 	{
1076 		@Override
1077 		public void notify(BotKilled event)
1078 		{
1079 			lastBotKilled = event;
1080 			lastBotKilledFlag = true;
1081 			lastBotKilledTime = agentInfo.getTime();
1082 		}
1083 
1084 		/**
1085 		 * Constructor. Registers itself on the given WorldView object.
1086 		 * @param worldView WorldView object to listen to.
1087 		 */
1088 		public BotKilledListener(IWorldView worldView)
1089 		{
1090 			worldView.addEventListener(BotKilled.class, this);
1091 		}
1092 	}
1093 
1094 	/** {@link BotKilled} listener */
1095 	BotKilledListener botKilledListener;
1096 	
1097 	/*========================================================================*/
1098 	
1099 	/**
1100 	 * {@link BeginMessage} listener.
1101 	 */
1102 	private class BeginMessageListener implements IWorldEventListener<BeginMessage>
1103 	{
1104 		@Override
1105 		public void notify(BeginMessage event)
1106 		{
1107 			previousBeginMessage = lastBeginMessage;
1108 			lastBeginMessage = event;
1109 		}
1110 
1111 		/**
1112 		 * Constructor. Registers itself on the given WorldView object.
1113 		 * @param worldView WorldView object to listen to.
1114 		 */
1115 		public BeginMessageListener(IWorldView worldView)
1116 		{
1117 			worldView.addEventListener(BeginMessage.class, this);
1118 		}
1119 	}
1120 	
1121 	/** {@link BeginMessage} listener */
1122 	BeginMessageListener beginMessageListener;
1123 	
1124 	/*========================================================================*/
1125 
1126 	
1127 	/**
1128 	 * {@link ItemPickedUp} listener.
1129 	 */
1130 	private class ItemPickedUpListener implements IWorldEventListener<ItemPickedUp>
1131 	{
1132 		@Override
1133 		public void notify(ItemPickedUp event)
1134 		{
1135 			lastItemPickedUp = event;
1136 			lastItemPickedUpFlag = true;
1137 			lastItemPickedUpTime = agentInfo.getTime();
1138 		}
1139 
1140 		/**
1141 		 * Constructor. Registers itself on the given WorldView object.
1142 		 * @param worldView WorldView object to listen to.
1143 		 */
1144 		public ItemPickedUpListener(IWorldView worldView)
1145 		{
1146 			worldView.addEventListener(ItemPickedUp.class, this);
1147 		}
1148 	}
1149 
1150 	/** {@link ItemPickedUp} listener */
1151 	ItemPickedUpListener itemPickedUpListener;
1152 	
1153 	/*========================================================================*/
1154 	
1155 	/** AgentInfo memory module. */
1156 	protected AgentInfo agentInfo;
1157 	/** Players memory module. */
1158 	private Players players;
1159 	
1160 	/**
1161 	 * Constructor. Setups the memory module based on bot's world view.
1162 	 * @param bot owner of the module that is using it
1163 	 */
1164 	public Senses(UT2004Bot bot)
1165 	{
1166 		this(bot, new AgentInfo(bot), new Players(bot), null);
1167 	}
1168 	
1169 	/**
1170 	 * Constructor. Setups the memory module based on bot's world view.
1171 	 * @param bot owner of the module that is using it
1172 	 * @param agentInfo AgentInfo memory module
1173 	 * @param players Players memory module
1174 	 */
1175 	public Senses(UT2004Bot bot, AgentInfo agentInfo, Players players)
1176 	{
1177 		this(bot, agentInfo, players, null);
1178 	}
1179 	
1180 	/**
1181 	 * Constructor. Setups the memory module based on bot's world view.
1182 	 * @param bot owner of the module that is using it
1183 	 * @param agentInfo AgentInfo memory module.
1184 	 * @param log Logger to be used for logging runtime/debug info. If <i>null</i>, module creates its own logger.
1185 	 */
1186 	public Senses(UT2004Bot bot, AgentInfo agentInfo, Players players, Logger log)
1187 	{
1188 		super(bot, log);
1189 
1190 		// set AgentInfo memory module
1191 		this.agentInfo = agentInfo;
1192 		NullCheck.check(this.agentInfo, "agentInfo");
1193 		
1194 		// set Players memory module
1195 		this.players = players;
1196 		NullCheck.check(this.players, "players");
1197 
1198 		// create listeners
1199 		bumpedListener =             new BumpedListener(worldView);
1200 		wallCollisitonListener =     new WallCollisionListener(worldView);
1201 		fallEdgeListener =           new FallEdgeListener(worldView);
1202 		hearNoiseListener =          new HearNoiseListener(worldView);
1203 		hearPickupListener =         new HearPickupListener(worldView);
1204 		botDamagedListener =         new BotDamagedListener(worldView);
1205 		incomingProjectileListener = new IncomingProjectileListener(worldView);
1206 		playerDamagedListener =      new PlayerDamagedListener(worldView);
1207 		playerKilledListener =       new PlayerKilledListener(worldView);
1208 		adrenalineGainedListener =   new AdrenalineGainedListener(worldView);
1209 		botKilledListener =          new BotKilledListener(worldView);
1210 		itemPickedUpListener =       new ItemPickedUpListener(worldView);
1211 		beginMessageListener =       new BeginMessageListener(worldView);
1212 		
1213 		cleanUp();
1214 	}
1215 	
1216 	@Override
1217 	protected void cleanUp() {
1218 		super.cleanUp();
1219 		lastAdrenalineGained = null;
1220 		lastAdrenalineGainedFlag = false;
1221 		lastAdrenalineGainedTime = -1;
1222 		lastBotDamaged = null;
1223 		lastBotDamagedFlag = false;
1224 		lastBotDamagedTime = -1;
1225 		lastBotKilled = null;
1226 		lastBotKilledFlag = false;
1227 		lastBotKilledTime = -1;
1228 		lastBotShot = null;
1229 		lastBotShotFlag = false;
1230 		lastBotShotTime = -1;
1231 		lastBumped = null;
1232 		lastBumpedFlag = false;
1233 		lastBumpedTime = -1;
1234 		lastFallEdge = null;
1235 		lastFallEdgeFlag = false;
1236 		lastFallEdgeTime = -1;
1237 		lastHearNoise = null;
1238 		lastHearNoiseFlag = false;
1239 		lastHearNoiseTime = -1;
1240 		lastHearPickup = null;
1241 		lastHearPickupFlag = false;
1242 		lastHearPickupTime = -1;
1243 		lastIncomingProjectile = null;
1244 		lastIncomingProjectileFlag = false;
1245 		lastIncomingProjectileTime = -1;
1246 		lastPlayerDamaged = null;
1247 		lastPlayerDamagedFlag = false;
1248 		lastPlayerDamagedTime = -1;
1249 		lastPlayerShot = null;
1250 		lastPlayerShotFlag = false;
1251 		lastPlayerShotTime = -1;
1252 		lastWallCollision = null;
1253 		lastWallCollisionFlag = false;
1254 		lastWallCollisionTime = -1;
1255 	}
1256 	
1257 }