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