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