001 // edu.isi.gamebots.examples.ExampleBot 002 // Copyright 2000, University of Southern California, 003 // Information Science Institute 004 // 005 // Personal and Educational use is hereby granted. 006 // Permission required for commercial use and redistribution. 007 008 009 package edu.isi.gamebots.examples; 010 011 import java.awt.*; 012 import java.awt.geom.*; 013 import java.lang.*; 014 import java.util.*; 015 016 import javax.swing.*; 017 018 import edu.isi.gamebots.client.*; 019 import edu.isi.gamebots.examples.*; 020 021 022 /** 023 * This example implementation the Bot class shows basic message handling, Bot 024 * control, and and uses {@link UTMapUI} for a map in its user interface. 025 * 026 * @author <a href="mailto:amarshal#gamebots@isi.edu">Andrew n marshall</a> 027 */ 028 public class ExampleBot extends Bot { 029 // Private Static Data 030 /////////////////////////////////////////////////////////////////////////// 031 protected static final int MAX_OUTPUT = 100; 032 protected static final double PLAYER_RADIUS = 18.0d; 033 protected static final double THETA_DELTA = (2d*Math.PI)/60d; // Angular Delta (6 deg) 034 protected static final int NONE = 0; 035 036 // state 037 protected static final int EXPLORING = 1; 038 protected static final int HEALING = 2; 039 protected static final int HUNTING = 3; 040 041 // interference 042 protected static final int INTERFERENCE_BUMP = 1; 043 protected static final int INTERFERENCE_WALL = 2; 044 protected static final int INTERFERENCE_DAMAGE = 3; 045 046 // directional tendancy 047 protected static final int LEFT = 1; 048 protected static final int RIGHT = 2; 049 050 /** 051 * Compares Paths based on theta. 052 * 053 * Note: this comparator imposes orderings that are inconsistent with equals(...). 054 */ 055 protected static final Comparator pathComparator = new Comparator() { 056 public int compare( Object aObj, Object bObj ) { 057 Path a = (Path) aObj; 058 Path b = (Path) bObj; 059 060 if( a.theta < b.theta ) 061 return -1; 062 if( a.theta == b.theta ) 063 return 0; 064 else // a.theta > b.theta 065 return 1; 066 } 067 }; 068 069 // Private Data 070 /////////////////////////////////////////////////////////////////////// 071 protected NodeMap map = new NodeMap(); 072 073 protected Random random = new Random(); 074 075 protected Thread runnerThread; 076 077 protected boolean logIntents = false; 078 protected boolean logMessages = false; 079 protected boolean logSelf = false; 080 081 protected boolean didInit = false; 082 083 protected Object stateLock = new Object(); 084 protected long stateChangeTime = 0; 085 protected int state = EXPLORING; 086 protected int direction = NONE; // for rot/straft tendancies.. 087 088 /** 089 * This lock is used to synchronized reads from/writes to the various state 090 * variables. 091 */ 092 protected final Object selfLock = new Object(); 093 protected double x, y, z; // in world coordinates 094 protected double yaw, pitch, roll; // in Radians 095 protected int health, armor, ammo; 096 protected int team = 255; 097 protected String weapon; 098 protected Vector3D here; // Location as of the last vision update 099 100 protected boolean interfered = false; 101 protected long interferenceTime = 0; 102 protected int interferenceType = NONE; 103 104 protected long lastRotIncTime = 0; 105 protected double yawTarget; 106 107 protected Object nodeInfoLock = new Object(); 108 protected java.util.Map idToNode = new HashMap(); // Id String to Node object 109 protected Collection knownNodes = idToNode.values(); // automatic updates 110 protected Set exploredNodes = new HashSet(); 111 protected Set visibleNodes = new HashSet(); 112 protected Set reachableNodes = new HashSet(); 113 114 protected Node target, lastTarget; 115 protected long targetAcquiredTime = 0; 116 protected long targetLostTime = 0; 117 118 // Public Methods 119 /////////////////////////////////////////////////////////////////////// 120 public JComponent getView() { 121 return map; 122 } 123 124 // Protected Methods 125 /////////////////////////////////////////////////////////////////////// 126 127 // runner 128 protected Runnable runner = new Runnable() { 129 public void run() { 130 Thread thread = Thread.currentThread(); 131 while( thread == runnerThread ) { 132 try { 133 if( didInit ) { 134 synchronized( stateLock ) { 135 switch( state ) { 136 default: 137 case EXPLORING: 138 explore(); 139 break; 140 141 case HEALING: 142 heal(); 143 break; 144 145 case HUNTING: 146 hunt(); 147 break; 148 } 149 } // end sync 150 } // end if 151 thread.sleep( 1000l ); 152 } catch( InterruptedException error ) { 153 // ignore 154 } 155 } 156 } 157 }; 158 159 // Event Handlers 160 protected void connected() { 161 super.connected(); 162 163 log.logNote( "Connected... ("+new Date()+")" ); 164 165 runnerThread = new Thread( runner ); 166 runnerThread.start(); 167 168 map.repaint(); 169 } 170 171 protected void receivedAsyncMessage( Message message ) { 172 if( didInit ) { 173 if( message.getType().equals( BUMP ) ) { 174 interferenceTime = System.currentTimeMillis(); 175 interferenceType = INTERFERENCE_BUMP; 176 interfered = true; 177 } else if( message.getType().equals( WALL ) ) { 178 interferenceTime = System.currentTimeMillis(); 179 interferenceType = INTERFERENCE_WALL; 180 interfered = true; 181 } else if( message.getType().equals( DAMAGE ) ) { 182 interferenceTime = System.currentTimeMillis(); 183 interferenceType = INTERFERENCE_DAMAGE; 184 interfered = true; 185 } 186 } else if( message.getType().equals( INFO ) ) { 187 // Init 188 // Should check to make sure it is only the first... 189 Properties props = new Properties(); 190 props.setProperty( client.PLAYER_NAME, getName() ); 191 int team = getInitialTeam(); 192 if( team != TEAM_ANY ) 193 props.setProperty( client.PLAYER_TEAM, Integer.toString(team) ); 194 client.sendMessage( client.INIT, props ); 195 didInit = true; 196 } 197 198 if( logMessages ) 199 log.logNote( message ); 200 } 201 202 protected void receivedSyncMessage( MessageBlock block ) { 203 StringBuffer sb = new StringBuffer(); 204 if( logMessages ) { 205 sb.append( block ); 206 } 207 208 synchronized( selfLock ) { 209 if( here == null || 210 ( here.x != x || 211 here.y != y || 212 here.z != z ) ) 213 here = new Vector3D( x, y, z ); 214 } 215 Message message; 216 String type, value; 217 Node node; 218 219 synchronized( nodeInfoLock ) { 220 visibleNodes.clear(); 221 reachableNodes.clear(); 222 223 Iterator i = block.getMessages(); 224 while( i.hasNext() ) { 225 message = (Message) i.next(); 226 type = message.getType(); 227 if( type.equals( END ) ) { 228 // ignore 229 } else if( message.getType().equals( SELF ) ) { 230 if( logSelf ) 231 log.logNote( message ); 232 updateSelf( message ); 233 if( logSelf ) 234 log.logNote( "DEBUG: Loc: "+x+" "+y+" "+z+"; Rot: "+pitch+" "+yaw+" "+roll ); 235 } else if( type.equals( PLAYER ) ) { 236 // TO-DO 237 } else { 238 node = (Node) idToNode.get( message.getProperty( ACTOR_ID ) ); 239 if( node == null ) { 240 node = new Node( message ); 241 if( node.id != null ) 242 idToNode.put( node.id, node ); 243 else if( logMessages ) 244 sb.append( "\n * No ID: " ); 245 } 246 value = message.getProperty( ACTOR_REACHABLE ); 247 if( value != null && 248 value.equals( TRUE ) ) { 249 node.reachableFrom( here ); 250 reachableNodes.add( node ); 251 } 252 visibleNodes.add( node ); 253 } 254 if( logMessages ) { 255 sb.append('\n'); 256 sb.append( message ); 257 } 258 } 259 260 runnerThread.interrupt(); 261 if( logMessages ) 262 log.logNote( sb.toString() ); 263 } 264 265 map.repaint(); 266 } 267 268 protected void disconnected() { 269 didInit = false; 270 271 log.logNote( "Disconnected... ("+new Date()+")" ); 272 273 if( runnerThread != null ) { 274 Thread oldThread = runnerThread; 275 runner = null; 276 oldThread.interrupt(); 277 } 278 279 map.repaint(); 280 } 281 282 protected void updateSelf( Message message ) { 283 String value; 284 double vector[]; 285 286 synchronized( selfLock ) { 287 value = message.getProperty( PLAYER_NAME ); 288 if( value != null ) 289 setName( value ); 290 291 value = message.getProperty( PLAYER_TEAM ); 292 if( value != null ) { 293 try { 294 team = Integer.parseInt( value ); 295 } catch( NumberFormatException error ) { 296 // Log and ignore 297 log.logError( "Illegal \""+PLAYER_TEAM+"\" value: "+value, error ); 298 } 299 } 300 301 value = message.getProperty( LOCATION ); 302 if( value != null ) { 303 vector = parseVector( value ); 304 x = vector[0]; 305 y = vector[1]; 306 z = vector[2]; 307 } 308 309 value = message.getProperty( ROTATION ); 310 if( value != null ) { 311 vector = parseVector( value ); 312 pitch = vector[0] * 2 * Math.PI / 65535; 313 yaw = vector[1] * 2 * Math.PI / 65535; 314 roll = vector[2] * 2 * Math.PI / 65535; 315 } 316 317 value = message.getProperty( PLAYER_AMMO ); 318 if( value != null ) { 319 try { 320 ammo = Integer.parseInt( value ); 321 } catch( NumberFormatException error ) { 322 // Log and ignore 323 log.logError( "Illegal \""+PLAYER_AMMO+"\" value: "+value, error ); 324 } 325 } 326 327 value = message.getProperty( PLAYER_ARMOR ); 328 if( value != null ) { 329 try { 330 armor = Integer.parseInt( value ); 331 } catch( NumberFormatException error ) { 332 // Log and ignore 333 log.logError( "Illegal \""+PLAYER_ARMOR+"\" value: "+value, error ); 334 } 335 } 336 337 value = message.getProperty( PLAYER_HEALTH ); 338 if( value != null ) { 339 try { 340 health = Integer.parseInt( value ); 341 } catch( NumberFormatException error ) { 342 // Log and ignore 343 log.logError( "Illegal \""+PLAYER_HEALTH+"\" value: "+value, error ); 344 } 345 } 346 347 value = message.getProperty( PLAYER_WEAPON ); 348 if( value != null && 349 value != weapon ) 350 weapon = value; 351 } 352 353 synchronized( nodeInfoLock ) { 354 if( target != null && 355 target.loc.near( x, y, z, PLAYER_RADIUS ) ) { 356 exploredNodes.add( target ); 357 if( target != null ) 358 lastTarget = target; 359 target = null; 360 } 361 } 362 map.repaint(); 363 } 364 365 protected void explore() { 366 long now = System.currentTimeMillis(); 367 // Assumes synchronized on stateLock 368 if( state != EXPLORING ) { 369 state = EXPLORING; 370 stateChangeTime = now; 371 } 372 373 // Atomic copy (need a consistent set of variables, but won't write to them; 374 final double x, y, z, yaw, pitch, roll; 375 synchronized( selfLock ) { 376 x = ExampleBot.this.x; 377 y = ExampleBot.this.y; 378 z = ExampleBot.this.z; 379 yaw = ExampleBot.this.yaw; 380 pitch = ExampleBot.this.pitch; 381 } 382 383 synchronized( nodeInfoLock ) { 384 if( reachableNodes == null ) 385 return; // No game update yet. 386 387 if( target != null ) { 388 targetLostTime = now; 389 } else if( reachableNodes.contains( target ) ) { 390 long delay = interferenceTime - targetAcquiredTime; 391 if( ( interferenceType == INTERFERENCE_WALL && 392 delay > 1000 ) || 393 ( interferenceType != NONE && 394 delay > 3000 ) || 395 ( delay > 6000 ) ) { // Give up after some time 396 if( logIntents ) 397 log.logNote( "Giving up on target. Interference Type: "+interferenceType+", delay: "+delay ); 398 findNewTarget(); 399 } else { 400 // Otherwise continue persuing target. 401 runTo( target.id ); 402 } 403 return; 404 } 405 if( reachableNodes.isEmpty() ) { 406 if( logIntents ) 407 log.logNote( "No visible nodes." ); 408 findNewTarget(); 409 } else { 410 Set options = new HashSet( reachableNodes ); 411 options.removeAll( exploredNodes ); 412 if( options.isEmpty() ) { 413 if( random.nextBoolean() ) { 414 if( logIntents ) 415 log.logNote( "No unexplored nodes." ); 416 findNewTarget(); 417 return; 418 } 419 options = reachableNodes; 420 if( lastTarget != null ) 421 reachableNodes.remove( lastTarget ); 422 } 423 Node node; 424 Iterator i = options.iterator(); 425 while( i.hasNext() ) { 426 node = (Node) i.next(); 427 if( node.loc.near( x, y, z, PLAYER_RADIUS ) ) 428 i.remove(); 429 } 430 if( options.isEmpty() ) { 431 if( logIntents ) 432 log.logNote( "No distant nodes." ); 433 findNewTarget(); 434 return; 435 } 436 437 node = (Node) options.toArray()[ random.nextInt( options.size() ) ]; 438 if( target == null ) { 439 if( lastTarget == node ) 440 if( logIntents ) 441 log.logNote( "No visible nodes." ); 442 findNewTarget(); 443 } else { 444 if( target == node ) 445 findNewTarget(); 446 } 447 448 direction = NONE; 449 interferenceType = NONE; 450 targetAcquiredTime = now; 451 if( target != null ) 452 lastTarget = target; 453 target = node; 454 455 runTo( target.id ); 456 457 log.logNote( "Exploring new target: "+target ); 458 } 459 } 460 } 461 462 protected void heal() { 463 } 464 465 protected void hunt() { 466 } 467 468 protected void findNewTarget() { 469 interferenceType = NONE; 470 synchronized( nodeInfoLock ) { 471 if( target != null ) 472 lastTarget = target; 473 target = null; 474 } 475 interfered = false; 476 incRotation(); 477 } 478 479 protected void incRotation() { 480 long now = System.currentTimeMillis(); 481 if( now-lastRotIncTime < 200 ) { 482 // log.logNote( "Don't turn yet." ); 483 return; 484 } 485 if( direction == NONE ) { 486 if( random.nextBoolean() ) 487 direction = LEFT; 488 else 489 direction = RIGHT; 490 } 491 double newYaw; 492 if( direction == LEFT ) 493 newYaw = yaw+(Math.PI/6); // 15 deg 494 else 495 newYaw = yaw-(Math.PI/6); // 15 deg 496 if( newYaw == yawTarget ) { 497 if( logIntents ) 498 log.logNote( "Stuck in rotation: Jittering." ); 499 jitter(); // get unstuck 500 } else { 501 if( logIntents ) 502 log.logNote( "Rotating to "+newYaw ); 503 turnTo( pitch, newYaw, 0 ); 504 yawTarget = newYaw; 505 lastRotIncTime = now; 506 } 507 } 508 509 protected void jitter() { 510 switch( random.nextInt(8) ) { 511 case 0: 512 runTo( x+(PLAYER_RADIUS/2), y, z ); 513 break; 514 case 1: 515 runTo( x+(PLAYER_RADIUS/2), y+(PLAYER_RADIUS/2), z ); 516 break; 517 case 2: 518 runTo( x, y+(PLAYER_RADIUS/2), z ); 519 break; 520 case 3: 521 runTo( x-(PLAYER_RADIUS/2), y+(PLAYER_RADIUS/2), z ); 522 break; 523 case 4: 524 runTo( x-(PLAYER_RADIUS/2), y, z ); 525 break; 526 case 5: 527 runTo( x-(PLAYER_RADIUS/2), y-(PLAYER_RADIUS/2), z ); 528 break; 529 case 6: 530 runTo( x, y-(PLAYER_RADIUS/2), z ); 531 break; 532 default: 533 runTo( x+(PLAYER_RADIUS/2), y-(PLAYER_RADIUS/2), z ); 534 break; 535 } 536 } 537 538 // Inner Classes 539 /////////////////////////////////////////////////////////////////////////// 540 protected class Path { 541 public final Vector3D to; // Usually the location of a node 542 public final Vector3D from; 543 public final double theta; // Angle on the x-y plane, from x axis 544 public final double phi; // Angle from the x-y plane 545 public final double distSquared; 546 547 public final Line2D line; 548 549 public Path( Vector3D to, Vector3D from ) { 550 this.to = to; 551 this.from = from; 552 553 double dx = to.x-from.x; 554 double dy = to.y-from.y; 555 double dz = to.z-from.z; 556 theta = Math.atan2( dy, dx ); 557 double xyRadiusSquared = dx*dx + dy*dy; 558 phi = Math.atan2( dz, Math.sqrt( xyRadiusSquared ) ); 559 distSquared = xyRadiusSquared + dz*dz; 560 561 line = new Line2D.Double( from.x, from.y, to.x, to.y ); 562 } 563 } 564 565 protected class Node { 566 // Public Data 567 /////////////////////////////////////////////////////////////////////// 568 public final String id; 569 public final int type = NONE; // TO-DO: need to define node types 570 public final Vector3D loc; 571 public final SortedSet paths = new TreeSet( pathComparator ); 572 573 // Public Methods 574 /////////////////////////////////////////////////////////////////////// 575 public Node( Message message ) { 576 String value = message.getProperty( LOCATION ); 577 Vector3D temp = null; 578 if( value != null ) { 579 try { 580 double[] vector = parseVector( value ); 581 temp = new Vector3D( vector[0], vector[1], vector[2] ); 582 } catch( RuntimeException error ) { 583 // temp 584 } 585 } 586 loc = temp; 587 id = message.getProperty( ACTOR_ID ); 588 } 589 590 public boolean equals( Object obj ) { 591 if( !(obj instanceof Node) ) 592 return false; 593 Node node = (Node) obj; 594 if( id == null ) 595 return node.id == null && 596 loc.near( node.loc, PLAYER_RADIUS ); 597 return id.equals( node.id ); 598 } 599 600 public String toString() { 601 return "{Node "+ACTOR_ID+"="+id+", "+LOCATION+"="+loc+"}"; 602 } 603 604 public void reachableFrom( Vector3D from ) { 605 synchronized( paths ) { 606 Path path = new Path( loc, from ); 607 if( !paths.contains( path ) ) { 608 SortedSet sub = paths.headSet( path ); 609 if( !sub.isEmpty() ) { 610 Path prev = (Path) sub.last(); 611 if( prev != null && 612 (path.theta - prev.theta < THETA_DELTA) && 613 (path.distSquared > prev.distSquared) ) 614 paths.remove( prev ); 615 } 616 sub = paths.tailSet( path ); 617 if( !sub.isEmpty() ) { 618 Path next = (Path) sub.first(); 619 if( next != null && 620 (next.theta - path.theta < THETA_DELTA) && 621 (path.distSquared > next.distSquared) ) 622 paths.remove( next ); 623 } 624 // TO-DO: special case theta's near +/-pi? 625 paths.add( path ); 626 } 627 } 628 } 629 } 630 631 protected class NodeMap extends JComponent { 632 // Private Constants 633 /////////////////////////////////////////////////////////////////////// 634 protected final double minSymbolSize = 8d; 635 protected final double symbolSize = 12d; 636 637 protected final Color COLOR_TEAM_GOLD = new Color( 0xCC9900 ); // Gold 638 protected final Color COLOR_TEAM_DEFAULT = Color.black; 639 640 protected final Color COLOR_BACKGROUND = Color.white; 641 protected final Color COLOR_AXIS = Color.lightGray; 642 643 protected final Color COLOR_NODE = Color.gray; 644 protected final Color COLOR_NODE_VISIBLE = Color.darkGray; 645 protected final Color COLOR_NODE_TARGET = Color.pink; 646 protected final Color COLOR_NODE_TARGET_VISIBLE = Color.magenta; 647 protected final Color COLOR_PATH = new Color( 0xEEEEEE ); // Very light gray 648 649 // Private Data 650 /////////////////////////////////////////////////////////////////////// 651 protected Object scaleLock = new Object(); 652 protected double scale, symbolScale; 653 654 protected Line2D xAxis = new Line2D.Double( -Double.MAX_VALUE, 0, Double.MAX_VALUE, 0 ); 655 protected Line2D yAxis = new Line2D.Double( 0, -Double.MAX_VALUE, 0, Double.MAX_VALUE ); 656 protected Ellipse2D playerCircle = new Ellipse2D.Double( -symbolSize/2, -symbolSize/2, 657 symbolSize, symbolSize ); 658 protected Ellipse2D navPointCircle = new Ellipse2D.Double( -symbolSize/4, -symbolSize/4, 659 symbolSize/2, symbolSize/2 ); 660 661 // Public Methods 662 /////////////////////////////////////////////////////////////////////// 663 public NodeMap() { 664 setDoubleBuffered( true ); 665 setScale( minSymbolSize/(2d*PLAYER_RADIUS) ); 666 } 667 668 public double getScale() { 669 return scale; 670 } 671 672 public void setScale( double scale ) { 673 synchronized( scaleLock ) { 674 this.scale = scale; 675 this.symbolScale = Math.max( 2*PLAYER_RADIUS, minSymbolSize/scale )/symbolSize; 676 } 677 repaint(); 678 } 679 680 // Private Methods 681 /////////////////////////////////////////////////////////////////////// 682 protected void paintComponent( Graphics g ) { 683 Color oldColor = g.getColor(); 684 Font oldFont = g.getFont(); 685 686 Font font = getFont(); 687 g.setFont( font ); 688 FontMetrics fm = getFontMetrics( font ); 689 Dimension size = getSize(); 690 691 if( g instanceof Graphics2D ) { 692 // Clear background 693 g.setColor( COLOR_BACKGROUND ); 694 g.fillRect( 0, 0, size.width, size.height ); 695 696 if( paintComponent2D( (Graphics2D) g.create() ) ) 697 repaint(); 698 } else { 699 // Clear background 700 g.setColor( getBackground() ); 701 g.fillRect( 0, 0, size.width, size.height ); 702 703 g.setColor( getForeground() ); 704 g.drawString( "Requires Graphics2D", 2, fm.getAscent()+2 ); 705 } 706 707 if( !client.isConnected() ) { 708 final String message = "Disconnected"; 709 int width = fm.stringWidth( message ); 710 711 g.setColor( Color.red ); 712 g.drawString( message, size.width-width-2, size.height-fm.getDescent()-2 ); 713 } 714 715 716 g.setColor( oldColor ); 717 g.setFont( oldFont ); 718 } 719 720 protected boolean paintComponent2D( Graphics2D g ) { 721 Graphics2D g2, g3; 722 Iterator i, i2; 723 Node node; 724 Path path; 725 boolean needRepaint = false; 726 727 Dimension size = getSize(); 728 Rectangle2D.Double bounds = new Rectangle2D.Double( 0, 0, size.width, size.height ); 729 730 // Atomic copy (need a consistent set of variables, but won't write to them; 731 final double scale, symbolScale; 732 synchronized( scaleLock ) { 733 scale = this.scale; 734 symbolScale = this.symbolScale; 735 } 736 final double x, y, z, yaw, pitch; 737 synchronized( selfLock ) { 738 x = ExampleBot.this.x; 739 y = ExampleBot.this.y; 740 z = ExampleBot.this.z; 741 yaw = ExampleBot.this.yaw; 742 pitch = ExampleBot.this.pitch; 743 } 744 745 // Scale && translate 746 g.translate( bounds.width/2, bounds.height/2 ); 747 g.scale( scale, scale ); 748 g2 = (Graphics2D) g.create(); 749 g2.translate( -x, -y ); 750 751 // Draw Origin 752 g2.setColor( COLOR_AXIS ); 753 g2.draw( xAxis ); 754 g2.draw( yAxis ); 755 756 // synchronized( nodeInfoLock ) { 757 try { 758 // Draw Paths... 759 i = knownNodes.iterator(); 760 while( i.hasNext() ) { 761 node = (Node) i.next(); 762 763 i2 = node.paths.iterator(); 764 g2.setColor( COLOR_PATH ); 765 while( i2.hasNext() ) { 766 path = (Path) i2.next(); 767 g2.draw( path.line ); 768 } 769 } 770 771 // Draw Nodes... 772 i = knownNodes.iterator(); 773 while( i.hasNext() ) { 774 node = (Node) i.next(); 775 776 g3 = (Graphics2D) g2.create(); 777 g3.translate( node.loc.x, node.loc.y ); 778 g3.scale( symbolScale, symbolScale ); 779 drawNode( g3, node ); 780 g3.dispose(); 781 } 782 } catch( RuntimeException error ) { 783 // Probably just a problem with not blocking while accessing the node Info 784 needRepaint = true; 785 } 786 787 // Draw Players... 788 789 g2.dispose(); 790 791 // Draw Self 792 g2 = (Graphics2D) g.create(); 793 g2.scale( symbolScale, symbolScale ); 794 drawPlayer( g2, team, yaw, pitch ); 795 g2.dispose(); 796 797 return needRepaint; 798 } 799 800 protected void drawPlayer( Graphics2D g, int team, double yaw, double pitch ) { 801 switch( team ) { 802 case TEAM_RED: 803 g.setColor( Color.red ); 804 break; 805 case TEAM_BLUE: 806 g.setColor( Color.blue ); 807 break; 808 case TEAM_GREEN: 809 g.setColor( Color.green ); 810 break; 811 case TEAM_GOLD: 812 g.setColor( COLOR_TEAM_GOLD ); 813 break; 814 default: 815 g.setColor( COLOR_TEAM_DEFAULT ); 816 break; 817 } 818 819 double length = symbolSize*Math.cos( pitch ); 820 double x = length * Math.cos( yaw ); 821 double y = length * Math.sin( yaw ); 822 823 g.draw( playerCircle ); 824 g.draw( new Line2D.Double( 0d, 0d, x, y ) ); 825 } 826 827 protected void drawNode( Graphics2D g, Node node ) { 828 switch( node.type ) { 829 default: 830 if( visibleNodes.contains(node) ) { 831 if( node == target ) 832 g.setColor( Color.magenta ); 833 else 834 g.setColor( Color.darkGray ); 835 } else { 836 if( node == target ) 837 g.setColor( Color.pink ); 838 else 839 g.setColor( Color.lightGray ); 840 } 841 g.draw( navPointCircle ); 842 break; 843 } 844 } 845 } 846 }