View Javadoc

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