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