com.lyndir.omicron.cli.view.MapView.java Source code

Java tutorial

Introduction

Here is the source code for com.lyndir.omicron.cli.view.MapView.java

Source

/*
 * 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);
    }
}