Java tutorial
/* * Copyright 2010, Maarten Billemont * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.lyndir.omicron.cli.view; import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*; import com.google.common.collect.*; import com.googlecode.lanterna.input.Key; import com.googlecode.lanterna.screen.Screen; import com.googlecode.lanterna.screen.ScreenCharacterStyle; import com.googlecode.lanterna.terminal.Terminal; import com.lyndir.lanterna.view.*; import com.lyndir.lhunath.opal.math.*; import com.lyndir.omicron.api.*; import com.lyndir.omicron.api.util.Maybe; import com.lyndir.omicron.cli.OmicronCLI; import java.util.Map; import java.util.Optional; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * @author lhunath, 2013-07-21 */ public class MapView extends View { private static final Map<LevelType, Terminal.Color> levelTypeColors = // ImmutableMap.of(LevelType.GROUND, Terminal.Color.GREEN, LevelType.SKY, Terminal.Color.CYAN, LevelType.SPACE, Terminal.Color.BLUE); private static final Map<ResourceType, Terminal.Color> resourceTypeColors = // ImmutableMap.of(ResourceType.FUEL, Terminal.Color.RED, ResourceType.METALS, Terminal.Color.WHITE, ResourceType.SILICON, Terminal.Color.YELLOW, ResourceType.RARE_ELEMENTS, Terminal.Color.MAGENTA); private Vec2 offset = Vec2.create(); private LevelType levelType; @Nullable private Terminal.Color mapColor; @Nullable private String backgroundPattern; private boolean hasUnits; public MapView(@Nonnull final LevelType levelType) { this.levelType = levelType; } @Override protected void onReady() { super.onReady(); OmicronCLI.get().addGameListener(new GameListener() { @Override public void onNewTurn(final Turn currentTurn) { if (!hasUnits) setHomeOffset(); } }); } @Override protected void drawForeground(final Screen screen) { super.drawForeground(screen); Optional<IGameController> gameController = OmicronCLI.get().getGameController(); if (!gameController.isPresent()) return; Optional<IPlayer> localPlayerOptional = OmicronCLI.get().getLocalPlayer(); if (!localPlayerOptional.isPresent()) return; IPlayer localPlayer = localPlayerOptional.get(); // Create an empty grid. Size levelSize = gameController.get().getGame().getLevel(getLevelType()).getSize(); Table<Integer, Integer, ITile> grid = HashBasedTable.create(levelSize.getHeight(), levelSize.getWidth()); // Iterate observable tiles and populate the grid. localPlayer.observableTiles().forEach(tile -> { Vec2 coordinate = positionToMapCoordinate(tile.getPosition()); grid.put(coordinate.getY(), coordinate.getX(), tile); }); // Draw grid in view. Box contentBox = getContentBoxOnScreen(); for (int screenX = contentBox.getLeft(); screenX <= contentBox.getRight(); ++screenX) for (int screenY = contentBox.getTop(); screenY <= contentBox.getBottom(); ++screenY) { int tileY = screenY - contentBox.getTop() + getOffset().getY(); int tileX = screenX - contentBox.getLeft() + getOffset().getX(); if (!levelSize.isInBounds(Vec2.create(tileX, tileY))) continue; ITile tile = grid.get(tileY, tileX); Maybe<? extends IGameObject> contents; Terminal.Color bgColor = getBackgroundColor(); if (tile == null) contents = Maybe.empty(); else { contents = tile.getContents(); bgColor = levelTypeColors.get(tile.getLevel().getType()); for (final ResourceType resourceType : ResourceType.values()) { Maybe<Integer> resourceQuantity = tile.getResourceQuantity(resourceType); if (resourceQuantity.presence() == Maybe.Presence.PRESENT) bgColor = resourceTypeColors.get(resourceType); } } screen.putString(screenX + (screenY % 2 == 0 ? 0 : 1), screenY, contents.presence() == Maybe.Presence.PRESENT ? contents.get().getType().getTypeName().substring(0, 1) : " ", getMapColor(), bgColor, ScreenCharacterStyle.Bold); // Draw off-screen warning labels. } Inset offScreen = new Inset(Math.max(0, getOffset().getY()), Math.max(0, levelSize.getWidth() - contentBox.getSize().getWidth() - getOffset().getX() + 1), Math.max(0, levelSize.getHeight() - contentBox.getSize().getHeight() - getOffset().getY() - 1), Math.max(0, getOffset().getX())); int centerX = contentBox.getLeft() + (levelSize.getWidth() - offScreen.getHorizontal()) / 2 - getOffset().getX() + offScreen.getLeft(); int centerY = contentBox.getTop() + (levelSize.getHeight() - offScreen.getVertical()) / 2 - getOffset().getY() + offScreen.getTop(); centerX = Math.min(contentBox.getRight() - 3, Math.max(contentBox.getLeft(), centerX)); centerY = Math.min(contentBox.getBottom() - 1, Math.max(contentBox.getTop() + 1, centerY)); if (offScreen.getTop() > 0) screen.putString(centerX, contentBox.getTop(), // String.format("%+d", offScreen.getTop()), getInfoTextColor(), getInfoBackgroundColor()); if (offScreen.getRight() > 0) { String label = String.format("%+d", offScreen.getRight()); screen.putString(contentBox.getRight() - label.length(), centerY, // label, getInfoTextColor(), getInfoBackgroundColor()); } if (offScreen.getBottom() > 0) screen.putString(centerX, contentBox.getBottom(), // String.format("%+d", offScreen.getBottom()), getInfoTextColor(), getInfoBackgroundColor()); if (offScreen.getLeft() > 0) screen.putString(contentBox.getLeft(), centerY, // String.format("%+d", offScreen.getLeft()), getInfoTextColor(), getInfoBackgroundColor()); } @Override protected boolean onKey(final Key key) { if (key.getKind() == Key.Kind.ArrowUp && key.isCtrlPressed()) { setOffset(getOffset().translate(0, -1)); return true; } if (key.getKind() == Key.Kind.ArrowDown && key.isCtrlPressed()) { setOffset(getOffset().translate(0, 1)); return true; } if (key.getKind() == Key.Kind.ArrowLeft && key.isCtrlPressed()) { setOffset(getOffset().translate(-1, 0)); return true; } if (key.getKind() == Key.Kind.ArrowRight && key.isCtrlPressed()) { setOffset(getOffset().translate(1, 0)); return true; } if (key.getKind() == Key.Kind.Home && key.isCtrlPressed()) { setHomeOffset(); return true; } return false; } private void setHomeOffset() { Optional<IPlayer> localPlayerOptional = OmicronCLI.get().getLocalPlayer(); Stream<? extends IGameObject> gameObjects = Stream.empty(); if (localPlayerOptional.isPresent()) gameObjects = localPlayerOptional.get().getController().playerObjectsObservable(); setOffset(gameObjects .filter(gameObject -> gameObject.getLocation().get().getLevel().getType() == getLevelType()) .map(gameObject -> { // Transform game objects into their offset from the center of the map. hasUnits = true; Box contentBox = getContentBoxOnScreen(); return positionToMapCoordinate(gameObject.getLocation().get().getPosition()) // .translate(-contentBox.getSize().getWidth() / 2, -contentBox.getSize().getHeight() / 2); }).findFirst().orElseGet(() -> { // If there is no game object in this level, go to the map's center. hasUnits = false; Box contentBox = getContentBoxOnScreen(); return Vec2.create(contentBox.getSize().getWidth() / 2, contentBox.getSize().getHeight() / 2); })); } private static Vec2 positionToMapCoordinate(final Vec2 position) { int y = position.getY(); int x = position.getX() + y / 2; return Vec2.create(x, y); } @Override public String getBackgroundPattern() { return ifNotNullElse(backgroundPattern, getTheme().mapBgPattern()); } @Override public void setBackgroundPattern(@Nullable final String backgroundPattern) { this.backgroundPattern = backgroundPattern; } public Terminal.Color getMapColor() { return ifNotNullElse(mapColor, getTheme().mapFg()); } public void setMapColor(@Nullable final Terminal.Color mapColor) { this.mapColor = mapColor; } public LevelType getLevelType() { return levelType; } public void setLevelType(final LevelType levelType) { this.levelType = levelType; } @Nonnull public Vec2 getOffset() { return offset; } public void setOffset(@Nonnull final Vec2 offset) { this.offset = offset; } public Vec2 getCenterTile() { Size contentBox = getContentBoxOnScreen().getSize(); return offset.translate(contentBox.getWidth() / 2, contentBox.getHeight() / 2); } }