/** * Returns the location, in pixels, of the centre of a tile at a given location, taking the walls * into account. */ private Location<Float> centreForTileAtLocation( Location<Integer> location, Board board, double startX, double startY, double tileSize) { double tileCentreX = tileSize / 2.0; double tileCentreY = tileSize / 2.0; if (board.hasWallBetween(location, location.locationInDirection(Direction.Up))) { tileCentreY += WallWidth / 4.0; } if (board.hasWallBetween(location, location.locationInDirection(Direction.Down))) { tileCentreY -= WallWidth / 4.0; } if (board.hasWallBetween(location, location.locationInDirection(Direction.Left))) { tileCentreX += WallWidth / 4.0; } if (board.hasWallBetween(location, location.locationInDirection(Direction.Right))) { tileCentreX -= WallWidth / 4.0; } return new Location<>( (float) (startX + tileSize * location.x + tileCentreX), (float) (startY + tileSize * location.y + tileCentreY)); }
@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); } }