View Javadoc

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