1 package cz.cuni.amis.pogamut.udk.agent.module.sensor; 2 3 import java.util.logging.Logger; 4 5 import cz.cuni.amis.pogamut.base.agent.module.SensorModule; 6 import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView; 7 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener; 8 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEvent; 9 import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener; 10 import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectFirstEncounteredEvent; 11 import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent; 12 import cz.cuni.amis.pogamut.base.utils.math.DistanceUtils; 13 import cz.cuni.amis.pogamut.base3d.worldview.object.ILocated; 14 import cz.cuni.amis.pogamut.base3d.worldview.object.Location; 15 import cz.cuni.amis.pogamut.base3d.worldview.object.Rotation; 16 import cz.cuni.amis.pogamut.base3d.worldview.object.Velocity; 17 import cz.cuni.amis.pogamut.udk.bot.IUDKBotController; 18 import cz.cuni.amis.pogamut.udk.bot.impl.UDKBot; 19 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.BotKilled; 20 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.ConfigChange; 21 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.InitedMessage; 22 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Item; 23 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.NavPoint; 24 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Player; 25 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.PlayerKilled; 26 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.PlayerScore; 27 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Self; 28 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.TeamScore; 29 import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.VolumeChanged; 30 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId; 31 import cz.cuni.amis.utils.NullCheck; 32 33 /** 34 * Memory module specialized on general info about the agent whereabouts. 35 * <p><p> 36 * It is designed to be initialized inside {@link IUDKBotController#prepareBot(UDKBot)} method call 37 * and may be used since first {@link Self} message is received, i.e, since the first {@link IUDKBotController#botSpawned(cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.GameInfo, ConfigChange, InitedMessage, Self)} 38 * is called. 39 * 40 * @author Juraj 'Loque' Simlovic 41 * @author Jimmy 42 */ 43 public class AgentInfo extends SensorModule<UDKBot> 44 { 45 public static final String NONE_WEAPON_ID = "None"; 46 47 /** 48 * Retreives a unique ID of the agent in the game. 49 * 50 * <p>Note: This ID does not change and can be relied upon during entire 51 * match. However, be aware that the ID may change between different matches 52 * and/or sessions. 53 * 54 * @return ID of the agent in the game. 55 */ 56 public UnrealId getId() 57 { 58 // retreive from self object 59 if (self == null) return null; 60 return self.getId(); 61 } 62 63 /** 64 * Retreives current name of the agent in the game. 65 * 66 * <p>Note: The agent may choose and change it's name during a match and it 67 * does not need to be unique among players. Even an empty string might be 68 * a valid name. 69 * 70 * @return Name of the agent in the game. 71 */ 72 public String getName() 73 { 74 // retreive from self object 75 if (self == null) return null; 76 return self.getName(); 77 } 78 79 /*========================================================================*/ 80 81 /** Red team number. */ 82 public static final int TEAM_RED = 0; 83 /** Blue team number. */ 84 public static final int TEAM_BLUE = 1; 85 /** Green team number. */ 86 public static final int TEAM_GREEN = 2; 87 /** Gold team number. */ 88 public static final int TEAM_GOLD = 3; 89 /** No-team number. */ 90 public static final int TEAM_NONE = 255; 91 92 /** 93 * Retreives team number the agent is on. 94 * 95 * @return Team number the player is on. 96 * 97 * @see #TEAM_RED 98 * @see #TEAM_BLUE 99 * @see #TEAM_GREEN 100 * @see #TEAM_GOLD 101 * @see #TEAM_NONE 102 * 103 * @see isEnemy(int) 104 * @see isEnemy(Player) 105 * @see isFriend(int) 106 * @see isFriend(Player) 107 */ 108 public Integer getTeam() 109 { 110 // retreive from self object 111 if (self == null) return null; 112 return self.getTeam(); 113 } 114 115 /** 116 * Tells, whether a given team is an enemy team to the agent. 117 * 118 * @param team Team number to be tested. 119 * @return True, if the given team is an enemy team. 120 * 121 * @see getTeam() 122 * @see isFriend(int) 123 */ 124 public boolean isEnemy(int team) 125 { 126 // freelancers' team or different team 127 return (team == TEAM_NONE) || (team != getTeam()); 128 } 129 130 /** 131 * Tells, whether a given player is an enemy to the agent. 132 * 133 * @param player Player to be tested. 134 * @return True, if the given player is an enemy. 135 * 136 * @see getTeam() 137 * @see isFriend(Player) 138 */ 139 public boolean isEnemy(Player player) 140 { 141 // test the enemy team number 142 return isEnemy(player.getTeam()); 143 } 144 145 /** 146 * Tells, whether a given team is a friend team to the agent. 147 * 148 * @param team Team number to be tested. 149 * @return True, if the given team is a friend team. 150 * 151 * @see getTeam() 152 * @see isEnemy(int) 153 */ 154 public boolean isFriend(int team) 155 { 156 // same team only 157 return (team == getTeam()); 158 } 159 160 /** 161 * Tells, whether a given player is a friend to the agent. 162 * 163 * @param player Player to be tested. 164 * @return True, if the given player is a friend. 165 * 166 * @see getTeam() 167 * @see isEnemy(Player) 168 */ 169 public boolean isFriend(Player player) 170 { 171 // test the friend team number 172 return isFriend(player.getTeam()); 173 } 174 175 /*========================================================================*/ 176 177 /** 178 * Which distance to a location is considered the same as specified location. Note 179 * that UT units are rather small. 180 */ 181 public static final double AT_LOCATION_EPSILON = 120; 182 183 /** 184 * What angle is considered to be maximum facing angle by default (in degrees). 185 */ 186 public static final double IS_FACING_ANGLE = 6; 187 188 /** 189 * Retreives absolute location of the agent within the map. 190 * 191 * @return Location of the agent within the map. 192 * 193 * @see getDistance(Location) 194 * @see Location#getDistance(Location) 195 * @see Location#getDistanceL1(Location) 196 * @see Location#getDistanceLinf(Location) 197 * @see Location#getDistancePlane(Location) 198 * @see Location#getDistanceSquare(Location) 199 */ 200 public Location getLocation() 201 { 202 // retreive from self object 203 if (self == null) return null; 204 return self.getLocation(); 205 } 206 207 public boolean atLocation(Location location) { 208 return atLocation(location, AT_LOCATION_EPSILON); 209 } 210 211 public boolean atLocation(Location location, double epsilon) { 212 if (location == null || getLocation() == null) return false; 213 return getLocation().getPoint3d().distance(location.getPoint3d()) < epsilon; 214 } 215 216 /** 217 * Computes crow-fly distance of the agent from given location. 218 * 219 * @param location Location within the map. 220 * @return Crow-fly distance of the agent and the location. 221 * 222 * @see getLocation() 223 */ 224 public Double getDistance(Location location) 225 { 226 // retreive from self object 227 if (self == null) return null; 228 return self.getLocation().getDistance(location); 229 } 230 231 /*========================================================================*/ 232 233 /** 234 * Retreives absolute rotation of the agent within the map. 235 * 236 * @return Rotation of the agent within the map. 237 */ 238 public Rotation getRotation() 239 { 240 // retreive from self object 241 if (self == null) return null; 242 return self.getRotation(); 243 } 244 245 /*========================================================================*/ 246 247 /** 248 * Retreives current velocity of the agent as a vector of movement. 249 * 250 * @return Current velocity of the agent in the map. 251 * 252 * @see isMoving() 253 */ 254 public Velocity getVelocity() 255 { 256 // retreive from self object 257 if (self == null) return null; 258 return self.getVelocity(); 259 } 260 261 /** 262 * Tells, whether the agent is moving. The agent is moving, when his 263 * actual velocity is non-zero. 264 * 265 * @return True, if the agent is moving. 266 * 267 * @see getVelocity() 268 */ 269 public Boolean isMoving() 270 { 271 // check the size of the velocity 272 if (getVelocity() == null) return null; 273 return !getVelocity().isZero(); 274 } 275 276 /*========================================================================*/ 277 278 /** 279 * Tells, whether the agent is crouched. When crouched, the height of the 280 * agent is smaller and thus harder to spot/hit. 281 * 282 * @return True, if the agent is crouched. 283 */ 284 public Boolean isCrouched() 285 { 286 // retreive from self object 287 if (self == null) return null; 288 return self.isCrouched(); 289 } 290 291 /** 292 * Tells, whether the agent is walking. When walking, the agent does not 293 * fall off the edges before notification about such edge can be sent to 294 * the agent. The agent's movement is, however, much slower. 295 * 296 * @return True, if the agent is walking. 297 */ 298 public Boolean isWalking() 299 { 300 // retreive from self object 301 if (self == null) return null; 302 return self.isWalking(); 303 } 304 /*========================================================================*/ 305 306 /** 307 * Tells if the agent is currently facing input location. 308 * 309 * @param location input location. 310 * @return True, if the bot is facing input location. 311 */ 312 public Boolean isFacing(ILocated location) 313 { 314 if (location == null || getRotation() == null) return null; 315 Location directionVector = location.getLocation().sub(this.getLocation()).getNormalized(); 316 Location agentFaceVector = this.getRotation().toLocation().getNormalized(); 317 318 if (Math.acos(directionVector.dot(agentFaceVector)) <= Math.toRadians(IS_FACING_ANGLE)) 319 return true; 320 321 return false; 322 } 323 324 325 /** 326 * Tells if the agent is currently facing input location. 327 * 328 * @param location input location. 329 * @param angle specifies maximum angle (in degrees) that will be still considered as facing angle. 330 * @return True, if the angle between agent facing vector and input location is smaller or equal to input angle. 331 */ 332 public Boolean isFacing(ILocated location, double angle) 333 { 334 if (location == null || getRotation() == null) return null; 335 Location directionVector = location.getLocation().sub(this.getLocation()).getNormalized(); 336 Location agentFaceVector = this.getRotation().toLocation().getNormalized(); 337 338 if (Math.acos(directionVector.dot(agentFaceVector)) <= Math.toRadians(angle)) 339 return true; 340 341 return false; 342 } 343 344 /*========================================================================*/ 345 346 /** 347 * Retreives location of the nearest map geometry directly beneath the 348 * agent. This can be used to determine how far the agent is above the 349 * ground, etc. 350 * 351 * @return Location of <i>the ground</i> beneath the agent. 352 */ 353 public Location getFloorLocation() 354 { 355 if (self == null) return null; 356 // retreive from self object 357 return self.getFloorLocation(); 358 } 359 360 /** 361 * Tells, whether the agent is currently touching the groud with his feets. 362 * When not touching ground, the agent might be either jumping, or falling, 363 * or hanging from a ledge, or otherwise flying above the ground. 364 * 365 * @return True, if the agent is touching ground with his feets. 366 */ 367 public Boolean isTouchingGround() 368 { 369 // compare locations of agent and floor (beware of being crouched) 370 // FIXME[jimmy]: Test the values.. 371 if (getLocation() == null || getFloorLocation() == null) return null; 372 return (getLocation().z - getFloorLocation().z) 373 < (isCrouched() ? 50 : 80); 374 } 375 376 /*========================================================================*/ 377 378 /** 379 * Tells whether the agent has the damage multiplier (UDamage) bonus boost 380 * activated and how long will the UDamage boost remain active. 381 * 382 * <p>When UDamage is activated, the agent is causing double (or tripple, 383 * or even more) damage to other players. The multiplying factor depends 384 * on game settings and mutators. 385 * 386 * @return Time remaining for UDamage bonus boost. When this value is 387 * positive, the agent has the UDamage bonus boost currently activated. 388 * When this value is negative, the agent does not have UDamage activated. 389 * 390 * @see hasUDamage() 391 */ 392 public Double getRemainingUDamageTime() 393 { 394 // calculate remaining time by substracting current time 395 if (self == null) return null; 396 return self.getUDamageTime() - getTime(); 397 } 398 399 /** 400 * Tells whether the agent has the damage multiplier (UDamage) bonus boost 401 * activated. 402 * 403 * <p>When UDamage is activated, the agent is causing double (or tripple, 404 * or even more) damage to other players. The multiplying factor depends 405 * on game settings and mutators. 406 * 407 * @return True, if the agent has damage multiplier bonus action activated. 408 * 409 * @see getRemainingUDamageTime() 410 */ 411 public Boolean hasUDamage() 412 { 413 if (getRemainingUDamageTime() == null) return null; 414 // is there any remaining time? 415 return getRemainingUDamageTime() > 0; 416 } 417 418 /*========================================================================*/ 419 420 /** 421 * Tells, how much health the agent has. 422 * 423 * <p>The health usually starts at 100, and ranges from 0 to 199. These 424 * values, however, can be changed by various mutators. 425 * 426 * @return Current health status. 427 * 428 * @see isHealthy() 429 * @see isSuperHealthy() 430 */ 431 public Integer getHealth() 432 { 433 // retreive from self object 434 if (self == null) return null; 435 return self.getHealth(); 436 } 437 438 /** 439 * Tells, whether the agent is healthy, i.e. not wounded. 440 * 441 * @return True, if the agent has at least standard amount of health. 442 * 443 * @see getHealth() 444 * @see isSuperHealthy() 445 */ 446 public Boolean isHealthy() 447 { 448 // compare self object and game info 449 if (getHealth() == null || game == null) return null; 450 return (getHealth() >= game.getFullHealth()); 451 } 452 453 /** 454 * Tells, whether the agent is healthy to the maximum boostable extent. 455 * 456 * @return True, if the agent has maximum amount of health. 457 * 458 * @see getHealth() 459 * @see isHealthy() 460 */ 461 public Boolean isSuperHealthy() 462 { 463 // compare self object and game info 464 if (getHealth() == null || game == null) return null; 465 return (getHealth() >= game.getMaxHealth()); 466 } 467 468 /*========================================================================*/ 469 470 /** 471 * Tells, how much of combined armor the agent is wearing. 472 * 473 * <p>The combined armor usually starts at 0, and ranges from 0 to 150. 474 * These values, however, can be changed by various mutators. 475 * 476 * <p>Note: The armor consist of two parts, which are summed together into 477 * combined armor value. However, each part is powered-up by different item 478 * (low armor by <i>small shield</i>; high armor by <i>super-shield</i>). 479 * 480 * @return Current armor status. 481 * 482 * @see hasArmor() 483 * @see getLowArmor() 484 * @see getHighArmor() 485 */ 486 public Integer getArmor() 487 { 488 // retreive from self object 489 if (self == null) return null; 490 return self.getArmor(); 491 } 492 493 /** 494 * Tells, whether the agent is armored to the maximum extent. 495 * 496 * @return True, if the agent has maximum amount of armor. 497 * 498 * @see getArmor() 499 * @see hasLowArmor() 500 * @see hasHighArmor() 501 */ 502 public Boolean hasArmor() 503 { 504 // compare self object and game info 505 if (getArmor() == null) return null; 506 return (getArmor() >= game.getMaxArmor()); 507 } 508 509 /** 510 * Tells, how much of low armor the agent is wearing. 511 * 512 * <p>The low armor usually starts at 0, and ranges from 0 to 50. 513 * These values, however, can be changed by various mutators. 514 * 515 * <p>Note: The armor consist of two parts, which are summed together into 516 * combined armor value. However, each part is powered-up by different item 517 * (low armor by <i>small shield</i>; high armor by <i>super-shield</i>). 518 * 519 * @return Current low armor status. 520 * 521 * @see hasLowArmor() 522 * @see getArmor() 523 * @see getHighArmor() 524 */ 525 public Integer getLowArmor() 526 { 527 // retreive from self object 528 if (self == null) return null; 529 return self.getSmallArmor(); 530 } 531 532 /** 533 * Tells, whether the agent is armored to the maximum of low-armor extent. 534 * 535 * @return True, if the agent has maximum amount of low-armor. 536 * 537 * @see getLowArmor() 538 * @see hasArmor() 539 * @see hasHighArmor() 540 */ 541 public Boolean hasLowArmor() 542 { 543 // compare self object and game info 544 if (getLowArmor() == null || game == null) return null; 545 return (getLowArmor() >= game.getMaxLowArmor()); 546 } 547 548 /** 549 * Tells, how much of high armor the agent is wearing. 550 * 551 * <p>The high armor usually starts at 0, and ranges from 0 to 100. 552 * These values, however, can be changed by various mutators. 553 * 554 * <p>Note: The armor consist of two parts, which are summed together into 555 * combined armor value. However, each part is powered-up by different item 556 * (low armor by <i>small shield</i>; high armor by <i>super-shield</i>). 557 * 558 * @return Current high armor status. 559 * 560 * @see hasHighArmor() 561 * @see getArmor() 562 * @see getLowArmor() 563 */ 564 public Integer getHighArmor() 565 { 566 // calculate from armor and small armor in self object 567 if (self == null) return null; 568 return self.getArmor() - self.getSmallArmor(); 569 } 570 571 /** 572 * Tells, whether the agent is armored to the maximum of high-armor extent. 573 * 574 * @return True, if the agent has maximum amount of high-armor. 575 * 576 * @see getHighArmor() 577 * @see hasArmor() 578 * @see hasLowArmor() 579 */ 580 public Boolean hasHighArmor() 581 { 582 // compare self object and game info 583 if (getHighArmor() == null) return null; 584 return (getHighArmor() >= game.getMaxHighArmor()); 585 } 586 587 /*========================================================================*/ 588 589 /** 590 * Retreives UnrealId of the weapon the agent is currently holding. This 591 * UnrealId is a unique identifier of weapon from the agent's inventory. 592 * Note that this UnrealId is different from UnrealId of item the agent 593 * seen or picked up from the ground earlier. 594 * 595 * <p>The UnrealId might contains a substring, which identifies the type 596 * of the weapon. However, this is not guaranteed by definition. Therefore, 597 * you shoud use inventory to retreive the appropriate weapon object, to 598 * further retreive correct type of weapon. 599 * 600 * @return UnrealId of the weapon the agent is currently holding in hands. 601 * 602 * @see getCurrentAmmo() 603 * @see getCurrentSecondaryAmmo() 604 * @see Inventory#getCurrentWeapon() 605 * @see Inventory#getWeapon(UnrealId) 606 */ 607 public UnrealId getCurrentWeapon() 608 { 609 // retreive from self object 610 if (self == null) return null; 611 return self.getWeapon(); 612 } 613 614 /** 615 * Tells whether the bot is holding some weapon or not. 616 * <p><p> 617 * Note that {@link AgentInfo#getCurrentWeapon()} always returns some id. But there is a special id that marks 'no weapon' 618 * @return 619 */ 620 public Boolean hasWeapon() { 621 if (self == null) return null; 622 return !self.getWeapon().getStringId().equalsIgnoreCase(NONE_WEAPON_ID); 623 } 624 625 /** 626 * Tells, how much ammunition the agent has left for the current weapon 627 * in its primary firing mode. 628 * 629 * @return Amount of ammunition for the primary firing mode. 630 * 631 * @see getCurrentSecondaryAmmo() 632 * @see Inventory#getCurrentPrimaryAmmo() 633 */ 634 public Integer getCurrentAmmo() 635 { 636 // retreive from self object 637 if (self == null) return null; 638 return self.getPrimaryAmmo(); 639 } 640 641 /** 642 * Tells, how much ammunition the agent has left for the current weapon 643 * in its alternate (secondary) firing mode. Note that many weapons use primary ammo 644 * for the alternate (secondary) firing mode as well. In such cases, the amount of 645 * ammo for primary mode is returned. 646 * 647 * @return Amount of ammunition for the secondary firing mode. 648 * 649 * @see getCurrentAmmo() 650 * @see Inventory#getCurrentSecondaryAmmo() 651 */ 652 public Integer getCurrentSecondaryAmmo() 653 { 654 // retreive from self object 655 if (self == null) return null; 656 return self.getSecondaryAmmo(); 657 } 658 659 /*========================================================================*/ 660 661 /** 662 * Tells, whether the agent is shooting or not. 663 * 664 * <p>This method reports shooting with either primary or secondary fire 665 * mode. To distinguish between the fire modes, see {@link isPriShooting()}, 666 * {@link isAltShooting()}. 667 * 668 * @return Returns true, if the agent is shooting his weapon. 669 * 670 * @see isPrimaryShooting() 671 * @see isSecondaryShooting() 672 */ 673 public Boolean isShooting() 674 { 675 // retreive from self object 676 if (self == null) return null; 677 return self.isShooting(); 678 } 679 680 /** 681 * Tells, whether the agent is shooting with primary fire mode. 682 * 683 * <p>This method reports shooting with primary fire mode only. See 684 * {@link isAltShooting()} method to determine, whether the agent shoots 685 * with alternate firing mode. See {@link isShooting()} to determine, 686 * whether the agent shoots with either primary or alternate firing mode. 687 * 688 * @return True, if the agent is shooting weapon in primary firing mode. 689 * 690 * @see isShooting() 691 * @see isSecondaryShooting() 692 */ 693 public Boolean isPrimaryShooting() 694 { 695 // shooting but not in altrenate fire mode 696 if (self == null) return null; 697 return isShooting() && !isSecondaryShooting(); 698 } 699 700 /** 701 * Tells, whether the agent is shooting with alternate (secondary) fire mode. 702 * 703 * <p>This method reports shooting with alternate (secondary) fire mode only. See 704 * {@link isPriShooting()} method to determine, whether the agent shoots 705 * with primary firing mode. See {@link isShooting()} to determine, 706 * whether the agent shoots with either primary or alternate (secondary) firing mode. 707 * 708 * @return True, if the agent is shooting his weapon in alternate (secondary) firing 709 * mode. 710 * 711 * @see isShooting() 712 * @see isPrimaryShooting() 713 */ 714 public Boolean isSecondaryShooting() 715 { 716 // retreive from self object 717 if (self == null) return null; 718 return self.isAltFiring(); 719 } 720 721 /*========================================================================*/ 722 723 /** 724 * Retreives number of kills the agent scored. 725 * 726 * <p>A kill is counted, whenever the agent kills an opponent. 727 * 728 * @return Number of kills the agent scored. 729 */ 730 public int getKills() 731 { 732 // returns number of kills, counted in PlayerKilledListener 733 return kills; 734 } 735 736 /** 737 * Retreives number of deaths the agent took. 738 * 739 * <p>A death is counted, whenever the agent dies. 740 * 741 * @return Number of deaths the agent took. 742 */ 743 public int getDeaths() 744 { 745 // retreive from PlayerScore object 746 return lastPlayerScore.getDeaths(); 747 } 748 749 /** 750 * Retreives number of suicides the agent commited. 751 * 752 * <p>A suicide is counted, whenever the agent dies by his own weapon, or 753 * by damaging himself by falling into pits, lava, acid, etc. 754 * 755 * <p>It can also be said that suicide is every agent's death, which could 756 * not be credited to any other player in the map. 757 * 758 * <p>Each suicide is also counted as death. See {@link getDeaths()}. 759 * 760 * @return Number of suicides the agent commited. 761 */ 762 public int getSuicides() 763 { 764 // returns number of suicides, counted in BotKilledListener 765 return suicides; 766 } 767 768 /** 769 * Retreives current agent score. 770 * 771 * <p>Agent score is usually rising by achieving some goals, e.g. killing 772 * opponents, capturing flags, controlling domination points, etc. Note: 773 * Agent score might decrease upon suicides, based on map, game type and 774 * game settings. 775 * 776 * @return Current agent score. 777 */ 778 public int getScore() 779 { 780 // retreive from PlayerScore object 781 return lastPlayerScore.getScore(); 782 } 783 784 /** 785 * Retreives current agent's team score. 786 * 787 * <p>Agent's team score is usually rising by achieving team goals, e.g. 788 * killing opponents, capturing flags, controlling domination points, etc. 789 * Note: Agent's team score might decrease, when oposing teams score points 790 * themselves, based on map, game type and game settings. 791 * 792 * @return Current agent's team score. 793 */ 794 public int getTeamScore() 795 { 796 if (lastTeamScore == null) return 0; 797 // retreive from TeamScore object 798 return lastTeamScore.getScore(); 799 } 800 801 /*========================================================================*/ 802 803 /** 804 * Pulling velocity in this map zone. Such pulling velocity effectively 805 * draws the player towards a specific direction or even lifts him upwards. 806 * 807 * @return Pulling velocity in this zone. 808 */ 809 public Velocity getCurrentZoneVelocity() 810 { 811 // retreive from VolumeChanged object 812 if (lastVolumeChanged == null) return null; 813 return lastVolumeChanged.getZoneVelocity(); 814 } 815 816 /** 817 * Gravity in this map zone. Gravity might differ throughout different 818 * parts of the map. The gravity is expressed as a velocity vector. This 819 * vector is used an acceleration. The fall speed may ramp up, to as much 820 * as {@link getFallSpeed()}. 821 * 822 * @return Gravity in this zone. 823 */ 824 public Velocity getCurrentZoneGravity() 825 { 826 // retreive from VolumeChanged object 827 if (lastVolumeChanged == null) return null; 828 return lastVolumeChanged.getZoneGravity(); 829 } 830 831 /** 832 * Friction of the floor in this map volume. Friction of the floor works 833 * towards movement, slowing down the acceleration and speed of the agent 834 * in any direction. 835 * 836 * @return Friction of the floor. 837 */ 838 public Double getCurrentVolumeGroundFriction() 839 { 840 // retreive from VolumeChanged object 841 if (lastVolumeChanged == null) return null; 842 return lastVolumeChanged.getGroundFriction(); 843 } 844 845 /** 846 * Friction of the fluid in this map volume. Friction of the fluid works 847 * towards movement, slowing down the acceleration and speed of the agent 848 * in any direction. 849 * 850 * @return Friction of the fluid. 851 */ 852 public Double getCurrentVolumeFluidFriction() 853 { 854 // retreive from VolumeChanged object 855 if (lastVolumeChanged == null) return null; 856 return lastVolumeChanged.getFluidFriction(); 857 } 858 859 /** 860 * FIXME[js]: What the hell is this good for? 861 * 862 * @return TerminalVelocity of the CurrentVolume. 863 */ 864 public Double _getCurrentVolumeTerminalVelocity() 865 { 866 // retreive from VolumeChanged object 867 if (lastVolumeChanged == null) return null; 868 return lastVolumeChanged.getTerminalVelocity(); 869 } 870 871 /** 872 * Tells, whether the current volume is water. When the agent is in water, 873 * {@link getCurrentVolumeFluidFriction()} and {@link getWaterSpeed()} can 874 * help to determine changes to movement and speed of the agent. Also note 875 * that {@link getCurrentZoneVelocity()}, {@link getCurrentZoneGravity()}, 876 * and others may change (and usually does) in water. 877 * 878 * @return True, if the current volume is water. 879 */ 880 public Boolean isCurrentVolumeWater() 881 { 882 // retreive from VolumeChanged object 883 if (lastVolumeChanged == null) return null; 884 return lastVolumeChanged.isWaterVolume(); 885 } 886 887 /** 888 * Tells, whether the current volume is causing damage. Such damage is 889 * applied to the agent's health every second. The amount of damage taken 890 * per each second spent in this volume can be determined by 891 * {@link getCurrentVolumeDamagePerSec()}. When the volume damages the 892 * agent to the death, the death is counted as a suicide. 893 * 894 * @return True, if the current volume is causing damage. 895 * 896 * @see isCurrentVolumeDestructive() 897 * @see getCurrentVolumeDamagePerSec() 898 */ 899 public Boolean isCurrentVolumePainCausing() 900 { 901 // retreive from VolumeChanged object 902 if (lastVolumeChanged == null) return null; 903 return lastVolumeChanged.isPainCausing(); 904 } 905 906 /** 907 * Amount of damage taken for spending time in the current volume. Such 908 * damage is applied to the agent's health every second. When the volume 909 * damages the agent to the death, the death is counted as a suicide. 910 * 911 * @return Amount of damage taken for spending time in the current volume. 912 * 913 * @see isCurrentVolumePainCausing() 914 */ 915 public Double getCurrentVolumeDamagePerSec() 916 { 917 // retreive from VolumeChanged object 918 if (lastVolumeChanged == null) return null; 919 return lastVolumeChanged.getDamagePerSec(); 920 } 921 922 /** 923 * Tells, whether the current volume kills the actors (almost) instantly. 924 * Death in such destructive volume is counted as a suicide. 925 * 926 * @return True, if the current volume kills (almost) instantly. 927 * 928 * @see isCurrentVolumePainCausing() 929 */ 930 public Boolean isCurrentVolumeDestructive() 931 { 932 // retreive from VolumeChanged object 933 if (lastVolumeChanged == null) return null; 934 return lastVolumeChanged.isDestructive(); 935 } 936 937 /** 938 * Retreives type of damage the current volume inflicts to the agent while 939 * he spends time in this volume. 940 * 941 * <p>FIXME[js]: Is is possible to provide an enum here? 942 * 943 * @return Type of the damage the current volume inflicts to the agent. 944 */ 945 public String getCurrentVolumeDamageType() 946 { 947 // retreive from VolumeChanged object 948 if (lastVolumeChanged == null) return null; 949 return lastVolumeChanged.getDamageType(); 950 } 951 952 /** 953 * Tells, whether the current volume (the one the agent is within) forbids 954 * usage of the inventory. If so, no weapons or items can be used, changed, 955 * or picked up. 956 * 957 * @return True, if the current volume forbids usage of the inventory. 958 */ 959 public Boolean isCurrentVolumeBanningInventory() 960 { 961 // retreive from VolumeChanged object 962 if (lastVolumeChanged == null) return null; 963 return lastVolumeChanged.isNoInventory(); 964 } 965 966 /** 967 * Tells, whether the current volume imparts its velocity to projectiles. 968 * E.g. A volume might impart velocity to players to emulate <i>wind</i>. 969 * This settings tells, whether the same applies to projectiles. If so, 970 * Their trajectory will be affected by this volume velocity. 971 * 972 * @return True, if the current volume imparts its velocity to projectiles. 973 */ 974 public Boolean isCurrentVolumeAffectingProjectiles() 975 { 976 // retreive from VolumeChanged object 977 if (lastVolumeChanged == null) return null; 978 return lastVolumeChanged.isMoveProjectiles(); 979 } 980 981 /** 982 * Tells, whether the current zone is a neutral zone. In neutral zone, 983 * players can't take damage. 984 * 985 * @return True, if the current zone is a neutral zone. 986 */ 987 public Boolean isCurrentZoneNeutral() 988 { 989 // retreive from VolumeChanged object 990 if (lastVolumeChanged == null) return null; 991 return lastVolumeChanged.isNeutralZone(); 992 } 993 994 /*========================================================================*/ 995 996 /** 997 * Retreives scaling factor for damage dealt by the agent. All damage 998 * dealt by the agent is reduced (or increased) by this value. 999 * 1000 * @return Scaling factor for damage dealt by the agent. 1001 */ 1002 public Double getDamageScaling() 1003 { 1004 // retreive from InitedMessage object 1005 if (lastInitedMessage == null) return null; 1006 return lastInitedMessage.getDamageScaling(); 1007 } 1008 1009 /*========================================================================*/ 1010 1011 /** 1012 * Retreives maximum base speed of the agent. 1013 * 1014 * @return Maximum base speed of the agent. 1015 */ 1016 public Double getBaseSpeed() 1017 { 1018 // retreive from InitedMessage object 1019 if (lastInitedMessage == null) return null; 1020 return lastInitedMessage.getGroundSpeed(); 1021 } 1022 1023 /** 1024 * Retreives maximum speed of the agent while moving in the air. 1025 * 1026 * @return Maximum speed of the agent while moving in the air. 1027 */ 1028 public Double getAirSpeed() 1029 { 1030 // retreive from InitedMessage object 1031 if (lastInitedMessage == null) return null; 1032 return lastInitedMessage.getAirSpeed(); 1033 } 1034 1035 /** 1036 * Retreives maximum speed of the agent while moving on a ladder. 1037 * 1038 * @return Maximum speed of the agent while moving on a ladder. 1039 */ 1040 public Double getLadderSpeed() 1041 { 1042 // retreive from InitedMessage object 1043 if (lastInitedMessage == null) return null; 1044 return lastInitedMessage.getLadderSpeed(); 1045 } 1046 1047 /** 1048 * Retreives maximum speed of the agent while moving in water. 1049 * 1050 * @return Maximum speed of the agent while moving in water. 1051 */ 1052 public Double getWaterSpeed() 1053 { 1054 // retreive from InitedMessage object 1055 if (lastInitedMessage == null) return null; 1056 return lastInitedMessage.getWaterSpeed(); 1057 } 1058 1059 /** 1060 * Retreives maximum speed of the agent while falling. 1061 * 1062 * @return Maximum speed of the agent while falling. 1063 */ 1064 public Double getFallSpeed() 1065 { 1066 // retreive from InitedMessage object 1067 if (lastInitedMessage == null) return null; 1068 return lastInitedMessage.getMaxFallSpeed(); 1069 } 1070 1071 /** 1072 * Retreives maximum speed of the agent while using dodge. 1073 * 1074 * <p>FIXME[js]: Check about the name depending on the meaning/value. 1075 * 1076 * @return Maximum speed of the agent while using dodge. 1077 */ 1078 public Double getDodgeSpeedFactor() 1079 { 1080 // retreive from InitedMessage object 1081 if (lastInitedMessage == null) return null; 1082 return lastInitedMessage.getDodgeSpeedFactor(); 1083 } 1084 1085 /** 1086 * Retreives acceleration rate of the agent. 1087 * 1088 * @return Acceleration rate of the agent. 1089 */ 1090 public Double getAccelerationRate() 1091 { 1092 // retreive from InitedMessage object 1093 if (lastInitedMessage == null) return null; 1094 return lastInitedMessage.getAccelRate(); 1095 } 1096 1097 /** 1098 * Retreives agent's control of movement while in the air. 1099 * This value ranges from 0 (none) to 1 (full control). 1100 * 1101 * @return Agent's control of movement while in the air. 1102 */ 1103 public Double getAirControl() 1104 { 1105 // retreive from InitedMessage object 1106 if (lastInitedMessage == null) return null; 1107 return lastInitedMessage.getAirControl(); 1108 } 1109 1110 /** 1111 * Retreives boost of the agent in the Z axis while jumping. 1112 * 1113 * @return Jumping boost of the agent in the Z axis. 1114 */ 1115 public Double getJumpZBoost() 1116 { 1117 // retreive from InitedMessage object 1118 if (lastInitedMessage == null) return null; 1119 return lastInitedMessage.getJumpZ(); 1120 } 1121 1122 /** 1123 * Retreives boost of the agent in the Z axis while using dodge. 1124 * 1125 * @return Dodge boost of the agent in the Z axis. 1126 */ 1127 public Double getDodgeZBoost() 1128 { 1129 // retreive from InitedMessage object 1130 if (lastInitedMessage == null) return null; 1131 return lastInitedMessage.getDodgeSpeedZ(); 1132 } 1133 1134 /*========================================================================*/ 1135 1136 /** 1137 * Retreives current game time, since the game started. 1138 * 1139 * @return Current game timestamp. 1140 */ 1141 public double getTime() 1142 { 1143 if (game == null) return 0; 1144 return game.getTime(); 1145 } 1146 1147 /*========================================================================*/ 1148 1149 /** 1150 * Retrieves the configuration of the bot inside UT2004. 1151 * @return Configuration of the bot. 1152 */ 1153 public ConfigChange getConfig() { 1154 return lastConfig; 1155 } 1156 1157 /*========================================================================*/ 1158 1159 1160 /** 1161 * Retrieves nearest known navpoint to current agent location. 1162 * <p><p> 1163 * WARNING: O(n) complexity. 1164 * 1165 * @return nearest navpoint 1166 */ 1167 public NavPoint getNearestNavPoint() { 1168 if (getLocation() == null) return null; 1169 return DistanceUtils.getNearest(agent.getWorldView().getAll(NavPoint.class).values(), getLocation()); 1170 } 1171 1172 /** 1173 * Retrieves nearest visible navpoint to current agent location. 1174 * <p><p> 1175 * WARNING: O(n) complexity. 1176 * 1177 * @return nearest visible navpoint 1178 */ 1179 public NavPoint getNearestVisibleNavPoint() { 1180 if (getLocation() == null) return null; 1181 return DistanceUtils.getNearestVisible(agent.getWorldView().getAll(NavPoint.class).values(), getLocation()); 1182 } 1183 1184 /** 1185 * Retrieves nearest known item to current agent location. 1186 * <p><p> 1187 * WARNING: O(n) complexity. 1188 * 1189 * @return nearest item 1190 */ 1191 public Item getNearestItem() { 1192 if (getLocation() == null) return null; 1193 return DistanceUtils.getNearest(agent.getWorldView().getAll(Item.class).values(), getLocation()); 1194 } 1195 1196 /** 1197 * Retrieves nearest visible item to current agent location. 1198 * <p><p> 1199 * WARNING: O(n) complexity. 1200 * 1201 * @return nearest visible item 1202 */ 1203 public Item getNearestVisibleItem() { 1204 if (getLocation() == null) return null; 1205 return DistanceUtils.getNearestVisible(agent.getWorldView().getAll(Item.class).values(), getLocation()); 1206 } 1207 1208 /** 1209 * Retrieves nearest known player to current agent location. 1210 * <p><p> 1211 * WARNING: O(n) complexity. 1212 * 1213 * @return nearest player 1214 */ 1215 public Player getNearestPlayer() { 1216 if (getLocation() == null) return null; 1217 return DistanceUtils.getNearest(agent.getWorldView().getAll(Player.class).values(), getLocation()); 1218 } 1219 1220 /** 1221 * Retrieves nearest visible player to current agent location. 1222 * <p><p> 1223 * WARNING: O(n) complexity. 1224 * 1225 * @return nearest visible player 1226 */ 1227 public Player getNearestVisiblePlayer() { 1228 if (getLocation() == null) return null; 1229 return DistanceUtils.getNearestVisible(agent.getWorldView().getAll(Player.class).values(), getLocation()); 1230 } 1231 1232 /*========================================================================*/ 1233 1234 /** Most rescent message containing info about the agent. */ 1235 Self self = null; 1236 1237 /** How many suicides the bot commited. */ 1238 int suicides = 0; 1239 1240 /** How many player we have killed so far. */ 1241 int kills = 0; 1242 1243 /** Most rescent message containing info about the game frame. */ 1244 InitedMessage lastInitedMessage = null; 1245 1246 /** Most rescent message containing info about the player's score. */ 1247 PlayerScore lastPlayerScore = null; 1248 1249 /** Most rescent message containing info about the player team's score. */ 1250 TeamScore lastTeamScore = null; 1251 1252 /** Most rescent message containing info about the volume the player is in. */ 1253 VolumeChanged lastVolumeChanged = null; 1254 1255 /** Configuration of the bot inside UT2004. */ 1256 ConfigChange lastConfig = null; 1257 1258 /*========================================================================*/ 1259 1260 /** 1261 * {@link BotKilled} listener counting the number of suicides. 1262 */ 1263 private class BotKilledListener implements IWorldEventListener<BotKilled> { 1264 1265 public BotKilledListener(IWorldView worldView) { 1266 worldView.addEventListener(BotKilled.class, this); 1267 } 1268 1269 @Override 1270 public void notify(BotKilled event) { 1271 if (self == null) return; 1272 // TODO: [jimmy] is it correct? 1273 if (event.getKiller() == null || event.getKiller().equals(getId())) { 1274 ++suicides; 1275 } 1276 } 1277 1278 } 1279 1280 /** {@link BotKilled} listener. */ 1281 BotKilledListener botKilledListener; 1282 1283 /*========================================================================*/ 1284 1285 private class PlayerKilledListener implements IWorldEventListener<PlayerKilled> { 1286 1287 public PlayerKilledListener(IWorldView worldView) { 1288 worldView.addEventListener(PlayerKilled.class, this); 1289 } 1290 1291 @Override 1292 public void notify(PlayerKilled event) { 1293 if (self == null) return; 1294 if (event.getKiller() != null && event.getKiller().equals(getId())) { 1295 ++kills; 1296 } 1297 } 1298 1299 } 1300 1301 /** {@link PlayerKilled} listener. */ 1302 PlayerKilledListener playerKilledListener; 1303 1304 /*========================================================================*/ 1305 1306 /** 1307 * InitedMessage listener. 1308 */ 1309 private class InitedMessageListener implements IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>> 1310 { 1311 @Override 1312 public void notify(WorldObjectUpdatedEvent<InitedMessage> event) 1313 { 1314 lastInitedMessage = event.getObject(); 1315 } 1316 1317 /** 1318 * Constructor. Registers itself on the given WorldView object. 1319 * @param worldView WorldView object to listent to. 1320 */ 1321 public InitedMessageListener(IWorldView worldView) 1322 { 1323 worldView.addObjectListener(InitedMessage.class, WorldObjectUpdatedEvent.class, this); 1324 } 1325 } 1326 1327 /** InitedMessage listener */ 1328 private InitedMessageListener initedMessageListener; 1329 1330 /*========================================================================*/ 1331 1332 /** 1333 * PlayerScore listener. 1334 */ 1335 private class PlayerScoreListener implements IWorldEventListener<PlayerScore> 1336 { 1337 @Override 1338 public void notify(PlayerScore event) 1339 { 1340 lastPlayerScore = event; 1341 } 1342 1343 /** 1344 * Constructor. Registers itself on the given WorldView object. 1345 * @param worldView WorldView object to listent to. 1346 */ 1347 public PlayerScoreListener(IWorldView worldView) 1348 { 1349 worldView.addEventListener(PlayerScore.class, this); 1350 } 1351 } 1352 1353 /** PlayerScore listener */ 1354 private PlayerScoreListener playerScoreListener; 1355 1356 /*========================================================================*/ 1357 1358 /** 1359 * TeamScore listener. 1360 */ 1361 private class TeamScoreListener implements IWorldObjectEventListener<TeamScore, WorldObjectUpdatedEvent<TeamScore>> 1362 { 1363 1364 /** 1365 * Constructor. Registers itself on the given WorldView object. 1366 * @param worldView WorldView object to listent to. 1367 */ 1368 public TeamScoreListener(IWorldView worldView) 1369 { 1370 worldView.addEventListener(TeamScore.class, this); 1371 } 1372 1373 @Override 1374 public void notify(WorldObjectUpdatedEvent<TeamScore> event) { 1375 lastTeamScore = event.getObject(); 1376 } 1377 } 1378 1379 /** TeamScore listener */ 1380 private TeamScoreListener teamScoreListener; 1381 1382 /*========================================================================*/ 1383 1384 /** 1385 * VolumeChanged listener. 1386 */ 1387 private class VolumeChangedListener implements IWorldEventListener<VolumeChanged> 1388 { 1389 @Override 1390 public void notify(VolumeChanged event) 1391 { 1392 lastVolumeChanged = event; 1393 } 1394 1395 /** 1396 * Constructor. Registers itself on the given WorldView object. 1397 * @param worldView WorldView object to listent to. 1398 */ 1399 public VolumeChangedListener(IWorldView worldView) 1400 { 1401 worldView.addEventListener(VolumeChanged.class, this); 1402 } 1403 } 1404 1405 /** VolumeChanged listener */ 1406 private VolumeChangedListener volumeChangedListener; 1407 1408 /*========================================================================*/ 1409 1410 /** 1411 * {@link Self} listener. 1412 */ 1413 private class SelfListener implements IWorldObjectEventListener<Self, WorldObjectFirstEncounteredEvent<Self>> 1414 { 1415 private IWorldView worldView; 1416 1417 /** 1418 * Constructor. Registers itself on the given WorldView object. 1419 * @param worldView WorldView object to listent to. 1420 */ 1421 public SelfListener(IWorldView worldView) 1422 { 1423 this.worldView = worldView; 1424 worldView.addObjectListener(Self.class, WorldObjectFirstEncounteredEvent.class, this); 1425 } 1426 1427 @Override 1428 public void notify(WorldObjectFirstEncounteredEvent<Self> event) { 1429 self = event.getObject(); 1430 } 1431 } 1432 1433 /** {@link Self} listener */ 1434 private SelfListener selfListener; 1435 1436 1437 /*========================================================================*/ 1438 1439 /** 1440 * {@link ConfigChange} listener. 1441 */ 1442 private class ConfigChangeListener implements IWorldObjectEventListener<ConfigChange, IWorldObjectEvent<ConfigChange>> 1443 { 1444 private IWorldView worldView; 1445 1446 /** 1447 * Constructor. Registers itself on the given WorldView object. 1448 * @param worldView WorldView object to listent to. 1449 */ 1450 public ConfigChangeListener(IWorldView worldView) 1451 { 1452 worldView.addObjectListener(ConfigChange.class, WorldObjectFirstEncounteredEvent.class, this); 1453 this.worldView = worldView; 1454 } 1455 1456 @Override 1457 public void notify(IWorldObjectEvent<ConfigChange> event) { 1458 lastConfig = event.getObject(); 1459 } 1460 } 1461 1462 /** {@link ConfigChange} listener */ 1463 private ConfigChangeListener configChangeListener; 1464 1465 1466 /*========================================================================*/ 1467 1468 public Self getSelf() { 1469 return self; 1470 } 1471 1472 /** Game memory module. */ 1473 public Game game; 1474 1475 /** 1476 * Constructor. Setups the memory module based on bot's world view. 1477 * @param game game info module 1478 */ 1479 public AgentInfo(UDKBot bot) { 1480 this(bot, new Game(bot), null); 1481 } 1482 1483 /** 1484 * Constructor. Setups the memory module based on bot's world view. 1485 * @param bot owner of the module 1486 * @param game game info module 1487 */ 1488 public AgentInfo(UDKBot bot, Game game) { 1489 this(bot, game, null); 1490 } 1491 1492 /** 1493 * Constructor. Setups the memory module based on bot's world view. 1494 * @param bot owner of the module 1495 * @param game game info module 1496 * @param log Logger to be used for logging runtime/debug info. Note: If <i>null</i> is provided, 1497 * this memory module creates it's own logger. 1498 */ 1499 public AgentInfo(UDKBot bot, Game game, Logger log) 1500 { 1501 super(bot, log); 1502 1503 this.game = game; 1504 NullCheck.check(this.game, "game"); 1505 1506 // create listeners 1507 selfListener = new SelfListener(worldView); 1508 initedMessageListener = new InitedMessageListener(worldView); 1509 playerScoreListener = new PlayerScoreListener(worldView); 1510 teamScoreListener = new TeamScoreListener(worldView); 1511 volumeChangedListener = new VolumeChangedListener(worldView); 1512 botKilledListener = new BotKilledListener(worldView); 1513 playerKilledListener = new PlayerKilledListener(worldView); 1514 configChangeListener = new ConfigChangeListener(worldView); 1515 } 1516 1517 /** 1518 * Provides initialization of the module (clearing internal data structures). Called automatically 1519 * during the agent starting sequence. 1520 */ 1521 @Override 1522 protected void start(boolean startPaused) { 1523 super.start(startPaused); 1524 self = null; 1525 suicides = 0; 1526 kills = 0; 1527 lastInitedMessage = null; 1528 lastPlayerScore = null; 1529 lastTeamScore = null; 1530 lastVolumeChanged = null; 1531 lastConfig = null; 1532 } 1533 1534 }