net.noviden.towerdefense.MapEditor.MapEditorScreen.java Source code

Java tutorial

Introduction

Here is the source code for net.noviden.towerdefense.MapEditor.MapEditorScreen.java

Source

/**
 Infinite Paths : Infinite Tower Defense Game With User Created Maps
 Copyright (C) 2015 Tristan Kernan
    
 This program 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.
    
 This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.noviden.towerdefense.MapEditor;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;

import net.noviden.towerdefense.Maps.Map;
import net.noviden.towerdefense.MapSettings;
import net.noviden.towerdefense.Path;
import net.noviden.towerdefense.Point;
import net.noviden.towerdefense.TowerDefense;
import net.noviden.towerdefense.TurretFactory.BaseTurret;
import net.noviden.towerdefense.UnitFactory.UnitManager;

import java.util.ArrayList;

public class MapEditorScreen implements Screen {

    private final TowerDefense _towerDefense;

    private Stage stage;
    private Map _originalMapReference; // reference to original map, to be overwritten for saving
    private Map _map; // copy of map that is being edited

    private OrthographicCamera _orthoCamera;
    private SpriteBatch _spriteBatch;
    private ShapeRenderer _shapeRenderer;

    private int _selectedPathIndex;

    private boolean _displayUnits;
    private UnitManager[] _unitManagers;
    private MapSettings _mapSettings;

    private Table rootTable;
    private InputMultiplexer inputMultiplexer;

    public MapEditorScreen(final TowerDefense towerDefense, Map pMap) {
        _towerDefense = towerDefense;
        _originalMapReference = pMap;
        _map = pMap.clone();

        _orthoCamera = new OrthographicCamera();
        _orthoCamera.setToOrtho(true, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        _orthoCamera.update();

        _spriteBatch = new SpriteBatch();
        _shapeRenderer = new ShapeRenderer();
        _shapeRenderer.setAutoShapeType(true);

        // make use of map settings to have units travel slightly faster, for display niceties
        _mapSettings = new MapSettings();
        _mapSettings.putValue(MapSettings.UNIT_INITIAL_SPEED_KEY, 150.0f);

        // set units display to true initially
        _displayUnits = true;

        // instantiate/create unit managers for paths
        resetUnitManagers();

        // there must exist at least one path if we are EDITING an existing map
        _selectedPathIndex = 0;

        final Skin skin = new Skin(Gdx.files.internal("assets/uiskin.json"));

        stage = new Stage();

        rootTable = new Table();
        rootTable.setFillParent(true);

        Table pathAddDeleteTable = new Table();

        TextButton saveButton = new TextButton("Save", skin);
        TextButton exitButton = new TextButton("Exit Without Saving", skin);
        TextButton addPathButton = new TextButton("Add Path", skin);
        TextButton deletePathButton = new TextButton("Delete Current Path", skin);
        TextButton deleteLastNodeOnPathButton = new TextButton("Delete End Node", skin);
        TextButton displayOptionsButton = new TextButton("Options", skin);
        TextButton toggleUnitsDisplayButton = new TextButton("Toggle Units Display", skin);

        pathAddDeleteTable.add(deletePathButton);
        pathAddDeleteTable.add(addPathButton);
        pathAddDeleteTable.add(saveButton);
        pathAddDeleteTable.add(exitButton);
        pathAddDeleteTable.row();
        pathAddDeleteTable.add(deleteLastNodeOnPathButton).expandX().left();
        pathAddDeleteTable.add(displayOptionsButton);
        pathAddDeleteTable.add(toggleUnitsDisplayButton);

        Table pathSelectorTable = new Table();

        Texture leftArrowTexture = new Texture(Gdx.files.internal("leftArrow.png"));
        Image leftArrowImage = new Image(leftArrowTexture);
        Texture rightArrowTexture = new Texture(Gdx.files.internal("rightArrow.png"));
        Image rightArrowImage = new Image(rightArrowTexture);

        final Label selectedPathLabel = new Label("0", skin);

        pathSelectorTable.add(leftArrowImage).pad(8.0f);
        pathSelectorTable.add(selectedPathLabel).pad(8.0f);
        pathSelectorTable.add(rightArrowImage).pad(8.0f);

        // options table

        // set up options table
        final Table optionsTable = new Table();
        optionsTable.setVisible(false);

        MapSettings currentSettings = _map.getSettings();

        Label initialPlayerHealthLabel = new Label("Initial Player Health:", skin);
        final TextField initialPlayerHealthText = new TextField(
                "" + (int) currentSettings.getValue(MapSettings.PLAYER_INITIAL_HEALTH_KEY), skin);
        Label initialPlayerResourcesLabel = new Label("Initial Player Resources:", skin);
        final TextField initialPlayerResourcesText = new TextField(
                "" + (int) currentSettings.getValue(MapSettings.PLAYER_INITIAL_RESOURCES_KEY), skin);
        Label initialUnitHealthLabel = new Label("Initial Unit Health:", skin);
        final TextField initialUnitHealthText = new TextField(
                "" + currentSettings.getValue(MapSettings.UNIT_INITIAL_HEALTH_KEY), skin);
        Label initialUnitMovementSpeedLabel = new Label("Initial Unit Movement Speed:", skin);
        final TextField initialUnitMovementSpeedText = new TextField(
                "" + currentSettings.getValue(MapSettings.UNIT_INITIAL_SPEED_KEY), skin);
        Label initialUnitDamageLabel = new Label("Initial Unit Damage:", skin);
        final TextField initialUnitDamageText = new TextField(
                "" + (int) currentSettings.getValue(MapSettings.UNIT_INITIAL_DAMAGE_KEY), skin);
        Label defaultUnitSpawnRateLabel = new Label("Unit Spawn Rate:", skin);
        final TextField defaultUnitSpawnRateText = new TextField(
                "" + currentSettings.getValue(MapSettings.UNIT_SPAWN_RATE_KEY), skin);

        // TODO Implement this button
        TextButton displayWavesCreatorButton = new TextButton("Waves Creator", skin);
        TextButton displayTurretsButton = new TextButton("Disable Turrets", skin);

        optionsTable.add(initialPlayerHealthLabel, initialPlayerHealthText);
        optionsTable.row();
        optionsTable.add(initialPlayerResourcesLabel, initialPlayerResourcesText);
        optionsTable.row();
        optionsTable.add(initialUnitHealthLabel, initialUnitHealthText);
        optionsTable.row();
        optionsTable.add(initialUnitMovementSpeedLabel, initialUnitMovementSpeedText);
        optionsTable.row();
        optionsTable.add(initialUnitDamageLabel, initialUnitDamageText);
        optionsTable.row();
        optionsTable.add(defaultUnitSpawnRateLabel, defaultUnitSpawnRateText);
        optionsTable.row();
        optionsTable.add(displayTurretsButton);

        final Table disableTurretsTable = new Table();
        disableTurretsTable.setVisible(false);

        final CheckBox disableChaingunTurretCheckbox = new CheckBox("Disable Chaingun", skin);
        disableChaingunTurretCheckbox.setChecked(currentSettings.isTurretTypeDisabled(BaseTurret.Type.CHAINGUN));

        final CheckBox disableBasicTurretCheckbox = new CheckBox("Disable Basic", skin);
        disableBasicTurretCheckbox.setChecked(currentSettings.isTurretTypeDisabled(BaseTurret.Type.NORMAL));

        final CheckBox disableBuffTurretCheckbox = new CheckBox("Disable Buff", skin);
        disableBuffTurretCheckbox.setChecked(currentSettings.isTurretTypeDisabled(BaseTurret.Type.BUFF));

        final CheckBox disableHomingTurretCheckbox = new CheckBox("Disable Homing", skin);
        disableHomingTurretCheckbox.setChecked(currentSettings.isTurretTypeDisabled(BaseTurret.Type.HOMING));

        final CheckBox disableRocketTurretCheckbox = new CheckBox("Disable Rocket", skin);
        disableRocketTurretCheckbox.setChecked(currentSettings.isTurretTypeDisabled(BaseTurret.Type.ROCKET));

        final CheckBox disableShotgunTurretCheckbox = new CheckBox("Disable Shotgun", skin);
        disableShotgunTurretCheckbox.setChecked(currentSettings.isTurretTypeDisabled(BaseTurret.Type.SHOTGUN));

        disableTurretsTable.add(disableChaingunTurretCheckbox);
        disableTurretsTable.row();
        disableTurretsTable.add(disableBasicTurretCheckbox);
        disableTurretsTable.row();
        disableTurretsTable.add(disableBuffTurretCheckbox);
        disableTurretsTable.row();
        disableTurretsTable.add(disableHomingTurretCheckbox);
        disableTurretsTable.row();
        disableTurretsTable.add(disableRocketTurretCheckbox);
        disableTurretsTable.row();
        disableTurretsTable.add(disableShotgunTurretCheckbox);

        // transformations table
        TextButton displayTransformationsTableButton = new TextButton("Enable Transformations", skin);

        CheckBox enableRandomTransformationCheckbox = new CheckBox("Random", skin);
        CheckBox enableTurretTransformationCheckbox = new CheckBox("Turret", skin);
        CheckBox enableUnitTransformationCheckbox = new CheckBox("Unit", skin);

        final Table transformationsTable = new Table();
        transformationsTable.setVisible(false);

        transformationsTable.add(enableRandomTransformationCheckbox).row();
        transformationsTable.add(enableTurretTransformationCheckbox).row();
        transformationsTable.add(enableUnitTransformationCheckbox).row();

        optionsTable.row();
        optionsTable.add(disableTurretsTable);
        optionsTable.row();
        optionsTable.add(displayTransformationsTableButton);
        optionsTable.row();
        optionsTable.add(transformationsTable);

        // finally, add tables to root table and set input processing

        rootTable.add(pathSelectorTable).expandX().left().expandY().top();

        rootTable.add(pathAddDeleteTable).expandX().right().expandY().top();

        rootTable.row();

        rootTable.add(optionsTable).expandX().right().expandY().top();

        stage.addActor(rootTable);

        inputMultiplexer = new InputMultiplexer(stage, new GestureDetector(new MyGestureListener()));

        Gdx.input.setInputProcessor(inputMultiplexer);

        // listeners
        saveButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                int index = _towerDefense.maps.indexOf(_originalMapReference);
                String message = "";

                int playerHealth = Integer.parseInt(initialPlayerHealthText.getText()),
                        playerResources = Integer.parseInt(initialPlayerResourcesText.getText()),
                        unitDamage = Integer.parseInt(initialUnitDamageText.getText());
                float unitHealth = Float.parseFloat(initialUnitHealthText.getText()),
                        unitSpeed = Float.parseFloat(initialUnitMovementSpeedText.getText()),
                        unitSpawnRate = Float.parseFloat(defaultUnitSpawnRateText.getText());

                // validation
                if (playerHealth <= 0.0f) {
                    displayErrorMessage("Error! Player cannot start with 0.0 health", skin);
                    return;
                } else if (unitHealth <= 0.0f) {
                    displayErrorMessage("Error! Units cannot start with 0.0 health", skin);
                    return;
                } else if (unitDamage <= 0.0f) {
                    displayErrorMessage("Error! Units cannot start with 0.0 damage", skin);
                    return;
                } else if (unitSpeed <= 0.0f) {
                    displayErrorMessage("Error! Units cannot start with 0.0 speed", skin);
                    return;
                } else if (unitSpawnRate <= 0.0f) {
                    displayErrorMessage("Error! Spawn rate cannot be 0.0", skin);
                    return;
                }

                ArrayList<BaseTurret.Type> disabledTypes = new ArrayList<BaseTurret.Type>();

                if (disableBasicTurretCheckbox.isChecked()) {
                    disabledTypes.add(BaseTurret.Type.NORMAL);
                }
                if (disableBuffTurretCheckbox.isChecked()) {
                    disabledTypes.add(BaseTurret.Type.BUFF);
                }
                if (disableChaingunTurretCheckbox.isChecked()) {
                    disabledTypes.add(BaseTurret.Type.CHAINGUN);
                }
                if (disableHomingTurretCheckbox.isChecked()) {
                    disabledTypes.add(BaseTurret.Type.HOMING);
                }
                if (disableRocketTurretCheckbox.isChecked()) {
                    disabledTypes.add(BaseTurret.Type.ROCKET);
                }
                if (disableShotgunTurretCheckbox.isChecked()) {
                    disabledTypes.add(BaseTurret.Type.SHOTGUN);
                }

                if (disabledTypes.size() == BaseTurret.Type.values().length) {
                    displayErrorMessage("Error! Cannot disable all turrets", skin);
                    return;
                }

                MapSettings mapSettings = new MapSettings();

                BaseTurret.Type[] disabledTypesArr = new BaseTurret.Type[disabledTypes.size()];
                disabledTypesArr = disabledTypes.toArray(disabledTypesArr);

                mapSettings.setDisabledTurretTypes(disabledTypesArr);

                String[] keys = { MapSettings.PLAYER_INITIAL_HEALTH_KEY, MapSettings.PLAYER_INITIAL_RESOURCES_KEY,
                        MapSettings.UNIT_INITIAL_DAMAGE_KEY, MapSettings.UNIT_INITIAL_HEALTH_KEY,
                        MapSettings.UNIT_INITIAL_SPEED_KEY, MapSettings.UNIT_SPAWN_RATE_KEY };

                float[] values = { playerHealth, playerResources, unitDamage, unitHealth, unitSpeed,
                        unitSpawnRate };

                mapSettings.putValues(keys, values);

                // save settings to map
                _map.setSettings(mapSettings);

                // finally, save map
                if (index >= 0) {
                    _towerDefense.maps.set(index, _map);
                    message = "Saved over old map.";
                } else {
                    _towerDefense.maps.add(_map);
                    message = "Something weird happened, saved new copy of map.";
                }

                _originalMapReference = _map;
                _map = _map.clone();

                Dialog saveSucceeded = new Dialog("Success!", skin);
                saveSucceeded.text(message);
                saveSucceeded.button("Continue");
                saveSucceeded.show(stage);
            }
        });

        exitButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                _towerDefense.setScreen(new MapEditorSelectorScreen(_towerDefense));
                dispose();
            }
        });

        addPathButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Path[] paths = _map.getPaths();

                // make a copy of old paths array, and add a new path to the end
                Path[] updatedPaths = new Path[paths.length + 1];

                for (int i = 0; i < paths.length; i++) {
                    updatedPaths[i] = paths[i];
                }

                updatedPaths[updatedPaths.length - 1] = new Path(new ArrayList<Point>(), 5.0f); // TODO remove magic number

                _map.setPaths(updatedPaths);

                _selectedPathIndex = updatedPaths.length - 1;
                resetUnitManagers();

                selectedPathLabel.setText("" + _selectedPathIndex);
            }
        });

        deletePathButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Path[] paths = _map.getPaths();

                if (paths.length < 1) {
                    return;
                }

                // make a new array and set it to the map
                Path[] updatedPaths = new Path[paths.length - 1];
                int offset = 0;

                for (int i = 0; i < paths.length; i++) {
                    if (i == _selectedPathIndex) {
                        offset = -1;

                        continue;
                    }

                    updatedPaths[i + offset] = paths[i];
                }

                _map.setPaths(updatedPaths);
                resetUnitManagers();
                if (_selectedPathIndex > 0) {
                    _selectedPathIndex--;
                }

                selectedPathLabel.setText("" + _selectedPathIndex);
            }
        });

        deleteLastNodeOnPathButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                Path[] paths = _map.getGenericPaths();

                if (_selectedPathIndex >= paths.length) {
                    return;
                }

                Path selectedPath = paths[_selectedPathIndex];
                if (selectedPath.set.size() > 0) {
                    selectedPath.set.remove(selectedPath.set.size() - 1);

                    resetUnitManagers();
                }
            }
        });

        leftArrowImage.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                if (_selectedPathIndex > 0) {
                    _selectedPathIndex--;
                    selectedPathLabel.setText("" + _selectedPathIndex);
                }
            }
        });

        rightArrowImage.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                if (_selectedPathIndex < (_map.getPaths().length - 1)) {
                    _selectedPathIndex++;
                    selectedPathLabel.setText("" + _selectedPathIndex);
                }
            }
        });

        displayOptionsButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                optionsTable.setVisible(!optionsTable.isVisible());
            }
        });

        displayTurretsButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                disableTurretsTable.setVisible(!disableTurretsTable.isVisible());
            }
        });

        displayTransformationsTableButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                transformationsTable.setVisible(!transformationsTable.isVisible());
            }
        });

        toggleUnitsDisplayButton.addListener(new ClickListener() {
            @Override
            public void clicked(InputEvent event, float x, float y) {
                _displayUnits = !_displayUnits;
            }
        });
    }

    public void render(float deltaTime) {
        Gdx.gl.glClearColor(0, 0, 0.2f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        _orthoCamera.update();
        _spriteBatch.setProjectionMatrix(_orthoCamera.combined);
        _shapeRenderer.setProjectionMatrix(_orthoCamera.combined);

        _spriteBatch.begin();
        _shapeRenderer.begin();

        Path[] paths = _map.getPaths();
        for (int i = 0; i < paths.length; i++) {
            if (i == _selectedPathIndex) {
                _shapeRenderer.setColor(Color.GREEN);
            } else {
                _shapeRenderer.setColor(Color.BLUE);
            }

            for (int j = 0; j < (paths[i].set.size() - 1); j++) {
                Point a = paths[i].set.get(j), b = paths[i].set.get(j + 1);
                _shapeRenderer.line(a.x, a.y, b.x, b.y);

                _shapeRenderer.circle(a.x, a.y, 5.0f);
            }

            if (paths[i].set.size() > 0) {
                Point lastPoint = paths[i].set.get(paths[i].set.size() - 1);

                float length = 12.0f;
                _shapeRenderer.triangle(lastPoint.x, lastPoint.y, lastPoint.x - length, lastPoint.y - length,
                        lastPoint.x, lastPoint.y - length);
                _shapeRenderer.triangle(lastPoint.x, lastPoint.y, lastPoint.x + length, lastPoint.y + length,
                        lastPoint.x, lastPoint.y + length);
            }
        }

        if (_displayUnits) {
            for (UnitManager unitManager : _unitManagers) {
                if (unitManager != null) {
                    unitManager.act(deltaTime, null);
                    unitManager.draw(_shapeRenderer);
                }
            }
        }

        _shapeRenderer.end();
        _spriteBatch.end();

        stage.act(deltaTime);
        stage.draw();
    }

    private void resetUnitManagers() {
        Path[] paths = _map.getPaths();
        _unitManagers = new UnitManager[paths.length];
        for (int i = 0; i < _unitManagers.length; i++) {
            if (paths[i].set.size() > 1) {
                _unitManagers[i] = new UnitManager(paths[i], _mapSettings);
            } else {
                _unitManagers[i] = null;
            }
        }
    }

    private void displayErrorMessage(String message, Skin skin) {
        Dialog errorDialog = new Dialog("Error!", skin);
        errorDialog.text(message);
        errorDialog.button("Ok");
        errorDialog.show(stage);
    }

    public void pause() {
    }

    public void resume() {
    }

    public void show() {
    }

    public void hide() {
    }

    public void dispose() {
    }

    public void resize(int width, int height) {

        stage.dispose();
        stage = new Stage();
        stage.addActor(rootTable);

        inputMultiplexer.clear();
        inputMultiplexer = new InputMultiplexer(stage, new GestureDetector(new MyGestureListener()));

        Gdx.input.setInputProcessor(inputMultiplexer);

        _orthoCamera.setToOrtho(true, width, height);

        resetUnitManagers();
    }

    private class MyGestureListener implements GestureDetector.GestureListener {
        @Override
        public boolean touchDown(float x, float y, int pointer, int button) {

            return false;
        }

        @Override
        public boolean tap(float screenX, float screenY, int count, int button) {

            // add latest `click` to path set

            Vector3 vector = new Vector3(screenX, screenY, 0);
            _orthoCamera.unproject(vector);

            Point target = new Point(vector.x, vector.y);
            target.x /= Gdx.graphics.getWidth();
            target.y /= Gdx.graphics.getHeight();

            if (_selectedPathIndex < _map.getNumPaths()) {
                _map.getRealPath(_selectedPathIndex).set.add(target);
                resetUnitManagers();
            }

            return true;
        }

        @Override
        public boolean longPress(float x, float y) {

            return false;
        }

        @Override
        public boolean fling(float velocityX, float velocityY, int button) {

            return false;
        }

        @Override
        public boolean pan(float x, float y, float deltaX, float deltaY) {

            return false;
        }

        @Override
        public boolean panStop(float x, float y, int pointer, int button) {

            return false;
        }

        @Override
        public boolean zoom(float initialDistance, float distance) {

            return false;
        }

        @Override
        public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {

            return false;
        }
    }
}