@Override public void showGame( Game game, Set<Location<Integer>> blockedLocations, Map<Room, Weapon> weaponLocations) { Board board = game.board; char[][] buffer = new char[2 * (board.width + 1)][2 * (board.height + 1)]; for (char[] line : buffer) { Arrays.fill(line, ' '); } for (int x = 0; x < board.width; x++) { for (int y = 0; y < board.height; y++) { // The actual tile is at buffer[2x + 1][2y + 1] // Walls: above is [2x + 1][2y], left is [2x][2y + 1] if (board.hasWallBetween( new Location<>(x, y), new Location<>(x, y - 1))) { // if there's a wall above the tile. buffer[2 * x + 1][2 * y] = '-'; } if (board.hasWallBetween( new Location<>(x, y), new Location<>(x - 1, y))) { // if there's a wall to the left of the tile. buffer[2 * x][2 * y + 1] = '|'; } } } for (int x = 0; x < board.width; x++) { buffer[2 * x + 1][2 * board.height] = '-'; } for (int y = 0; y < board.height; y++) { buffer[2 * board.width][2 * y + 1] = '|'; } for (Room room : Room.values()) { Location<Float> centre = board.centreLocationForRoom(room); String name = room.shortName(); int startX = 2 * centre.x.intValue() + 1 - name.length() / 2; int y = 2 * centre.y.intValue() + 1; for (int i = 0; i < +name.length(); i++) { buffer[startX + i][y] = name.charAt(i); } } for (Player player : game.allPlayers) { CluedoCharacter character = player.character; Location<Integer> location = player.location(); buffer[2 * location.x + 1][2 * location.y + 1] = this.asciiIconForCharacter(character); } for (int y = 0; y < 2 * (board.height + 1); y++) { for (int x = 0; x < 2 * (board.width + 1); x++) { this.out.print(buffer[x][y]); } this.out.print('\n'); } }
@Override public Board.Path requestPlayerMove( Player player, Board board, Set<Location<Integer>> blockedLocations, int distance) { this.out.printf("Enter a move of length %d.\n", distance); this.out.printf( "Moves are in the format UDLR, where such a move would mean go up, then down, then left, then right.\n\n"); List<Direction> moveSequence = new ArrayList<>(); String charSequence = this.scanner.next(); if (charSequence.length() != distance) { return this.requestPlayerMove(player, board, blockedLocations, distance); } for (int i = 0; i < charSequence.length(); i++) { char c = charSequence.charAt(i); Direction dir = Direction.fromCharacter(c); if (dir == null) { return this.requestPlayerMove(player, board, blockedLocations, distance); } moveSequence.add(dir); } Optional<Board.Path> path = board.directionsToPath(player.location(), moveSequence, blockedLocations); return path.isPresent() ? path.get() : this.requestPlayerMove(player, board, blockedLocations, distance); }
/** * 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); } }