View Javadoc

1   package cz.cuni.amis.pogamut.udk.agent.module.sensor;
2   
3   import java.util.ArrayList;
4   import java.util.Collections;
5   import java.util.HashMap;
6   import java.util.HashSet;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Random;
11  import java.util.Set;
12  import java.util.logging.Logger;
13  
14  import cz.cuni.amis.pogamut.base.agent.module.SensorModule;
15  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
16  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
17  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent;
18  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
19  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
20  import cz.cuni.amis.pogamut.udk.bot.IUDKBotController;
21  import cz.cuni.amis.pogamut.udk.bot.impl.UDKBot;
22  import cz.cuni.amis.pogamut.udk.communication.messages.ItemType;
23  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.EndMessage;
24  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Item;
25  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.ItemPickedUp;
26  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.NavPoint;
27  import cz.cuni.amis.pogamut.udk.communication.translator.shared.events.MapPointListObtained;
28  import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
29  import cz.cuni.amis.utils.NullCheck;
30  import cz.cuni.amis.utils.collections.MyCollections;
31  import cz.cuni.amis.utils.maps.HashMapMap;
32  
33  /**
34   * Memory module specialized on items on the map.
35   * <p><p>
36   * Apart from providing useful getters based on {@link ItemType}, {@link ItemType.Group} and {@link ItemType.Category} it also
37   * provides an optimistic approach for guessing whether some item is spawned inside the map via getSpawnedXYZ methods
38   * such as {@link Items#getSpawnedItems()}.
39   * <p><p>
40   * <b>WARNING:</b>There are methods that contains "reachable" in its name but it is totally unclear what UT2004 means by reachable!!!
41   * These methods are for experimenting purposes only.
42   * <p><p>
43   * It is designed to be initialized inside {@link IUDKBotController#prepareBot(UDKBot)} method call
44   * 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)}
45   * is called.
46   *
47   * @author Juraj 'Loque' Simlovic
48   * @author Jimmy
49   */
50  public class Items extends SensorModule<UDKBot>
51  {	
52  	/**
53  	 * How fast are items respawning inside UT2004.
54  	 */
55  	public static final double ITEM_RESPAWN_TIME = 20;
56  	
57  	private Random random = new Random(System.currentTimeMillis());
58  
59  	/**
60  	 * Returns random item from 'all' items.
61  	 * <p><p>
62  	 * Note that there is no need to provide all "getRandomXYZ(xyz)", just
63  	 * use {@link MyCollections#getRandom(java.util.Collection)}. 
64  	 * @return
65  	 */
66  	public Item getRandomItem() {
67  		if (getAllItems().size() == 0) return null;
68  		int num = random.nextInt(getAllItems().size());
69  		Iterator<Item> iter = getAllItems().values().iterator();
70  		for (int i = 0; i < num-1; ++i) iter.next();
71  		return iter.next();
72  	}
73  	
74  	/*========================================================================*/
75  	
76  	/**
77  	 * Retrieves list of all items, which includes all known pickups and all
78  	 * visible thrown items.
79  	 *
80  	 * <p>Note: The returned Map is unmodifiable and self updating throughout
81  	 * time. Once you obtain a specific Map of items from this module, the Map
82  	 * will get updated based on what happens within the map.
83  	 *
84  	 * @return List of all items. Note: Spawned items are included only.
85  	 */
86  	public Map<UnrealId, Item> getAllItems()
87  	{
88  		return Collections.unmodifiableMap(items.all);
89  	}
90  
91  	/**
92  	 * Retrieves list of all items <b>of specific type</b>.
93  	 *
94  	 * <p>Note: The returned Map is unmodifiable and self updating throughout
95  	 * time. Once you obtain a specific Map of items from this module, the Map
96  	 * will get updated based on what happens within the map.
97  	 *
98  	 * @return List of all items of specific type. Note: Spawned items are included only.
99  	 */
100 	public Map<UnrealId, Item> getAllItems(ItemType type)
101 	{
102 		return Collections.unmodifiableMap(items.allCategories.get(type));
103 	}
104 	
105 	/**
106 	 * Retrieves map of all items belonging to a <b>specific 'category'</b> of items, which
107 	 * includes all known pickups.
108 	 * 
109 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
110 	 * invocation of this method.
111 	 * 
112 	 * <p><p>WARNING: O(n) complexity!
113 	 * 
114 	 * @param category
115 	 * @return Map of all items of a specific category.
116 	 */
117 	public Map<UnrealId, Item> getAllItems(ItemType.Category category) {
118 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
119 		for (ItemType type : category.getTypes()) {
120 			result.putAll(getAllItems(type));
121 		}
122 		return result;
123 	}
124 	
125 	/**
126 	 * Retrieves map of all items belonging to a <b>specific 'group'</b> of items, which
127 	 * includes all known pickups.
128 	 * 
129 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
130 	 * invocation of this method.
131 	 * 
132 	 * <p><p>WARNING: O(n) complexity!
133 	 * 
134 	 * @param group
135 	 * @return Map of all items of a specific group.
136 	 */
137 	public Map<UnrealId, Item> getAllItems(ItemType.Group group) {
138 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
139 		for (ItemType type : group.getTypes()) {
140 			result.putAll(getAllItems(type));
141 		}
142 		return result;
143 	}
144 
145 	/**
146 	 * Retrieves a specific item from the all items in the map.
147 	 * <p><p>
148 	 * Once obtained it is self-updating based on what happens in the game.
149 	 *
150 	 * @param id
151 	 * @return A specific Item be it Spawned or Dropped (Dropped item must be visible though!).
152 	 */
153 	public Item getItem(UnrealId id) {
154 		Item item = items.all.get(id);
155 		if (item == null) item = items.visible.get(id);
156 		return item;
157 	}
158 
159 	/**
160 	 * Retrieves a specific item from the all items in the map.
161 	 * <p><p>
162 	 * Once obtained it is self-updating based on what happens in the game.
163 	 *
164 	 * @param stringUnrealId
165 	 * @return A specific Item be it Spawned or Dropped (Dropped item must be visible though!).
166 	 */
167 	public Item getItem(String stringUnrealId) {
168 		return getItem(UnrealId.get(stringUnrealId));
169 	}
170 
171 	/*========================================================================*/
172 
173 	/**
174 	 * Retreives list of all visible items, which includes all visible known
175 	 * pickups and all visible thrown items.
176 	 *
177 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
178 	 * time. Once you obtain a specific Map of items from this module, the Map
179 	 * will get updated based on what happens within the map.
180 	 *
181 	 * @return List of all visible items. Note: Spawned items are included only.
182 	 */
183 	public Map<UnrealId, Item> getVisibleItems()
184 	{
185 		return Collections.unmodifiableMap(items.visible);
186 	}
187 
188 	/**
189 	 * Retreives list of all visible items <b> of specific type</b>, which includes all visible known
190 	 * pickups and all visible thrown items.
191 	 *
192 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
193 	 * time. Once you obtain a specific Map of items from this module, the Map
194 	 * will get updated based on what happens within the map.
195 	 *
196 	 * @return List of all visible items of specific type. Note: Spawned items are included only.
197 	 */
198 	public Map<UnrealId, Item> getVisibleItems(ItemType type)
199 	{
200 		return Collections.unmodifiableMap(items.visibleCategories.get(type));
201 	}
202 	
203 	/**
204 	 * Retrieves map of visible items belonging to a <b>specific 'category'</b> of items, which
205 	 * includes all known pickups.
206 	 * 
207 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
208 	 * invocation of this method.
209 	 * 
210 	 * <p><p>WARNING: O(n) complexity!
211 	 * 
212 	 * @param category
213 	 * @return Map of visible items of a specific category.
214 	 */
215 	public Map<UnrealId, Item> getVisibleItems(ItemType.Category category) {
216 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
217 		for (ItemType type : category.getTypes()) {
218 			result.putAll(getVisibleItems(type));
219 		}
220 		return result;
221 	}
222 	
223 	/**
224 	 * Retrieves map of visible items belonging to a <b>specific 'group'</b> of items, which
225 	 * includes all known pickups.
226 	 * 
227 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
228 	 * invocation of this method.
229 	 * 
230 	 * <p><p>WARNING: O(n) complexity!
231 	 * 
232 	 * @param group
233 	 * @return Map of visible items of a specific group.
234 	 */
235 	public Map<UnrealId, Item> getVisibleItems(ItemType.Group group) {
236 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
237 		for (ItemType type : group.getTypes()) {
238 			result.putAll(getVisibleItems(type));
239 		}
240 		return result;
241 	}
242 
243 	/**
244 	 * Retrieves a specific item from the visible items in the map. If item of specified
245 	 * id is not visible returns null.
246 	 * <p><p>
247 	 * Once obtained it is self-updating based on what happens in the game.
248 	 *
249 	 * @param id
250 	 * @return A specific Item be it Spawned or Dropped.
251 	 */
252 	public Item getVisibleItem(UnrealId id) {
253 		Item item = items.visible.get(id);
254 		return item;
255 	}
256 
257 	/**
258 	 * Retrieves a specific item from the visible items in the map. If item of specified
259 	 * id is not visible returns null.
260 	 * <p><p>
261 	 * Once obtained it is self-updating based on what happens in the game.
262 	 *
263 	 * @param stringUnrealId
264 	 * @return A specific Item be it Spawned or Dropped.
265 	 */
266 	public Item getVisibleItem(String stringUnrealId) {
267 		return getVisibleItem(UnrealId.get(stringUnrealId));
268 	}
269 
270 	/*========================================================================*/
271 
272 	/**
273 	 * Retrieves list of all reachable items, which includes all reachable
274 	 * known pickups and all reachable and visible thrown items.
275 	 *
276 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
277 	 * time. Once you obtain a specific Map of items from this module, the Map
278 	 * will get updated based on what happens within the map.
279 	 * 
280 	 * <p><p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
281 	 *
282 	 * @return List of all reachable items. Note: Spawned items are included only.
283 	 */
284 	public Map<UnrealId, Item> getReachableItems()
285 	{
286 		return Collections.unmodifiableMap(items.reachable);
287 	}
288 
289 	/**
290 	 * Retrieves list of all reachable items <b>of specific type</b>, which includes all reachable
291 	 * known pickups and all reachable and visible thrown items.
292 	 *
293 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
294 	 * time. Once you obtain a specific Map of items from this module, the Map
295 	 * will get updated based on what happens within the map.
296 	 * 
297 	 * <p><p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
298 	 *
299 	 * @return List of all reachable items of specific type. Note: Spawned items are included only.
300 	 */
301 	public Map<UnrealId, Item> getReachableItems(ItemType type)
302 	{
303 		return Collections.unmodifiableMap(items.reachableCategories.get(type));
304 	}
305 	
306 	/**
307 	 * Retrieves map of reachable items belonging to a <b>specific 'category'</b> of items, which
308 	 * includes all known pickups.
309 	 * 
310 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
311 	 * invocation of this method.
312 	 * 
313 	 * <p><p>WARNING: O(n) complexity!
314 	 * 
315 	 * <p><p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
316 	 * 
317 	 * @param category
318 	 * @return Map of reachable items of a specific category.
319 	 */
320 	public Map<UnrealId, Item> getReachableItems(ItemType.Category category) {
321 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
322 		for (ItemType type : category.getTypes()) {
323 			result.putAll(getAllItems(type));
324 		}
325 		return result;
326 	}
327 	
328 	/**
329 	 * Retrieves map of reachable items belonging to a <b>specific 'group'</b> of items, which
330 	 * includes all known pickups.
331 	 * 
332 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
333 	 * invocation of this method.
334 	 * 
335 	 * <p><p>WARNING: O(n) complexity!
336 	 * 
337 	 * <p><p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
338 	 * 
339 	 * @param group
340 	 * @return Map of reachable items of a specific group.
341 	 */
342 	public Map<UnrealId, Item> getReachableItems(ItemType.Group group) {
343 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
344 		for (ItemType type : group.getTypes()) {
345 			result.putAll(getReachableItems(type));
346 		}
347 		return result;
348 	}
349 
350 	/**
351 	 * Retrieves a specific item from the all items in the map that is currently reachable.
352 	 * If item of specified item is not reachable returns null.
353 	 * <p><p>
354 	 * Once obtained it is self-updating based on what happens in the game.
355 	 * 
356 	 * <p><p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
357 	 *
358 	 * @param id
359 	 * @return A specific Item be it Spawned or Dropped (Dropped item must be visible though!).
360 	 */
361 	public Item getReachableItem(UnrealId id) {
362 		Item item = items.reachable.get(id);
363 		if (item == null) {
364 			item = items.visible.get(id);
365 			if (!item.isReachable()) return null;
366 		}
367 		return item;
368 	}
369 
370 	/**
371 	 * Retrieves a specific item from the all items in the map that is currently reachable.
372 	 * If item of specified item is not reachable returns null.
373 	 * <p><p>
374 	 * Once obtained it is self-updating based on what happens in the game.
375 	 * 
376 	 * <p><p><b>WARNING:</b>It is totally unclear what UT2004 means by reachable!!!
377 	 *
378 	 * @param stringUnrealId
379 	 * @return A specific Item be it Spawned or Dropped (Dropped item must be visible though!).
380 	 */
381 	public Item getReachableItem(String stringUnrealId) {
382 		return getReachableItem(UnrealId.get(stringUnrealId));
383 	}
384 
385 	/*========================================================================*/
386 
387 	/**
388 	 * Retrieves list of all known item pickup points.
389 	 *
390 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
391 	 * time. Once you obtain a specific Map of items from this module, the Map
392 	 * will get updated based on what happens within the map.
393 	 *
394 	 * @return List of all items. Note: Empty pickups are included as well.
395 	 *
396 	 * @see isPickupSpawned(Item)
397 	 */
398 	public Map<UnrealId, Item> getKnownPickups()
399 	{
400 		return Collections.unmodifiableMap(items.known);
401 	}
402 
403 	/**
404 	 * Retrieves list of all known item pickup points <b>of specific type</b>.
405 	 *
406 	 * <p>Note: The returned Map is unmodifiable and self updating throughout
407 	 * time. Once you obtain a specific Map of items from this module, the Map
408 	 * will get updated based on what happens within the map.
409 	 *
410 	 * @return List of all items of specific type. Note: Empty pickups are included as well.
411 	 *
412 	 * @see isPickupSpawned(Item)
413 	 */
414 	public Map<UnrealId, Item> getKnownPickups(ItemType type)
415 	{
416 		return Collections.unmodifiableMap(items.knownCategories.get(type));
417 	}
418 	
419 	/**
420 	 * Retrieves map of all known pickups belonging to a <b>specific 'category'</b> of items, which
421 	 * includes all known pickups.
422 	 * 
423 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
424 	 * invocation of this method.
425 	 * 
426 	 * <p><p>WARNING: O(n) complexity!
427 	 * 
428 	 * @param category
429 	 * @return Map of known pickups of a specific category.
430 	 */
431 	public Map<UnrealId, Item> getKnownPickups(ItemType.Category category) {
432 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
433 		for (ItemType type : category.getTypes()) {
434 			result.putAll(getKnownPickups(type));
435 		}
436 		return result;
437 	}
438 	
439 	/**
440 	 * Retrieves map of all known pickups belonging to a <b>specific 'group'</b> of items, which
441 	 * includes all known pickups.
442 	 * 
443 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
444 	 * invocation of this method.
445 	 * 
446 	 * <p><p>WARNING: O(n) complexity!
447 	 * 
448 	 * @param group
449 	 * @return Map of known pickups of a specific group.
450 	 */
451 	public Map<UnrealId, Item> getKnownPickups(ItemType.Group group) {
452 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
453 		for (ItemType type : group.getTypes()) {
454 			result.putAll(getKnownPickups(type));
455 		}
456 		return result;
457 	}
458 
459 	/**
460 	 * Retrieves a specific pickup point.
461 	 * <p><p>
462 	 * Once obtained it is self-updating based on what happens in the game.
463 	 *
464 	 * @param id
465 	 * @return A specific Item be it Spawned or Dropped (Dropped item must be visible though!).
466 	 */
467 	public Item getKnownPickup(UnrealId id) {
468 		return items.known.get(id);
469 	}
470 
471 	/**
472 	 * Retrieves a specific pickup point.
473 	 * <p><p>
474 	 * Once obtained it is self-updating based on what happens in the game.
475 	 *
476 	 * @param stringUnrealId
477 	 * @return A specific Item be it Spawned or Dropped (Dropped item must be visible though!).
478 	 */
479 	public Item getKnownPickup(String stringUnrealId) {
480 		return getKnownPickup(UnrealId.get(stringUnrealId));
481 	}
482 
483 	/*========================================================================*/
484 	
485 	/**
486 	 * Uses {@link Items#isPickupSpawned(Item)} to return all items that are believed to 
487 	 * be currently spawned.
488 	 * 
489 	 * <p><p>WARNING: O(n) complexity!
490 	 * 
491 	 * @return collection of spawned items
492 	 */
493 	public Map<UnrealId, Item> getSpawnedItems() {
494 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
495 		for (Item item : getAllItems().values()) {
496 			if (isPickupSpawned(item)) result.put(item.getId(), item);
497 		}
498 		return result;
499 	}
500 	
501 	/**
502 	 * Uses {@link Items#isPickupSpawned(Item)} to return all items of 'type' that are believed to 
503 	 * be currently spawned.
504 	 * 
505 	 * <p><p>WARNING: O(n) complexity!
506 	 * 
507 	 * @return Map of spawned items of a specific type.
508 	 */
509 	public Map<UnrealId, Item> getSpawnedItems(ItemType type) {
510 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
511 		for (Item item : getAllItems(type).values()) {
512 			if (isPickupSpawned(item)) {
513 				result.put(item.getId(), item);
514 			}
515 		}
516 		return result;
517 	}
518 	
519 	/**
520 	 * Uses {@link Items#isPickupSpawned(Item)} to return all items belonging to a specific 'category' that are believed to 
521 	 * be currently spawned.
522 	 * 
523 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
524 	 * invocation of this method.
525 	 * 
526 	 * <p><p>WARNING: O(n) complexity!
527 	 * 
528 	 * @param category
529 	 * @return Map of spawned items of a specific category.
530 	 */
531 	public Map<UnrealId, Item> getSpawnedItems(ItemType.Category category) {
532 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
533 		for (ItemType type : category.getTypes()) {
534 			result.putAll(getSpawnedItems(type));
535 		}
536 		return result;
537 	}
538 	
539 	/**
540 	 * Uses {@link Items#isPickupSpawned(Item)} to return all items belonging to a specific 'group' that are believed to 
541 	 * be currently spawned.
542 	 * 
543 	 * <p>Note: The returned Map is modifiable and is always constructed upon every
544 	 * invocation of this method.
545 	 * 
546 	 * <p><p>WARNING: O(n) complexity!
547 	 * 
548 	 * @param group
549 	 * @return Map of spawned items of a specific group.
550 	 */
551 	public Map<UnrealId, Item> getSpawnedItems(ItemType.Group group) {
552 		Map<UnrealId, Item> result = new HashMap<UnrealId, Item>();
553 		for (ItemType type : group.getTypes()) {
554 			result.putAll(getSpawnedItems(type));
555 		}
556 		return result;
557 	}
558 	
559 	/**
560 	 * Returns how fast are the items respawning based on their item type.
561 	 * @param itemType
562 	 * @return
563 	 */
564 	public double getItemRespawnTime(ItemType itemType) {
565 		return ITEM_RESPAWN_TIME;
566 	}
567 	
568 	/**
569 	 * Tells, whether the given pickup point contains a spawned item.
570 	 * 
571 	 * <p><p>This implementation is guessing (optimistically) whether the item is spawned based on
572 	 * the last time we have seen it missing (bot has seen its spawning-navpoint but the item was not laying there).
573 	 * 
574 	 * <p><p>Note that the guessing is not perfect, experiment with it or check the source code
575 	 * and possibly reimplement (probably copy-paste) to suit your needs.
576 	 * 
577 	 * <p><p>Note that this method is working correctly only if items are respawning.
578 	 *
579 	 * @param item Item, for which its pickup point is to be examined.
580 	 * @return True, if the item is spawned; false if the pickup is empty.
581 	 *
582 	 * @see getKnownPickups(boolean,boolean)
583 	 */
584 	public boolean isPickupSpawned(Item item) {
585 		if (item == null) return false;
586 		if (item.isVisible()) {
587 			// if the item is visible it is truly spawned
588 			return true;
589 		}
590 		NavPoint np = item.getNavPoint();
591 		if (np == null) {
592 			np = navPoints.get(item.getNavPointId());
593 		}		
594 		if (np != null) { 
595 			if (np.isVisible()) {
596 				// we can see the spawning-navpoint but the item is not visible!
597 				return false;
598 			}
599 			Double time = items.itemMissingTimes.get(item);
600 			if (time == null) {
601 				// we do not have information about item's spawning time
602 				return true; // guessing that the item is still there
603 			} else {
604 				return agentInfo.getTime() - time > getItemRespawnTime(item.getType());
605 			}
606 		} else {
607 			// we do not have item's navpoint, just check times
608 			Double time = items.itemMissingTimes.get(item);
609 			if (time == null) {
610 				// we do not have information about item's spawning time
611 				return true; // guessing that the item is still there
612 			} else {
613 				return agentInfo.getTime() - time > getItemRespawnTime(item.getType());
614 			}
615 		}
616 	}
617 	
618 	/**
619 	 * Tells, whether the given pickup point contains a spawned item.
620 	 * 
621 	 * <p><p>This implementation is guessing (optimistically) whether the item is spawned based on
622 	 * the last time we have seen it missing (saw its navpoint but the item was not laying there).
623 	 * 
624 	 * <p><p>Note that the guessing is not perfect, experiment with it or check the source code
625 	 * and possibly reimplement to suit your needs.
626 	 * 
627 	 * <p><p>Note that this method is working only if items are respawning.
628 	 *
629 	 * @param itemId Id of the item, for which its pickup point is to be examined.
630 	 * @return True, if the item is spawned; false if the pickup is empty.
631 	 *
632 	 * @see getKnownPickups(boolean,boolean)
633 	 */
634 	public boolean isPickupSpawned(UnrealId itemId) {	
635 		return isPickupSpawned(items.all.get(itemId));
636 	}
637 
638 	/*========================================================================*/
639 
640 	/**
641 	 * Maps of items of specific type.
642 	 */
643 	private class ItemMaps
644 	{
645 		/** Map of all items (known and thrown). */
646 		private HashMap<UnrealId, Item> all = new HashMap<UnrealId, Item> ();
647 		/** Map of visible items of the specific type. */
648 		private HashMap<UnrealId, Item> visible = new HashMap<UnrealId, Item> ();
649 		/** Map of visible items of the specific type. */
650 		private HashMap<UnrealId, Item> reachable = new HashMap<UnrealId, Item> ();
651 		/** Map of all known items of the specific type. */
652 		private HashMap<UnrealId, Item> known = new HashMap<UnrealId, Item> ();
653 		/** Map of all items (known and thrown) of specific categories. */
654 		private HashMapMap<ItemType, UnrealId, Item> allCategories = new HashMapMap<ItemType, UnrealId, Item>();
655 		/** Map of visible items of the specific type. */
656 		private HashMapMap<ItemType, UnrealId, Item> visibleCategories = new HashMapMap<ItemType, UnrealId, Item> ();
657 		/** Map of visible items of the specific type. */
658 		private HashMapMap<ItemType, UnrealId, Item> reachableCategories = new HashMapMap<ItemType, UnrealId, Item> ();
659 		/** Map of all known items of the specific type. */
660 		private HashMapMap<ItemType, UnrealId, Item> knownCategories = new HashMapMap<ItemType, UnrealId, Item> ();
661 		/**Map of all items and the time when they were last seen as 'missing' (== picked up == they were not on their navpoint). */		
662 		private HashMap<Item, Double> itemMissingTimes = new HashMap<Item, Double>();		
663 		/** Set of items that were picked up in this sync-batch. */
664 		private Set<Item> justPickedUp = new HashSet<Item>();
665 		
666 		/**
667 		 * Processes events.
668 		 * @param item Item to process.
669 		 */
670 		private void notify(Item item)
671 		{
672 			UnrealId uid = item.getId();
673 
674 			// be sure to be within all
675 			if (!all.containsKey(uid)) {
676 				all.put(uid, item);
677 				allCategories.put(item.getType(), item.getId(), item);
678 			}
679 
680 			// previous visibility
681 			boolean wasVisible = visible.containsKey(uid);
682 			boolean isVisible = item.isVisible();
683 
684 			// refresh visible
685 			if (isVisible && !wasVisible)
686 			{
687 				// add to visible(s)
688 				visible.put(uid, item);
689 				visibleCategories.put(item.getType(), item.getId(), item);
690 			}
691 			else if (!isVisible && wasVisible)
692 			{
693 				// remove from visible(s)
694 				visible.remove(uid);
695 				visibleCategories.remove(item.getType(), item.getId());
696 			}
697 
698 			// remove non-visible thrown items
699 			if (!isVisible && item.isDropped()) {
700 				all.remove(uid);
701 				allCategories.remove(item.getType(), item.getId());
702 			}
703 		
704 			// remove item from the missing one (we can clearly see that the item is spawned)
705 			if (isVisible) {
706 				itemMissingTimes.remove(item);
707 			}
708 
709 			// previous reachability
710 			boolean wasReachable = reachable.containsKey(uid);
711 			boolean isReachable = item.isReachable();
712 
713 			// refresh reachable
714 			if (isReachable && !wasReachable)
715 			{
716 				// add to reachables
717 				reachable.put(uid, item);
718 				reachableCategories.put(item.getType(), item.getId(), item);
719 			}
720 			else if (!isReachable && wasReachable)
721 			{
722 				// remove from reachables
723 				reachable.remove(uid);
724 				reachableCategories.remove(item.getType(), item.getId());
725 			}		
726 		}
727 		
728 		/**
729 		 * Processes events.
730 		 * @param items Map of known items to process.
731 		 */
732 		private void notify(Map<UnrealId, Item> items)
733 		{
734 			// register all known items
735 			known.putAll(items);
736 			for (Item item : items.values()) {
737 				knownCategories.put(item.getType(), item.getId(), item);
738 				notify(item);
739 			}
740 		}
741 		
742 		/**
743 		 * Handles 'itemMissingTimes', called from the {@link EndMessageListener}.
744 		 * @param navPoints
745 		 */
746 		private void notifyBatchEnd(List<NavPoint> navPoints) {
747 			for (NavPoint np : navPoints) {
748 				if (!np.isVisible()) continue;
749 				Item item = all.get(np.getItem());
750 				if (item == null) continue;
751 				if (justPickedUp.contains(item)) {
752 					// we do not need to handle these items
753 					continue;
754 				}
755 				if (visible.containsKey(np.getItem())) {
756 					// navpoint is visible and the item is there - delete the last entry in missing times
757 					itemMissingTimes.remove(item);
758 				} else {
759 					// navpoint is visible but item is not there
760 					if (!visible.containsKey(np.getItem())) {
761 						// we don't have a mark that the item was picked up, create
762 						itemMissingTimes.put(item, agentInfo.getTime());
763 					}
764 				}
765 			}
766 			justPickedUp.clear();
767 		}
768 
769 		/**
770 		 * Handles 'itemMissingTimes' for picked up item.
771 		 * @param event
772 		 */
773 		private void notify(ItemPickedUp event) {
774 			Item item = all.get(event.getId());
775 			if (item == null) return;
776 			itemMissingTimes.put(item, agentInfo.getTime());
777 			justPickedUp.add(item);
778 		}
779 
780 		private void clear() {
781 			all.clear();
782 			allCategories.clear();
783 			itemMissingTimes.clear();
784 			justPickedUp.clear();
785 			known.clear();
786 			knownCategories.clear();
787 			reachable.clear();
788 			reachableCategories.clear();
789 			visible.clear();
790 			visibleCategories.clear();			
791 		}
792 	}
793 
794 	/** Maps of all items. */
795 	private ItemMaps items = new ItemMaps ();
796 
797 	/*========================================================================*/
798 
799 	protected class ItemsListener implements IWorldObjectEventListener<Item, IWorldObjectEvent<Item>> {
800 
801 		public ItemsListener(IWorldView worldView) {
802 			worldView.addObjectListener(Item.class, IWorldObjectEvent.class, this);
803 		}
804 
805         public void notify(IWorldObjectEvent<Item> event) {
806             items.notify(event.getObject());
807         }
808 
809     }
810 
811 	protected ItemsListener itemsListener;
812 
813 	/**
814 	 *  This method is called from the constructor to hook a listener that updated the items field.
815 	 *  <p><p>
816 	 *  It must:
817 	 *  <ol>
818 	 *  <li>initialize itemsListener field</li>
819 	 *  <li>hook the listener to the world view</li>
820 	 *  </ol>
821 	 *  <p><p>
822 	 *  By overriding this method you may provide your own listener that may wrap Items with your class
823 	 *  adding new fields into them.
824 	 *
825 	 *  @param worldView
826 	 **/
827 	protected ItemsListener createItemsListener(IWorldView worldView) {
828 		return new ItemsListener(worldView);
829 	}
830 
831 	/*========================================================================*/
832 
833 	/**
834 	 * MapPointsListObtained listener.
835 	 */
836 	protected class MapPointsListener implements IWorldEventListener<MapPointListObtained>
837 	{
838 		/**
839 		 * Constructor. Registers itself on the given WorldView object.
840 		 * @param worldView WorldView object to listen to.
841 		 */
842 		public MapPointsListener(IWorldView worldView)
843 		{
844 			worldView.addEventListener(MapPointListObtained.class, this);
845 		}
846 
847 		@Override
848 		public void notify(MapPointListObtained event)
849 		{
850 			navPoints = event.getNavPoints();
851 			items.notify(event.getItems());			
852 		}
853 
854 	}
855 
856 	/** MapPointsListObtained listener */
857 	protected MapPointsListener mapPointsListener;
858 	protected Map<UnrealId, NavPoint> navPoints = new HashMap<UnrealId, NavPoint>();
859 
860 	/**
861 	 *  This method is called from the constructor to create a listener that initialize items field from
862 	 *  the MapPointListObtained event.
863 	 *  <p><p>
864 	 *  By overriding this method you may provide your own listener that may wrap Items with your class
865 	 *  adding new fields into them.
866 	 *
867 	 *  @param worldView
868 	 * @return
869 	 **/
870 	protected MapPointsListener createMapPointsListener(IWorldView worldView) {
871 		return new MapPointsListener(worldView);
872 	}
873 
874 	/*========================================================================*/
875 	
876 	/**
877 	 * MapPointsListObtained listener.
878 	 */
879 	protected class NavPointListener implements IWorldObjectEventListener<NavPoint, WorldObjectUpdatedEvent<NavPoint>>
880 	{
881 		/**
882 		 * Constructor. Registers itself on the given WorldView object.
883 		 * @param worldView WorldView object to listen to.
884 		 */
885 		public NavPointListener(IWorldView worldView) {
886 			worldView.addObjectListener(NavPoint.class, WorldObjectUpdatedEvent.class, this);
887 		}
888 
889 		@Override
890 		public void notify(WorldObjectUpdatedEvent<NavPoint> event) {
891 			if (event.getObject().isVisible()) {
892 				navPointsToProcess.add(event.getObject());
893 			} else {
894 				navPointsToProcess.remove(event.getObject());
895 			}
896 		}
897 
898 	}
899 	
900 	/**
901 	 * Contains only navpoints that are visible so we can check whether they are spawning-points,
902 	 * if so - we may check whether the item is laying there or not to handle spawning times.
903 	 */
904 	protected List<NavPoint> navPointsToProcess = new ArrayList<NavPoint>();
905 	
906 	protected NavPointListener navPointListener;
907 	
908 	/**
909 	 * {@link EndMessage} listener.
910 	 */
911 	protected class EndMessageListener implements IWorldEventListener<EndMessage>
912 	{
913 		/**
914 		 * Constructor. Registers itself on the given WorldView object.
915 		 * @param worldView WorldView object to listen to.
916 		 */
917 		public EndMessageListener(IWorldView worldView)
918 		{
919 			worldView.addEventListener(EndMessage.class, this);
920 		}
921 
922 		@Override
923 		public void notify(EndMessage event)
924 		{
925 			items.notifyBatchEnd(navPointsToProcess);
926 		}
927 
928 	}
929 	
930 	protected EndMessageListener endMessageListener;
931 	
932 	/**
933 	 * {@link ItemPickedUp} listener.
934 	 */
935 	protected class ItemPickedUpListener implements IWorldEventListener<ItemPickedUp>
936 	{
937 		/**
938 		 * Constructor. Registers itself on the given WorldView object.
939 		 * @param worldView WorldView object to listen to.
940 		 */
941 		public ItemPickedUpListener(IWorldView worldView)
942 		{
943 			worldView.addEventListener(ItemPickedUp.class, this);
944 		}
945 
946 		@Override
947 		public void notify(ItemPickedUp event)
948 		{
949 			items.notify(event);
950 		}
951 
952 	}
953 	
954 	protected ItemPickedUpListener itemPickedUpListener;
955 
956 	/*========================================================================*/
957 
958 	/** AgentInfo memory module. */
959 	protected AgentInfo agentInfo;
960 
961 	/**
962 	 * Constructor. Setups the memory module based on bot's world view.
963 	 * @param bot owner of the module that is using it
964 	 */
965 	public Items(UDKBot bot) {
966 		this(bot, new AgentInfo(bot), null);
967 	}
968 	
969 	/**
970 	 * Constructor. Setups the memory module based on bot's world view.
971 	 * @param bot owner of the module that is using it
972 	 * @param agentInfo AgentInfo memory module
973 	 */
974 	public Items(UDKBot bot, AgentInfo agentInfo) {
975 		this(bot, agentInfo, null);
976 	}
977 	
978 	/**
979 	 * Constructor. Setups the memory module based on bot's world view.
980 	 * @param bot owner of the module that is using it
981 	 * @param agentInfo AgentInfo memory module
982 	 * @param log Logger to be used for logging runtime/debug info, if null is provided the module creates its own logger
983 	 */
984 	public Items(UDKBot bot, AgentInfo agentInfo, Logger log)
985 	{
986 		super(bot, log);
987 
988 		// save reference
989 		this.agentInfo = agentInfo;
990 		NullCheck.check(this.agentInfo, "agentInfo");
991 
992 		// create listeners
993 		itemsListener = createItemsListener(worldView);
994 		mapPointsListener = createMapPointsListener(worldView);
995 		navPointListener = new NavPointListener(worldView);
996 		endMessageListener = new EndMessageListener(worldView);
997 		itemPickedUpListener = new ItemPickedUpListener(worldView);
998 	}
999 
1000 	/**
1001 	 * Provides initialization of the module (clearing internal data structures). Called automatically
1002 	 * during the agent starting sequence.
1003 	 */
1004 	@Override
1005 	protected void start(boolean startPaused) {
1006 		super.start(startPaused);
1007 		navPoints.clear();
1008 		items.clear();
1009 	}
1010 	
1011 }