private void drawPlayer( Graphics g, Location<Float> location, CluedoCharacter character, double step, boolean drawTransparent) { double diameter = step * PlayerDiameterRatio; final double characterBorderRatio = 1.2f; g.setColor(new Color(0.f, 0.f, 0.f, drawTransparent ? 0.2f : 1.f)); // Draw the black outline g.fillOval( round(location.x - diameter * characterBorderRatio / 2), round(location.y - diameter * characterBorderRatio / 2), round(diameter * characterBorderRatio), round(diameter * characterBorderRatio)); // Draw the character. g.setColor( new Color( character.colour().getRed(), character.colour().getGreen(), character.colour().getBlue(), drawTransparent ? 50 : 255)); g.fillOval( round(location.x - diameter / 2), round(location.y - diameter / 2), round(diameter), round(diameter)); }
private void drawWeaponTokenForRoom( Graphics g, Board board, Room room, double centreX, double topY, double step) { Weapon weapon = _weaponLocations.get(room); if (weapon != null) { Image weaponTokenImage = weapon.tokenImage(); double weaponTokenWidth = weaponTokenImage.getWidth(null); double weaponTokenHeight = weaponTokenImage.getHeight(null); double longestSideLength = step * WeaponTokenRatio; // scale so that the longest side is always larger if (weaponTokenWidth >= weaponTokenHeight) { weaponTokenWidth = longestSideLength; double ratio = weaponTokenImage.getWidth(null) / weaponTokenWidth; weaponTokenHeight = weaponTokenHeight / ratio; } else { weaponTokenHeight = longestSideLength; double ratio = weaponTokenImage.getHeight(null) / weaponTokenHeight; weaponTokenWidth = weaponTokenWidth / ratio; } g.drawImage( weaponTokenImage, (int) (centreX - weaponTokenWidth / 2), (int) (topY), (int) weaponTokenWidth, (int) weaponTokenHeight, null); } }
private void drawAccessibleTilesOverlay( Graphics g, Set<Board.Path> paths, double startX, double startY, double tileSize) { for (Board.Path path : paths) { // Draw each path's endpoint, with transparency proportional to how far it is to // travel. Location<Integer> endTile = path.locations[path.distance - 1]; g.setColor(new Color(0.8f, 0.2f, 0.3f, 1.f - path.cost / 14.f)); g.fillRect( round(startX + tileSize * endTile.x - 0.5), round(startY + tileSize * endTile.y - 0.5), round(tileSize + 1), round(tileSize + 1)); } }
@SuppressWarnings("SuspiciousNameCombination") // so we don't get warned about using 'WallWidth' in conjunction with height @Override protected void paintComponent(Graphics g) { double width = this.getWidth(); double height = this.getHeight(); Graphics2D graphics2D = (Graphics2D) g; graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics2D.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); Board board = _gameState.board; double ratio = (double) board.width / board.height; width = Math.min(width, (height * ratio)); height = Math.min(height, (width / ratio)); double startX = this.getWidth() / 2 - width / 2; double startY = this.getHeight() / 2 - height / 2; g.setColor(Color.yellow); g.fillRect(round(startX), round(startY), round(width), round(height)); // Draw the grid. g.setColor(Color.black); double step = width / board.width; for (double x = startX + step; x < startX + width; x += step) { g.drawLine(round(x), round(startY), round(x), round(startY + height)); } for (double y = startY + step; y < startY + height; y += step) { g.drawLine(round(startX), round(y), round(startX + width), round(y)); } // Draw the room tiles. int x = 0; for (Board.Tile[] column : board.tiles) { int y = 0; for (Board.Tile tile : column) { boolean hasRoom = tile.room.isPresent(); boolean isUnaccessibleSpace = tile.connectedLocations.isEmpty(); if (hasRoom || isUnaccessibleSpace) { g.setColor(hasRoom ? Color.lightGray : Color.cyan); g.fillRect( round(x * step + startX - 0.5), round(y * step + startY - 0.5), round(step + 1), round(step + 1)); if (tile.isPassageway(board)) { g.setColor(Color.darkGray); for (int i = 0; i < step; i += 2) { g.fillRect( round(x * step + startX - 0.5), round(y * step + startY - 0.5 + i), round(step + 1), 1); } } } y++; } x++; } // Draw the walls. // Loop again to draw over what we already have. g.setColor(Color.black); x = 0; for (Board.Tile[] column : board.tiles) { int y = 0; for (Board.Tile tile : column) { Location<Integer> location = new Location<>(x, y); if (board.hasWallBetween(location, new Location<>(x + 1, y))) { g.fillRect( round(startX + step * (x + 1) - WallWidth / 2), round(startY + step * y - 0.5), WallWidth, round(step + 1)); } if (board.hasWallBetween(location, new Location<>(x, y + 1))) { g.fillRect( round(startX + step * x - 0.5), round(startY + step * (y + 1) - WallWidth / 2), round(step + 1), WallWidth); } y++; } x++; } // Draw the outer walls. g.fillRect(round(startX), round(startY), WallWidth, round(height)); g.fillRect(round(startX), round(startY), round(width), WallWidth); g.fillRect(round(startX + width - WallWidth), round(startY), WallWidth, round(height)); g.fillRect(round(startX), round(startY + height - WallWidth), round(width), WallWidth); // Draw the names for the rooms. g.setFont(GameFont); g.setColor(Color.black); FontMetrics fontMetrics = g.getFontMetrics(GameFont); for (Room room : Room.values()) { Location<Float> centre = board.centreLocationForRoom(room); double centreX = (centre.x + 0.5f) * step + startX; double centreY = (centre.y + 0.5f) * step + startY; String name = room.shortName().toUpperCase(); Rectangle2D bounds = fontMetrics.getStringBounds(name, g); g.drawString( name, (int) (centreX - bounds.getCenterX()), (int) (centreY - bounds.getCenterY())); double stringBottom = bounds.getCenterY() + centreY; this.drawWeaponTokenForRoom(g, board, room, centreX, stringBottom, step); } // Draw the players. boolean shouldPlayMoveSequence = this.shouldPlayMoveSequence(); for (Player player : _gameState.allPlayers) { boolean drawTransparent = false; Location<Float> playerLocation; if (shouldPlayMoveSequence && // We haven't finished animating the move player.character == _lastPlayerMoveCharacter) { // and the move is for this character // then we need to lerp between two values in the move sequence for the character's // position. int lowIndex = (int) Math.floor(_moveSequencePosition); int highIndex = (int) Math.ceil(_moveSequencePosition); double lerpValue = _moveSequencePosition - lowIndex; Location<Float> startLocation = this.centreForTileAtLocation( _lastPlayerMove.locations[lowIndex], board, startX, startY, step); Location<Float> endLocation = this.centreForTileAtLocation( _lastPlayerMove.locations[highIndex], board, startX, startY, step); if (Location.distance(startLocation, endLocation) > step * 1.5) { // if the tiles aren't adjacent, allowing for some error. drawTransparent = true; } playerLocation = Location.lerp(startLocation, endLocation, (float) lerpValue); } else { playerLocation = this.centreForTileAtLocation(player.location(), board, startX, startY, step); } this.drawPlayer(g, playerLocation, player.character, step, drawTransparent); } // If we're supposed to draw the overlay for the paths, do so. Don't draw the overlay while // we're animating. if (_accessibleTilePaths != null && !shouldPlayMoveSequence) { this.drawAccessibleTilesOverlay(g, _accessibleTilePaths, startX, startY, step); } }