com.strategames.catchdastars.screens.editor.GameEditorScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.strategames.catchdastars.screens.editor.GameEditorScreen.java

Source

/**
 * 
 * Copyright 2014 Martijn Brekhof
 *
 * This file is part of Catch Da Stars.
 *
 * Catch Da Stars is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Catch Da Stars is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Catch Da Stars.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.strategames.catchdastars.screens.editor;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.utils.Align;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Scaling;
import com.strategames.catchdastars.game.CatchDaStars;
import com.strategames.engine.game.GameEngine;
import com.strategames.engine.gameobject.types.Door;
import com.strategames.engine.interfaces.OnLevelsReceivedListener;
import com.strategames.engine.scenes.scene2d.Stage;
import com.strategames.engine.scenes.scene2d.ui.EventHandler.ActorListener;
import com.strategames.engine.scenes.scene2d.ui.GridLayout;
import com.strategames.engine.scenes.scene2d.ui.ScreenshotImage;
import com.strategames.engine.scenes.scene2d.ui.TextButton;
import com.strategames.engine.screens.AbstractScreen;
import com.strategames.engine.storage.Files;
import com.strategames.engine.storage.GameMetaData;
import com.strategames.engine.storage.GameWriter;
import com.strategames.engine.storage.LevelLoader;
import com.strategames.engine.storage.LevelWriter;
import com.strategames.engine.utils.Game;
import com.strategames.engine.utils.Level;
import com.strategames.engine.utils.ScreenBorder;
import com.strategames.engine.utils.Textures;
import com.strategames.ui.dialogs.ConfirmationDialog;
import com.strategames.ui.dialogs.Dialog;
import com.strategames.ui.dialogs.Dialog.OnClickListener;
import com.strategames.ui.dialogs.EditLevelDialog;
import com.strategames.ui.dialogs.ErrorDialog;

import java.util.Collection;

public class GameEditorScreen extends AbstractScreen
        implements Dialog.OnClickListener, OnLevelsReceivedListener, ActorListener {
    private GridLayout levelButtonsGrid;
    private Vector2 gridElementSize = new Vector2(40f, 64f);
    private Level editingLevel;
    private Pixmap emptyLevelImage;
    private ScrollPane scrollPane;
    private Textures textures = Textures.getInstance();

    private class ArrowImage {
        private TextureRegion texture;
        private int alignment;

        /**
         *
         * @param texture of arrow image
         * @param alignment from {@link Align}
         */
        public ArrowImage(TextureRegion texture, int alignment) {
            this.texture = texture;
            this.alignment = alignment;
        }
    }

    public GameEditorScreen(GameEngine game) {
        super(game);
        setTitle(new Label("Level editor", getSkin()));
    }

    @Override
    protected void setupUI(Stage stage) {
        Game game = getGameEngine().getGame();
        Skin skin = getSkin();

        addMenuItem("Play game");
        addMenuItem("Save game");
        addMenuItem("Delete game");
        addMenuItem("Export game");

        Array<Level> localLevels = LevelLoader.loadAllLocalLevels(game.getGameMetaData());
        game.clearLevels();
        for (Level level : localLevels) {
            level.setGameMetaData(game.getGameMetaData());
            game.setLevel(level);
        }

        this.levelButtonsGrid = new GridLayout();

        //Center button grid in scrollpane
        //      this.levelButtonsGrid.setPosition((stage.getWidth() / 2f)-12f, 180f);
        this.levelButtonsGrid.setElementSize(gridElementSize.x, gridElementSize.y);
        this.levelButtonsGrid.setCenter(true);

        this.emptyLevelImage = new Pixmap((int) gridElementSize.x, (int) gridElementSize.y, Format.RGBA8888);
        this.emptyLevelImage.setColor(0, 1, 0, 0.3f);
        this.emptyLevelImage.fill();

        this.scrollPane = new ScrollPane(this.levelButtonsGrid, skin);
        this.scrollPane.setHeight(400f);
        this.scrollPane.setWidth(stage.getWidth());
        this.scrollPane.setPosition(0, 200f);
        stage.addActor(this.scrollPane);

        TextButton button = new TextButton("Main menu", skin);
        button.setWidth(stage.getWidth());

        button.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                getGameEngine().showMainMenu();
            }
        });
        stage.addActor(button);
    }

    @Override
    protected void setupActors(Stage stage) {

    }

    @Override
    public void show() {
        super.show();
        Game game = getGameEngine().getGame();
        GameMetaData gameMetaData = game.getGameMetaData();

        // We use 1000 to not intervene with any other saved game progressions
        // assuming user does not store more then 999 progressions
        gameMetaData.setSavedGameProgressNumber(1000);
        GameWriter.deleteInprogress(gameMetaData);
        getGameEngine().setTestMode(true);
        game.reset();

        if (editingLevel != null) {
            updateGame();
        }

        Collection<Level> levelsArrayList = game.getLevels().values();
        createLevelsOverview(levelsArrayList);

        this.scrollPane.scrollToCenter(30, 40, 100, 100);
    }

    @Override
    public void onClick(Dialog dialog, int which) {
        if (dialog instanceof EditLevelDialog) {
            final Color colorWhite = getSkin().getColor("white");
            final Level level = ((EditLevelDialog) dialog).getLevel();
            final ScreenshotImage image = (ScreenshotImage) dialog.getTag();
            switch (which) {
            case EditLevelDialog.BUTTON_COPY_CLICKED:
                copyLevel(level);
                dialog.remove();
                image.setColor(colorWhite);
                break;
            case EditLevelDialog.BUTTON_DELETELEVEL_CLICKED:
                /**
                 * TODO Add check to see if deleting level does not make
                 * other levels unreachable
                 */
                int[] pos = level.getPosition();
                deleteLevel(level);
                ScreenshotImage newImage = createLevelImage(null);
                this.levelButtonsGrid.set(pos[0], pos[1], newImage);
                dialog.remove();
                break;
            case EditLevelDialog.BUTTON_CLOSE_CLICKED:
                dialog.remove();
                image.setColor(colorWhite);
                break;
            }
        }
    }

    @Override
    public void levelsReceived(String json) {
        Collection<Level> levels = null;

        /**crec
         * TODO: refactor to support new Game and GameMetaData class setup
         */
        try {
            //         levels = GameLoader.getGame(json).getLevels().values();
        } catch (Exception e) {
            showErrorDialog("Error importing", "Failed to import levels");
            return;
        }

        GameMetaData gameMetaData = getGameEngine().getGame().getGameMetaData();
        boolean levelsFailedToSave = false;
        if (GameWriter.deleteOriginal(gameMetaData)) {
            for (Level level : levels) {
                if (!LevelWriter.saveOriginal(level)) {
                    levelsFailedToSave = true;
                }
            }
        } else {
            showErrorDialog("Error deleting directory", "Failed to delete directory holding the levels");
        }

        if (levelsFailedToSave) {
            showErrorDialog("Error saving levels", "Failed to save one or more levels");
        } else {
            createLevelsOverview(levels);
        }
    }

    private void copyLevel(Level level) {
        Level newLevel = level.copy();
        /**
         * TODO
         * User should select other level
         * If level is already filled ask user for confirmation
         * otherwise simply create a copy at new position
         */

    }

    private void startLevelEditor(ScreenshotImage image) {
        CatchDaStars gameEngine = (CatchDaStars) getGameEngine();
        Game game = gameEngine.getGame();
        Level level = null;

        try {
            level = (Level) image.getTag();
        } catch (ClassCastException e) {
            ErrorDialog dialog = new ErrorDialog(getStageUIActors(), "Starting level editor failed", getSkin());
            dialog.create();
            dialog.show();
            Gdx.app.log("GameEditorScreen", "startLevelEditor: " + e.getMessage());
            return;
        }

        //Make sure new levels are saved to disk
        if (!Files.originalLevelExists(level)) {
            if (!LevelWriter.saveOriginal(level)) {
                ErrorDialog dialog = new ErrorDialog(getStageUIActors(), "Failed to save level", getSkin());
                dialog.create();
                dialog.show();
                return;
            }
        }

        game.setLevel(level);
        game.setCurrentLevelPosition(level.getPosition());
        editingLevel = level;
        gameEngine.showLevelEditor();
    }

    private void startEditLevelDialog(ScreenshotImage image) {

        Object tag = image.getTag();
        if (!(tag instanceof Level)) {
            return;
        }

        Dialog dialog = new EditLevelDialog(getStageUIActors(), getSkin(), (Level) tag);
        dialog.create();
        dialog.setTag(image);
        dialog.setPosition(image.getX(), image.getY());
        dialog.setOnClickListener(this);
        dialog.show();

        Color selectedColor = getSkin().getColor("red");
        image.setColor(selectedColor);
    }

    private void deleteLevel(Level level) {
        Game game = getGameEngine().getGame();
        LevelWriter.deleteOriginal(level);
        int[] pos = level.getPosition();
        game.deleteLevel(pos[0], pos[1]);
    }

    /**
     * TODO replace loading all levels completely by something less memory hungry. We only need level position, doors, and name.
     * Also add caching to prevent loading the complete game from scratch each time we show this screen
     */
    private void createLevelsOverview(Collection<Level> levels) {
        this.levelButtonsGrid.clear();

        if ((levels == null) || (levels.size() == 0)) {
            Level level = createNewLevel(0, 0);
            ScreenshotImage image = createLevelImage(level);
            this.levelButtonsGrid.set(0, 0, image);
        } else {
            for (Level level : levels) {
                int[] position = level.getPosition();
                ScreenshotImage image = (ScreenshotImage) this.levelButtonsGrid.get(position[0], position[1]);
                if (image == null) {
                    image = createLevelImage(level);
                    this.levelButtonsGrid.set(position[0], position[1], image);
                }
                if (level.isReachable()) {
                    handleDoors(level);
                } else {
                    image.setColor(1f, 0.2f, 0.2f, 1f);
                }
            }
        }
    }

    private ScreenshotImage createLevelImage(Level level) {
        Vector2 elementSize = this.levelButtonsGrid.getElementSize();
        Texture texture = LevelLoader.loadScreenShot(level);
        if (texture == null) {
            texture = new Texture(emptyLevelImage);
        }

        return createScreenshotImage(texture, level, (int) elementSize.x, (int) elementSize.y);
    }

    private ScreenshotImage createScreenshotImage(Texture texture, Level level, int width, int height) {
        ScreenshotImage image = new ScreenshotImage(texture);
        image.setTag(level);
        image.setScaling(Scaling.fit);
        image.setSize(width, height);
        image.setListener(this);
        return image;
    }

    private void handleDoors(Level level) {
        Vector2 elementSize = this.levelButtonsGrid.getElementSize();
        Vector2 overlaySize = new Vector2(elementSize.x / 3f, 0);

        int[] levelPosition = level.getPosition();

        Array<Door> doors = level.getDoors();
        for (Door door : doors) {
            int[] nextLevelPosition = door.getAccessToPosition();
            ScreenshotImage nextLevelImage = (ScreenshotImage) this.levelButtonsGrid.get(nextLevelPosition[0],
                    nextLevelPosition[1]);
            if (nextLevelImage == null) {
                Level newLevel = createNewLevel(nextLevelPosition[0], nextLevelPosition[1]);
                nextLevelImage = createLevelImage(newLevel);
                nextLevelImage.setTag(newLevel);
                this.levelButtonsGrid.set(nextLevelPosition[0], nextLevelPosition[1], nextLevelImage);
            }
            ArrowImage arrowImage = getArrow(level.getPosition(), nextLevelPosition);
            if (arrowImage != null) {
                overlaySize.y = (overlaySize.x * arrowImage.texture.getRegionHeight())
                        / arrowImage.texture.getRegionWidth();
                nextLevelImage.addOverlay(arrowImage.texture, overlaySize, arrowImage.alignment);
            }

            ((Level) nextLevelImage.getTag()).addAccessibleBy(levelPosition[0], levelPosition[1]);
        }
    }

    private ArrowImage getArrow(int[] curPos, int[] nextPos) {
        if (nextPos[0] < curPos[0]) {
            return new ArrowImage(this.textures.arrowLeft, Align.right | Align.center);
        } else if (nextPos[0] > curPos[0]) {
            return new ArrowImage(this.textures.arrowRight, Align.left | Align.center);
        } else if (nextPos[1] < curPos[1]) {
            return new ArrowImage(this.textures.arrowBottom, Align.top | Align.center);
        } else if (nextPos[1] > curPos[1]) {
            return new ArrowImage(this.textures.arrowTop, Align.bottom | Align.center);
        }
        return null;
    }

    private void showErrorDialog(String title, String message) {
        ErrorDialog dialog = new ErrorDialog(getStageUIActors(), title, getSkin());
        dialog.setMessage(message);
        dialog.setCenter(true);
        dialog.create();
        dialog.show();
    }

    private void deleteAllLevels() {
        Game game = getGameEngine().getGame();
        Boolean success = true;
        Collection<Level> levels = game.getLevels().values();
        for (Level level : levels) {
            if (!LevelWriter.deleteOriginal(level)) {
                success = false;
                Gdx.app.log("LevelEditorMenuScreen", "Failed to delete level " + level.getFilename());
            }
            if (!LevelWriter.deleteScreenshot(level)) {
                success = false;
                Gdx.app.log("LevelEditorMenuScreen", "Failed to delete screenshot for " + level.getFilename());
            }
        }
        if (!success) {
            ErrorDialog dialog = new ErrorDialog(getStageUIActors(), "Failed to delete some levels", getSkin());
            dialog.create().show();
        }

        createLevelsOverview(game.getLevels().values());
    }

    @Override
    protected void onMenuItemSelected(String text) {
        GameEngine gameEngine = getGameEngine();
        GameMetaData gameMetaData = gameEngine.getGame().getGameMetaData();
        if (text.contentEquals("Export game")) {
            getGameEngine().getExporterImporter().export(gameMetaData.getJson());
        } else if (text.contentEquals("Play game")) {
            Gdx.app.log("GameEditorScreen", "playing game: " + gameMetaData);
            gameEngine.startLevel(new int[] { 0, 0 });
        } else if (text.contentEquals("Delete game")) {
            //ask for confirmation
            ConfirmationDialog dialog = new ConfirmationDialog(getStageUIActors(), "This will delete all levels",
                    getSkin());
            dialog.setPositiveButton("Delete", new OnClickListener() {

                @Override
                public void onClick(Dialog dialog, int which) {
                    dialog.remove();
                    deleteAllLevels();
                }
            });
            dialog.setNegativeButton("Cancel", new OnClickListener() {

                @Override
                public void onClick(Dialog dialog, int which) {
                    dialog.remove();
                }
            });
            dialog.create();
            dialog.show();

        } else if (text.contentEquals("Save game")) {

        }
        hideMainMenu();
    }

    private Level createNewLevel(int x, int y) {
        CatchDaStars game = (CatchDaStars) getGameEngine();
        Vector3 worldSize = game.getWorldSize();
        Level level = new Level();
        level.setWorldSize(new Vector2(worldSize.x, worldSize.y));
        level.setViewSize(new Vector2(game.getViewSize()));
        level.setReachable(true); //assume level can only be created if reachable
        level.setPosition(x, y);

        ScreenBorder.create(level, game);

        level.setGameMetaData(game.getGame().getGameMetaData());

        return level;
    }

    @Override
    public void onTap(Actor actor) {
        if (actor instanceof ScreenshotImage) {
            startLevelEditor((ScreenshotImage) actor);
        }
    }

    @Override
    public void onLongPress(Actor actor) {
        if (actor instanceof ScreenshotImage) {
            startEditLevelDialog((ScreenshotImage) actor);
        }
    }

    private void updateGame() {
        Game game = getGameEngine().getGame();
        // reload level to include added gameobjects
        Level editedLevel = LevelLoader.loadSync(game.getGameMetaData(), editingLevel.getPosition());
        game.setLevel(editedLevel);

        Array<Door> doors = editedLevel.getDoors();
        Array<Door> doorsRemoved = editingLevel.getDoors();

        //Remove all identical doors in new and old
        //All remaining doors were removed from the level
        doorsRemoved.removeAll(doors, false);

        int[] editedLevelPos = editedLevel.getPosition();

        //Handle previously accessible levels from edited level
        for (Door door : doorsRemoved) {
            int[] pos = door.getAccessToPosition();
            Level level = game.getLevel(pos[0], pos[1]);
            if (level != null) {
                level.delAccessibleBy(editedLevelPos[0], editedLevelPos[1]);
            }
        }

        for (Door door : doors) {
            int[] pos = door.getAccessToPosition();
            Level level = game.getLevel(pos[0], pos[1]);
            if (level != null) {
                level.addAccessibleBy(editedLevelPos[0], editedLevelPos[1]);
            }
        }

        updateReachability();

        saveGame();
    }

    private void updateReachability() {
        Game game = getGameEngine().getGame();
        Collection<Level> allLevels = game.getLevels().values();
        Array<Level> reachableLevelsFromStart = new Array<Level>();
        Level startLevel = game.getLevel(0, 0);

        game.getLevelsReachable(startLevel, reachableLevelsFromStart);

        for (Level level : allLevels) {
            level.setReachable(false);
        }

        for (Level level : reachableLevelsFromStart) {
            level.setReachable(true);
        }

        //Start level is ALWAYS reachable!
        startLevel.setReachable(true);
    }

    private void saveGame() {
        Game game = getGameEngine().getGame();
        GameMetaData metaData = game.getGameMetaData();
        Collection<Level> allLevels = game.getLevels().values();
        boolean savingFailed = false;
        for (Level level : allLevels) {
            level.setGameMetaData(metaData);
            if (!LevelWriter.saveOriginal(level)) {
                savingFailed = true;
            }
        }
        if (savingFailed) {
            ErrorDialog dialog = new ErrorDialog(getStageUIActors(), "Failed to save game to disk", getSkin());
            dialog.show();
        }
    }
}