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 }