1 package cz.cuni.amis.pogamut.udk.agent.module.sensor; 2 3 import java.util.List; 4 import java.util.logging.Logger; 5 6 import cz.cuni.amis.pogamut.base.agent.module.SensorModule; 7 import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView; 8 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener; 9 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent; 10 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener; 11 import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectFirstEncounteredEvent; 12 import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent; 13 import cz.cuni.amis.pogamut.udk.bot.IUDKBotController; 14 import cz.cuni.amis.pogamut.udk.bot.impl.UDKBot; 15 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.BeginMessage; 16 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.FlagInfo; 17 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.GameInfo; 18 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.InitedMessage; 19 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Mutator; 20 import cz.cuni.amis.pogamut.udk.communication.translator.shared.events.MutatorListObtained; 21 22 import java.util.Collection; 23 import java.util.HashMap; 24 import java.util.Map; 25 import java.util.logging.Level; 26 27 /** 28 * Memory module specialized on general info about the game. 29 * <p><p> 30 * It is designed to be initialized inside {@link IUDKBotController#prepareBot(UDKBot)} method call 31 * 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)} 32 * is called. 33 * 34 * @author Juraj 'Loque' Simlovic 35 * @author Jimmy 36 */ 37 public class Game extends SensorModule<UDKBot> 38 { 39 /** 40 * Enums for game types that shields you from Unreal's string ids of game types. 41 * @author Jimmy 42 * 43 */ 44 public enum GameType 45 { 46 /** Classic death-match: Kill or get killed. You're on you own! */ 47 BotDeathMatch, 48 /** Team death-match: Strategic team killing. Shoot opponents only. */ 49 BotTeamGame, 50 /** Capture the Flag! Raid the enemy base, steal their flag. */ 51 BotCTFGame, 52 /** Capture the Flag! Raid the enemy base, steal their flag, with vehicles */ 53 BotVehicleCTFGame, 54 /** This type of game is not supported. */ 55 Unknown; 56 57 /** 58 * Tedious work this is.. Let's do it once, shall we? 59 * 60 * @param type Name of the type of the game type. 61 * @return Game type associated with given name. 62 */ 63 public static GameType getType(String type) 64 { 65 if (type.equalsIgnoreCase("BotDeathMatch")) return BotDeathMatch; 66 if (type.equalsIgnoreCase("BotTeamGame")) return BotTeamGame; 67 if (type.equalsIgnoreCase("BotCTFGame")) return BotCTFGame; 68 if (type.equalsIgnoreCase("BotVehicleCTFGame")) return BotVehicleCTFGame; 69 return Unknown; 70 } 71 } 72 73 /** 74 * Retreives the type of the game. 75 * 76 * @return Type of the game. 77 */ 78 public GameType getGameType() 79 { 80 // retreive from GameInfo object and translate 81 if (lastGameInfo == null) return null; 82 return GameType.getType(lastGameInfo.getGametype()); 83 } 84 85 /*========================================================================*/ 86 87 /** 88 * Retreives the name of current map. 89 * 90 * @return Name of the current map. 91 */ 92 public String getMapName() 93 { 94 // retreive from GameInfo object 95 if (lastGameInfo == null) return null; 96 return lastGameInfo.getLevel(); 97 } 98 99 /*========================================================================*/ 100 101 /** 102 * Retreives current game time, since the game started. 103 * 104 * @return Current game timestamp. 105 * 106 * @todo Test, whether it is correct.. 107 */ 108 public double getTime() 109 { 110 // retreive from last BeginMessage object 111 if (lastBeginMessage == null) return 0; 112 return lastBeginMessage.getTime(); 113 } 114 115 /** 116 * Retreives time limit for the game. 117 * 118 * <p>Note: Then the time limit is reached and the game is tie, special 119 * game modes might be turned on, e.g. <i>sudden death overtime</i>. 120 * Depends on the game type and game settings. 121 * 122 * @return Time limit of the game. 123 * 124 * @see getRemainingTime() 125 */ 126 public Double getTimeLimit() 127 { 128 // retreive from GameInfo object 129 if (lastGameInfo == null) return null; 130 return lastGameInfo.getTimeLimit(); 131 } 132 133 /** 134 * Retreives time remaining for the game. 135 * 136 * <p>Note: Then the time limit is reached and the game is tie, special 137 * game modes might be turned on, e.g. <i>sudden death overtime</i>. 138 * Depends on the game type and game settings. 139 * 140 * @return Time limit of the game. 141 * 142 * @see getTime() 143 * @see getTimeLimit() 144 * 145 * @todo Test, whether it is correct.. 146 */ 147 public Double getRemainingTime() 148 { 149 // derive from the time limit and current time 150 if (getTimeLimit() == null) return null; 151 return getTimeLimit() - getTime(); 152 } 153 154 /*========================================================================*/ 155 156 /** 157 * <i>BotDeathMatch only:</i><p> 158 * Number of points (e.g. kills) needed to win the game. 159 * 160 * @return Frag limit of the game. 161 * 162 * @see getTeamScoreLimit() 163 */ 164 public Integer getFragLimit() 165 { 166 // retreive from GameInfo object 167 if (lastGameInfo == null) return null; 168 return lastGameInfo.getFragLimit(); 169 } 170 171 /** 172 * <i>BotTeamGame, BotCTFGame, BotBombingRun, BotDoubleDomination only:</i><p> 173 * Number of points a team needs to win the game. 174 * 175 * @return Team score limit of the game. 176 * 177 * @see getFragLimit() 178 */ 179 public Integer getTeamScoreLimit() 180 { 181 // retreive from GameInfo object 182 // we have to cast double to int because UT2004 exports it as double (duuno why) 183 if (lastGameInfo == null) return null; 184 return (int)lastGameInfo.getGoalTeamScore(); 185 } 186 187 /*========================================================================*/ 188 189 /** 190 * <i>BotTeamGame, BotCTFGame, BotDoubleDomination only:</i><p> 191 * Retrieves number of teams in the game. 192 * 193 * <p> Team numbers start from 0. Usually, there are just two teams: 0 and 1. 194 * 195 * @return Number of teams in the game. 196 */ 197 public Integer getMaxTeams() 198 { 199 // retreive from GameInfo object 200 if (lastGameInfo == null) return null; 201 return lastGameInfo.getMaxTeams(); 202 } 203 204 /** 205 * <i>BotTeamGame, BotCTFGame, BotDoubleDomination only:</i><p> 206 * Retreives maximum number of players per team. 207 * 208 * @return Maximum number of players per team. 209 */ 210 public Integer getMaxTeamSize() 211 { 212 // retreive from GameInfo object 213 if (lastGameInfo == null) return null; 214 return lastGameInfo.getMaxTeamSize(); 215 } 216 217 /*========================================================================*/ 218 219 /** 220 * Retreives starting level of health. This is the level of health the 221 * players spawn with into the game. 222 * 223 * @return Starting level of health. 224 * 225 * @see getMaxHealth() 226 * @see getFullHealth() 227 */ 228 public Integer getStartHealth() 229 { 230 // retreive from InitedMessage object 231 if (lastInitedMessage == null) return null; 232 return lastInitedMessage.getHealthStart(); 233 } 234 235 /** 236 * Retreives maximum level of <i>non-boosted</i> health. This is the level 237 * achievable by foraging standard health kits. 238 * 239 * @return Maximum level of <i>non-boosted</i> health. 240 * 241 * @see getStartHealth() 242 * @see getMaxHealth() 243 */ 244 public Integer getFullHealth() 245 { 246 // retreive from InitedMessage object 247 if (lastInitedMessage == null) return null; 248 return lastInitedMessage.getHealthFull(); 249 } 250 251 /** 252 * Retreives maximum level of <i>boosted</i> health. This is the total 253 * maximum health achievable by any means of health kits, super health, 254 * or health vials. 255 * 256 * @return Maximum level of <i>boosted</i> health. 257 * 258 * @see getStartHealth() 259 * @see getFullHealth() 260 */ 261 public Integer getMaxHealth() 262 { 263 // retreive from InitedMessage object 264 if (lastInitedMessage == null) return null; 265 return lastInitedMessage.getHealthMax(); 266 } 267 268 /*========================================================================*/ 269 270 /** 271 * Retreives maximum level of combined armor. The armor consist of two 272 * parts, which are summed together into combined armor value. However, 273 * each part is powered-up by different item (either by <i>small shield</i> 274 * or by <i>super-shield</i>). 275 * 276 * @return Maximum level of combined armor. 277 * 278 * @see getMaxLowArmor() 279 * @see getMaxHighArmor() 280 */ 281 public Integer getMaxArmor() 282 { 283 // retreive from InitedMessage object 284 if (lastInitedMessage == null) return null; 285 return lastInitedMessage.getShieldStrengthMax(); 286 } 287 288 /** 289 * Retreives maximum level of low armor. The armor consist of two 290 * parts, which are summed together into combined armor value. However, 291 * each part is powered-up by different item (either by <i>small shield</i> 292 * or by <i>super-shield</i>). 293 * 294 * <p>Low armor is powered-up by <i>small shield</i>. 295 * 296 * @return Maximum level of low armor. 297 * 298 * @see getMaxArmor() 299 * @see getMaxHighArmor() 300 */ 301 public int getMaxLowArmor() 302 { 303 // FIXME[js]: Where do we retreive the max low-armor info? 304 return 50; 305 } 306 307 /** 308 * Retreives maximum level of high armor. The armor consist of two 309 * parts, which are summed together into combined armor value. However, 310 * each part is powered-up by different item (either by <i>small shield</i> 311 * or by <i>super-shield</i>). 312 * 313 * <p>High armor is powered-up by <i>super-shield</i>. 314 * 315 * @return Maximum level of high armor. 316 * 317 * @see getMaxArmor() 318 * @see getMaxLowArmor() 319 */ 320 public int getMaxHighArmor() 321 { 322 // FIXME[js]: Where do we retreive the max high-armor info? 323 return 100; 324 } 325 326 /*========================================================================*/ 327 328 /** 329 * Retreives starting level of adrenaline. This is the level of adrenaline 330 * the players spawn with into the game. 331 * 332 * @return Starting level of adrenaline. 333 */ 334 public Integer getStartAdrenaline() 335 { 336 // retreive from InitedMessage object 337 // ut2004 exports it as double, must cast to int, ut's weirdness 338 if (lastInitedMessage == null) return null; 339 return (int)lastInitedMessage.getAdrenalineStart(); 340 } 341 342 /** 343 * Retreives target level of adrenaline that need to be gained to start 344 * special bonus actions. 345 * 346 * <p>Once the agent's adrenaline reaches this designated level, it can be 347 * used to start special bonus booster-actions like <i>invisibility</i>, 348 * <i>speed</i>, <i>booster</i>, etc. The adrenaline is then spent on the 349 * invoked action. 350 * 351 * @return Maximum level of adrenaline that can be gained. 352 */ 353 public Integer getTargetAdrenaline() 354 { 355 // retreive from InitedMessage object 356 // ut2004 exports it as double, must cast to int, ut's weirdness 357 if (lastInitedMessage == null) return null; 358 return (int)lastInitedMessage.getAdrenalineMax(); 359 } 360 361 /** 362 * Retreives maximum level of adrenaline that can be gained. 363 * 364 * @return Maximum level of adrenaline that can be gained. 365 */ 366 public Integer getMaxAdrenaline() 367 { 368 // retreive from InitedMessage object 369 // FIXME[js]: Return type! 370 if (lastInitedMessage == null) return null; 371 return (int)lastInitedMessage.getAdrenalineMax(); 372 } 373 374 /*========================================================================*/ 375 376 /** 377 * Tells, whether the weapons stay on pick-up points, even when they are 378 * picked-up by players. 379 * 380 * <p>If so, each weapon type can be picked up from pick-up points only 381 * once. If the player already has the weapon the pick-up point offers, he 382 * can not pick it up. Also, each weapon pick-up point always contains its 383 * associated weapon. 384 * 385 * <p>If not, weapons can be picked up from pick-up points repeatedly. 386 * If the player already has the weapon the pick-up point offers, the 387 * pick-up will simply replenish ammo for that weapon. Also, upon each 388 * pick-up by a player, the offered weapon disappears (it is "taken" by 389 * that player). Weapons respawn on empty pick-up points after a while. 390 * 391 * @return True, if weapons stay on pick-up points. 392 */ 393 public Boolean getWeaponsStay() 394 { 395 // retreive from GameInfo object 396 if (lastGameInfo == null) return null; 397 return lastGameInfo.isWeaponStay(); 398 } 399 400 /*========================================================================*/ 401 402 /** 403 * Retreives the maximum number of multi-jumping combos. 404 * 405 * <p>Note: Multi-jump combos are currently limited to double-jumps for 406 * bots. 407 * 408 * @return Maximum number of multi-jumping combos. 409 */ 410 public Integer getMaxMultiJump() 411 { 412 // retreive from InitedMessage object 413 if (lastInitedMessage == null) return null; 414 return lastInitedMessage.getMaxMultiJump(); 415 } 416 417 /*========================================================================*/ 418 419 /** 420 * Returns list of mutators that are active in the current game. 421 * 422 * @return Current game's mutators 423 */ 424 public List<Mutator> getMutators() 425 { 426 if (lastMutatorListObtained == null) return null; 427 return lastMutatorListObtained.getMutators(); 428 } 429 430 /*========================================================================*/ 431 432 /** 433 * Tells, whether the game is paused or running. When the game is paused, 434 * nobody can move or do anything (usually except posting text messages). 435 * 436 * @return True, if the game is paused. False otherwise. 437 * 438 * @see areBotsPaused() 439 */ 440 public Boolean isPaused() 441 { 442 // retreive from GameInfo object 443 if (lastGameInfo == null) return null; 444 return lastGameInfo.isGamePaused(); 445 } 446 447 /** 448 * Tells, whether the bots are paused or running. When the bots are paused, 449 * but the game is not paused as well, human controlled players can move. 450 * The bots are standing still and can do nothing (usually except posting 451 * text messages). 452 * 453 * @return True, if the bots are paused. False otherwise. 454 * 455 * @see isPaused() 456 */ 457 public Boolean isBotsPaused() 458 { 459 // retreive from GameInfo object 460 if (lastGameInfo == null) return null; 461 return lastGameInfo.isBotsPaused(); 462 } 463 464 /** 465 * Returns a map indexed by team numbers, holding all flags in the game. 466 * In non-Capture the Flag (CTF) gametypes the result map will be empty. 467 * 468 * @return Map containing all the flags in the game indexed by owner team number. 469 */ 470 public Map<Integer, FlagInfo> getAllCTFFlags() 471 { 472 return allCTFFlags; 473 } 474 475 /** 476 * Returns a collection of all the flags in the game. 477 * In non-Capture the Flag (CTF) gametypes the result collection will be empty. 478 * 479 * @return Collection containing all the flags in the game. 480 */ 481 public Collection<FlagInfo> getAllCTFFlagsCollection() 482 { 483 return allCTFFlags.values(); 484 } 485 486 /*========================================================================*/ 487 488 /** Most rescent message containing info about the game. */ 489 GameInfo lastGameInfo = null; 490 491 /** Most rescent message containing info about the game frame. */ 492 InitedMessage lastInitedMessage = null; 493 494 /** Most rescent message containing info about the game frame. */ 495 BeginMessage lastBeginMessage = null; 496 497 /** Most recent info about game's mutators. */ 498 MutatorListObtained lastMutatorListObtained = null; 499 500 /** All flags in the game - will be filled only in CTF games */ 501 Map<Integer, FlagInfo> allCTFFlags = new HashMap(); 502 503 /*========================================================================*/ 504 505 /** 506 * GameInfo listener. 507 */ 508 private class GameInfoListener implements IWorldObjectEventListener<GameInfo, IWorldObjectEvent<GameInfo>> 509 { 510 @Override 511 public void notify(IWorldObjectEvent<GameInfo> event) 512 { 513 lastGameInfo = event.getObject(); 514 } 515 516 /** 517 * Constructor. Registers itself on the given WorldView object. 518 * @param worldView WorldView object to listent to. 519 */ 520 public GameInfoListener(IWorldView worldView) 521 { 522 worldView.addObjectListener(GameInfo.class, this); 523 } 524 } 525 526 /** GameInfo listener */ 527 GameInfoListener gameInfoListener; 528 529 /*========================================================================*/ 530 531 /** 532 * InitedMessage listener. 533 */ 534 private class InitedMessageListener implements IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>> 535 { 536 @Override 537 public void notify(WorldObjectUpdatedEvent<InitedMessage> event) 538 { 539 lastInitedMessage = event.getObject(); 540 } 541 542 /** 543 * Constructor. Registers itself on the given WorldView object. 544 * @param worldView WorldView object to listent to. 545 */ 546 public InitedMessageListener(IWorldView worldView) 547 { 548 worldView.addObjectListener(InitedMessage.class, WorldObjectUpdatedEvent.class, this); 549 } 550 } 551 552 /** InitedMessage listener */ 553 InitedMessageListener initedMessageListener; 554 555 /*========================================================================*/ 556 557 /** 558 * BeginMessage listener. 559 */ 560 private class BeginMessageListener implements IWorldEventListener<BeginMessage> 561 { 562 @Override 563 public void notify(BeginMessage event) 564 { 565 lastBeginMessage = event; 566 } 567 568 /** 569 * Constructor. Registers itself on the given WorldView object. 570 * @param worldView WorldView object to listent to. 571 */ 572 public BeginMessageListener(IWorldView worldView) 573 { 574 worldView.addEventListener(BeginMessage.class, this); 575 } 576 } 577 578 /** BeginMessage listener */ 579 BeginMessageListener beginMessageListener; 580 581 /*========================================================================*/ 582 583 /** 584 * MutatorListObtained listener. 585 */ 586 private class MutatorListObtainedListener implements IWorldEventListener<MutatorListObtained> 587 { 588 @Override 589 public void notify(MutatorListObtained event) 590 { 591 lastMutatorListObtained = event; 592 } 593 594 /** 595 * Constructor. Registers itself on the given WorldView object. 596 * @param worldView WorldView object to listent to. 597 */ 598 public MutatorListObtainedListener(IWorldView worldView) 599 { 600 worldView.addEventListener(MutatorListObtained.class, this); 601 } 602 } 603 604 /** MutatorListObtained listener */ 605 MutatorListObtainedListener mutatorListObtainedListener; 606 607 /*========================================================================*/ 608 609 /** 610 * FlagInfo object listener. 611 */ 612 private class FlagInfoObjectListener implements IWorldObjectEventListener<FlagInfo,WorldObjectFirstEncounteredEvent<FlagInfo>> 613 { 614 /** 615 * Save flag in our HashMap. 616 * 617 * @param event 618 */ 619 public void notify(WorldObjectFirstEncounteredEvent<FlagInfo> event) 620 { 621 if (allCTFFlags.containsKey(event.getObject().getTeam())) 622 if (log.isLoggable(Level.WARNING)) log.warning("Saving second Flag for team: " + event.getObject().getTeam() + ". Bug?"); 623 624 allCTFFlags.put(event.getObject().getTeam(), event.getObject()); 625 } 626 627 /** 628 * Constructor. Registers itself on the given WorldView object. 629 * @param worldView WorldView object to listent to. 630 */ 631 public FlagInfoObjectListener(IWorldView worldView) 632 { 633 worldView.addObjectListener(FlagInfo.class, WorldObjectFirstEncounteredEvent.class, this); 634 } 635 } 636 637 /** FlagInfo object listener */ 638 FlagInfoObjectListener flagInfoObjectListener; 639 640 /*========================================================================*/ 641 642 /** 643 * Constructor. Setups the memory module based on bot's world view. 644 * @param bot owner of the module that is using it 645 */ 646 public Game(UDKBot bot) { 647 this(bot, null); 648 } 649 650 /** 651 * Constructor. Setups the memory module based on bot's world view. 652 * @param bot owner of the module that is using it 653 * @param log Logger to be used for logging runtime/debug info. If <i>null</i>, the module creates its own logger. 654 */ 655 public Game(UDKBot bot, Logger log) 656 { 657 super(bot, log); 658 659 // create listeners 660 gameInfoListener = new GameInfoListener(worldView); 661 beginMessageListener = new BeginMessageListener(worldView); 662 initedMessageListener = new InitedMessageListener(worldView); 663 mutatorListObtainedListener = new MutatorListObtainedListener(worldView); 664 flagInfoObjectListener = new FlagInfoObjectListener(worldView); 665 } 666 667 /** 668 * Provides initialization of the module (clearing internal data structures). Called automatically 669 * during the agent starting sequence. 670 */ 671 @Override 672 protected void start(boolean startPaused) { 673 super.start(startPaused); 674 lastGameInfo = null; 675 lastInitedMessage = null; 676 lastBeginMessage = null; 677 lastMutatorListObtained = null; 678 } 679 }