niwa99/Tower-Defense

View on GitHub
app/src/main/java/de/dhbw/game/Game.java

Summary

Maintainability
F
4 days
Test Coverage
package de.dhbw.game;

import android.app.AlertDialog;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Timer;
import java.util.TimerTask;

import de.dhbw.ImageElevation;
import de.dhbw.activities.GameActivity;
import de.dhbw.R;
import de.dhbw.game.match.AMatch;
import de.dhbw.game.match.EasyMatch;
import de.dhbw.game.match.HardMatch;
import de.dhbw.game.match.MediumMatch;
import de.dhbw.game.popups.MenuSettings;
import de.dhbw.game.popups.MenuTowerSelection;
import de.dhbw.game.popups.MenuUpgradeAndSell;
import de.dhbw.game.settings.Settings;
import de.dhbw.game.wave.AWave;
import de.dhbw.map.matchfield.MatchField;
import de.dhbw.map.objects.tower.ATower;
import de.dhbw.map.objects.tower.TowerArtillery;
import de.dhbw.map.objects.tower.TowerLaser;
import de.dhbw.map.objects.tower.TowerBoombastic;
import de.dhbw.map.objects.tower.TowerFreezer;
import de.dhbw.map.objects.tower.TowerPlasmarizer;
import de.dhbw.map.objects.tower.TowerType;
import de.dhbw.map.structure.Field;
import de.dhbw.map.structure.FieldDescription;
import de.dhbw.map.structure.MapStructure;
import de.dhbw.util.Constants;
import de.dhbw.util.Position;
import de.dhbw.util.PreferenceManager;

import static de.dhbw.util.Constants.FIELD_SIZE;
import static de.dhbw.util.Constants.STATUS_OFF;
import static de.dhbw.util.Constants.STATUS_ON;
import static de.dhbw.util.Constants.startGameDelay;

public class Game {

    private GameActivity gameActivity;

    private MapStructure mapStructure;
    private MatchField matchField;
    private AMatch match;

    private Optional<Button> clickedButton = Optional.ofNullable(null);

    //responsible for organizing waves
    private Timer gameTimer = new Timer();
    //responsible for spawning enemies in waves
    private Timer waveTimer =  new Timer();
    //responsible for status bar timer
    private final StatusBarCountDownTimer countDownTimer;

    private Map<Settings, Boolean> gameSettings = new HashMap<Settings, Boolean>();

    private boolean showCircle = false;
    private Position circleField = new Position(-1, -1);

    private boolean lastWaveOut = false;
    private boolean lastEnemyOfWaveSpawned = false;
    private IMoneyListener moneyListener = null;
    private boolean isMenuOpen = false;
    private int lifePoints = 100;
    private int money = 0;

    private int currentWaveNumber = 0;
    private int numberOfEnemiesKilled = 0;
    private int numberOfBuiltTowers = 0;
    private int numberOfUpgrades = 0;
    private int moneySpent = 0;

    private Button spawnButton;

    private MusicPlayer musicPLayer;

    public Game(GameActivity gameActivity) {
        this.gameActivity = gameActivity;
        mapStructure = new MapStructure();
        matchField = new MatchField(gameActivity);
        countDownTimer = new StatusBarCountDownTimer(gameActivity);
        generateButtonsOnMap();
        loadSettings();
        musicPLayer = new MusicPlayer(gameSettings.get(Settings.MUSIC), gameActivity);
    }

    private void loadSettings(){
        for (Settings setting: Settings.values()) {
            String settings = PreferenceManager.getSettingsValue(setting);
            if (settings == null) {
                PreferenceManager.setSettingsValue(setting, STATUS_ON);
                gameSettings.put(setting, true);
            }else if(settings.equals(STATUS_ON)){
                gameSettings.put(setting, true);
            } else if(settings.equals(STATUS_OFF)) {
                gameSettings.put(setting, false);
            }
        }
    }

    public MapStructure getMapStructure() {
        return mapStructure;
    }

    public MatchField getMatchField() {
        return matchField;
    }

    public void stop(boolean isRegularStop) {
        musicPLayer.stop();
        if(waveTimer!=null){
            waveTimer.cancel();
        }
        if(gameTimer!=null){
            gameTimer.cancel();
        }
        countDownTimer.stopTimer();
        if (!isRegularStop) {
            matchField.stopTimer();
        }
    }

    public void openSettings(){
        if(!isMenuOpen){
            isMenuOpen = true;
            Intent intent = new Intent(gameActivity, MenuSettings.class);
            MenuSettings.gameActivity = gameActivity;
            matchField.pauseTimers();
            pauseTimers();
            gameActivity.startActivity(intent);
        }
    }

    public void closeMenu(){
        this.isMenuOpen=false;
    }

    public void pauseTimers(){
        final long time = System.currentTimeMillis();
        match.calculateDelay(time);
        countDownTimer.stopTimer();
        waveTimer.cancel();
        waveTimer = null;
        gameTimer.cancel();
        gameTimer = null;
    }

    public void continueTimers(){
        startWave(match.getCurrent(), Math.round(match.getDelay()/1000));
    }

    public void init(Difficulty difficulty) {
        switch (difficulty) {
            case EASY:
                this.match = new EasyMatch();
                break;
            case MEDIUM:
                this.match = new MediumMatch();
                break;
            case HARD:
                this.match = new HardMatch();
                break;
        }
        match.create(gameActivity);
    }

    public void start() {
        this.money = match.getStartMoney();
        updateStatusBar();
        startNextWave(0);
    }

    public void startNextWave(long delay){
        if (match.hasNext()) {
            if (gameTimer != null) {
                gameTimer.cancel();
            }
            gameTimer = new Timer();
            gameTimer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Optional<AWave> wave = match.next();
                    if (wave.isPresent()) {
                        if(match.getCurrentWaveNumber()==1){
                            startWave(wave.get(), startGameDelay);
                        }else{
                            startWave(wave.get(), match.getWaveTime());
                        }
                    }
                }
            }, delay);
        }else{
            lastWaveOut = true;
        }
    }

    private void startWave(AWave wave, int seconds) {
        //timer
        if (waveTimer != null) {
            waveTimer.cancel();
        }
        waveTimer = new Timer();
        countDownTimer.timer(seconds);

        //status
        currentWaveNumber = match.getCurrentWaveNumber()-1;
        updateStatusBar();

        //pause actions
        match.setLastTimeActionMillis(System.currentTimeMillis());
        match.setDelay(seconds*1000);

        //next wave
        lastEnemyOfWaveSpawned = false;
        triggerSpawnButtonImageChange();
        startNextWave(seconds*1000);

        waveTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (wave.hasNext()) {
                    matchField.addEnemy(wave.next());
                } else {
                    lastEnemyOfWaveSpawned = true;
                    triggerSpawnButtonImageChange();
                    cancel();
                }
                wave.setLastTimeActionMillis(System.currentTimeMillis());
            }
        }, wave.getDelay(), wave.getWaveSpeed());
    }

    public void createNewTowerOnField(Position pos) {
        Intent intent = new Intent(gameActivity, MenuTowerSelection.class);
        intent.putExtra(gameActivity.getString(R.string.position), pos);
        MenuTowerSelection.game = this;
        gameActivity.startActivity(intent);
    }

    public void buildTower(TowerType type, int level, Position pos) {
        if (subMoney(type.getPrice()) && getMapStructure().getField(pos).getFieldDescription() == FieldDescription.FREE) {
            switch(type) {
                case ARTILLERY:
                    TowerArtillery newArtillery = new TowerArtillery(getMapStructure().getField(pos), level, gameActivity);
                    getMatchField().addTower(newArtillery);
                    getMapStructure().getField(pos).setFieldDescription(FieldDescription.TOWER);
                    break;
                case FREEZER:
                    TowerFreezer newFreezer = new TowerFreezer(getMapStructure().getField(pos), level, gameActivity);
                    getMatchField().addTower(newFreezer);
                    getMapStructure().getField(pos).setFieldDescription(FieldDescription.TOWER);
                    break;
                case BOOMBASTIC:
                    TowerBoombastic newBoombastic = new TowerBoombastic(getMapStructure().getField(pos), level, gameActivity);
                    getMatchField().addTower(newBoombastic);
                    getMapStructure().getField(pos).setFieldDescription(FieldDescription.TOWER);
                    break;
                case PLASMARIZER:
                    TowerPlasmarizer newPlasmarizer = new TowerPlasmarizer(getMapStructure().getField(pos), level, gameActivity);
                    getMatchField().addTower(newPlasmarizer);
                    getMapStructure().getField(pos).setFieldDescription(FieldDescription.TOWER);
                    break;
                case ASSAULTLASER:
                    TowerLaser newAssaultLaser = new TowerLaser(getMapStructure().getField(pos), level, gameActivity);
                    getMatchField().addTower(newAssaultLaser);
                    getMapStructure().getField(pos).setFieldDescription(FieldDescription.TOWER);
                    break;
            }
        }
    }

    public void sellTower(Position pos) {
        Field field = mapStructure.getField(pos);
        Optional<ATower> tower = matchField.getTower(field);
        if (tower.isPresent()) {
            ATower currrentTower = tower.get();
            if(currrentTower instanceof TowerLaser){
                ((TowerLaser) currrentTower).removeBullet();
            }
            addMoney((int) Math.round(currrentTower.getCosts() * 0.5));
            matchField.removeTower(currrentTower);
            mapStructure.getField(field.getFieldPosition()).setFieldDescription(FieldDescription.FREE);
        }
    }

    private void updateStatusBar() {
        gameActivity.setLifePoints(String.valueOf(lifePoints));
        gameActivity.setMoney(String.valueOf(money));
        gameActivity.setCurrentWaveNumber(String.valueOf(currentWaveNumber));
    }

    public int getLifePoints() {
        return lifePoints;
    }

    public void setLifePoints(int lifePoints) {
        this.lifePoints = lifePoints;
    }

    public boolean decreaseLifePoints(int lifePoints) {
        if (this.lifePoints > lifePoints) {
            this.lifePoints -= lifePoints;
            updateStatusBar();
            return true;
        }
        this.lifePoints = 0;
        updateStatusBar();
        return false;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void addMoney(int money) {
        this.money += money;
        updateStatusBar();
        if (moneyListener != null) {
            moneyListener.performMoneyUpdate(this.money);
        }
    }

    public boolean subMoney(int money) {
        if (this.money >= money) {
            this.money -= money;
            updateStatusBar();
            increaseMoneySpent(money);
            return true;
        }
        return false;
    }

    public boolean allEnemiesSpawned() {
        return lastWaveOut && lastEnemyOfWaveSpawned;
    }

    public void loseActions() {
        this.stop(true);
        updateStatisticsIfHighScore();
        showGameFinishedDialog(gameActivity, "You have lost the game!");
    }

    public void winActions() {
        this.stop(true);
        updateStatisticsIfHighScore();
        showGameFinishedDialog(gameActivity, "You have won the game!");
    }

    private void showGameFinishedDialog(GameActivity gameActivity, String message) {
        AlertDialog.Builder builder = new AlertDialog.Builder(gameActivity);
        builder.setTitle("Game finished!");
        builder.setMessage(message);
        builder.setPositiveButton("OK", (dialogInterface, i) -> gameActivity.returnToMainMenu());
        builder.setCancelable(false);
        gameActivity.showBuilder(builder);
    }

    public void setMenu(IMoneyListener listener) {
        moneyListener = listener;
    }

    public void increaseNumberOfEnemiesKilled() {
        numberOfEnemiesKilled++;
    }

    public void increaseNumberOfBuiltTowers() {
        numberOfBuiltTowers++;
    }

    public void increaseMoneySpent(int increase) {
        moneySpent += increase;
    }

    public void toggleMusic(boolean on){
        gameSettings.remove(Settings.MUSIC);
        gameSettings.put(Settings.MUSIC, on);
        musicPLayer.toggle(on, gameActivity);
    }

    public void setIngameSound(boolean on){
        gameSettings.remove(Settings.INGAME_SOUND);
        gameSettings.put(Settings.INGAME_SOUND, on);
    }

    public boolean isSoundOn(){
        return gameSettings.get(Settings.INGAME_SOUND);
    }

    public void setAnimationOn(boolean on){
        gameSettings.remove(Settings.ANIMATIONS);
        gameSettings.put(Settings.ANIMATIONS, on);
    }

    public boolean isAnimationOn(){
        return gameSettings.get(Settings.ANIMATIONS);
    }


    public void updateStatisticsIfHighScore() {
        Difficulty matchDifficulty = match.getDifficulty();
        if (currentWaveNumber >= Integer.parseInt(PreferenceManager.getStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.MAX_WAVE)))) {
            if (numberOfEnemiesKilled > Integer.parseInt(PreferenceManager.getStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.ENEMIES_KILLED)))) {
                PreferenceManager.setStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.MAX_WAVE), String.valueOf(currentWaveNumber));
                PreferenceManager.setStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.ENEMIES_KILLED), String.valueOf(numberOfEnemiesKilled));
                PreferenceManager.setStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.BUILT_TOWERS), String.valueOf(numberOfBuiltTowers));
                PreferenceManager.setStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.UPGRADES), String.valueOf(numberOfUpgrades));
                PreferenceManager.setStatisticsValue(PreferenceManager.Statistics.getStatisticsStringByDifficulty(matchDifficulty, PreferenceManager.Statistics.MONEY_SPENT), String.valueOf(moneySpent));
            }
        }
    }

    public Position getPositionFromButtonId(String id) {
        String[] result = id.split("01230");
        return new Position(Integer.parseInt(result[0]), Integer.parseInt(result[1]));
    }

    private void generateButtonsOnMap() {

        List<Button> mapButtons = new ArrayList<>();
        View.OnClickListener listener = view -> {
            Optional<Button> button = mapButtons.stream().filter(e -> view.getTransitionName().equals(e.getTransitionName())).findFirst();
            if (button.isPresent()) {
                Position pos = getPositionFromButtonId(button.get().getTransitionName());
                Field field = mapStructure.getField(pos);
                if (field.getFieldDescription() == FieldDescription.FREE) {
                    if (showCircle) {
                        ViewGroup vg = (ViewGroup)(gameActivity.findViewById(R.id.circle).getParent());
                        vg.removeView(gameActivity.findViewById(R.id.circle));
                        circleField = new Position(-1, -1);
                        showCircle = false;
                    } else {
                        if (clickedButton.isPresent()) {
                            if (clickedButton.get().getTransitionName().equals(button.get().getTransitionName())) {
                                clickedButton = Optional.empty();
                                button.get().setBackground(gameActivity.getResources().getDrawable(Constants.DRAWABLE_FIELD_TRANSPARENT, null));
                                createNewTowerOnField(getPositionFromButtonId(button.get().getTransitionName()));
                            } else {
                                clickedButton.get().setBackground(gameActivity.getResources().getDrawable(Constants.DRAWABLE_FIELD_TRANSPARENT, null));
                                button.get().setBackground(gameActivity.getResources().getDrawable(Constants.DRAWABLE_FIELD_ON_CLICK_PLUS, null));
                                clickedButton = button;
                            }
                        } else {
                            clickedButton = button;
                            button.get().setBackground(gameActivity.getResources().getDrawable(Constants.DRAWABLE_FIELD_ON_CLICK_PLUS, null));
                        }
                    }
                } else if (field.getFieldDescription() == FieldDescription.TOWER) {
                    Optional<ATower> tower = matchField.getTower(field);
                    if (tower.isPresent()) {
                        if (field.getFieldPositionX() != circleField.getX() || field.getFieldPositionY() != circleField.getY()) {
                            try {
                                if(clickedButton.isPresent()){
                                    clickedButton.get().setBackground(gameActivity.getResources().getDrawable(Constants.DRAWABLE_FIELD_TRANSPARENT, null));
                                    clickedButton = Optional.empty();
                                }
                            } catch (NoSuchElementException e) {
                                e.printStackTrace();
                            }

                            if (showCircle) {
                                ViewGroup vg = (ViewGroup)(gameActivity.findViewById(R.id.circle).getParent());
                                vg.removeView(gameActivity.findViewById(R.id.circle));
                            }
                            circleField = field.getFieldPosition();
                            setCircle(tower.get(), field);
                            showCircle = true;
                        } else {
                            try {
                                ViewGroup vg = (ViewGroup)(gameActivity.findViewById(R.id.circle).getParent());
                                vg.removeView(gameActivity.findViewById(R.id.circle));
                                circleField = new Position(-1, -1);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            openTowerPopup(tower.get(), field);
                            showCircle = false;
                        }
                    }
                }
            }
        };

        View.OnClickListener spawnFieldListener = view -> {
            if(lastEnemyOfWaveSpawned && !lastWaveOut){
                startNextWave(0);
            }
        };

        LinearLayout.LayoutParams buttonSizeParams = new LinearLayout.LayoutParams(FIELD_SIZE, FIELD_SIZE);
        mapStructure.getFields().stream().forEach(field -> {
            Button fieldButton = new Button(gameActivity);
            fieldButton.setX(field.getSpawnPoint().getX());
            fieldButton.setY(field.getSpawnPoint().getY());
            fieldButton.setLayoutParams(buttonSizeParams);
            fieldButton.setTransitionName(field.getId());
            if (field.getFieldDescription().equals(FieldDescription.PATH)) {
                fieldButton.setEnabled(false);
                fieldButton.setBackground(gameActivity.getResources().getDrawable(MapStructure.calculatePath(fieldButton.getX(), fieldButton.getY()), null));
                gameActivity.getMapFrameLayout().addView(fieldButton);
                mapButtons.add(fieldButton);
            } else if(field.getFieldDescription().equals(FieldDescription.SPAWN)) {
                fieldButton.setOnClickListener(spawnFieldListener);
                fieldButton.setBackground(gameActivity.getResources().getDrawable(MapStructure.calculatePath(fieldButton.getX(), fieldButton.getY()), null));
                fieldButton.setForeground(gameActivity.getResources().getDrawable(R.drawable.arrow_spawn_button, null));
                spawnButton = fieldButton;
                gameActivity.getMapFrameLayout().addView(fieldButton);
                mapButtons.add(fieldButton);
            } else if (field.getFieldDescription().equals(FieldDescription.FREE)) {
                fieldButton.setOnClickListener(listener);
                fieldButton.setBackground(gameActivity.getResources().getDrawable(Constants.DRAWABLE_FIELD_TRANSPARENT, null));
                gameActivity.getMapFrameLayout().addView(fieldButton);
                mapButtons.add(fieldButton);
            }
        });
    }

    private void triggerSpawnButtonImageChange(){
        if(lastEnemyOfWaveSpawned && !lastWaveOut){
            gameActivity.setForeGround(spawnButton, R.drawable.arrow_spawn_button);
        }else{
            gameActivity.setForeGround(spawnButton, R.drawable.transparent_background);
        }
    }

   public void setCircle(ATower tower, Field field) {
        ImageView image = new ImageView(gameActivity);
        image.setId(R.id.circle);
        image.setImageResource(R.drawable.circle);
        image.setX(field.getSpawnPoint().getX() + field.getSizeInPx()/2 - tower.getRange());
        image.setY(field.getSpawnPoint().getY() + field.getSizeInPx()/2 - tower.getRange());
        image.setLayoutParams(new LinearLayout.LayoutParams(tower.getRange()*2, tower.getRange()*2));
        image.setElevation(ImageElevation.RANGE_INDICATOR.elevation);
        gameActivity.getMapFrameLayout().addView(image);
    }

    public void openTowerPopup(ATower tower, Field field) {
        Intent intent = new Intent(gameActivity, MenuUpgradeAndSell.class);
        intent.putExtra(gameActivity.getString(R.string.position), field.getFieldPosition());
        intent.putExtra(gameActivity.getString(R.string.towerDamage), tower.getDamage());
        intent.putExtra(gameActivity.getString(R.string.towerRange), tower.getRange());
        intent.putExtra(gameActivity.getString(R.string.towerFireRate), tower.getFireRate());
        intent.putExtra(gameActivity.getString(R.string.towerCost), tower.getCosts());
        intent.putExtra(gameActivity.getString(R.string.towerLevel), tower.getLevel());
        intent.putExtra(gameActivity.getString(R.string.towerDrawable), tower.getTowerType().getDrawable());
        intent.putExtra(gameActivity.getString(R.string.towerType), tower.getTowerType().getType());
        intent.putExtra(gameActivity.getString(R.string.towerUpgrCost), tower.getCosts(tower.getLevel() + 1));
        intent.putExtra(gameActivity.getString(R.string.towerUpgrDamage), tower.getDamage(tower.getLevel() + 1));
        intent.putExtra(gameActivity.getString(R.string.towerUpgrRange), tower.getRange(tower.getLevel() + 1));
        intent.putExtra(gameActivity.getString(R.string.towerUpgrFireRate), tower.getFireRate(tower.getLevel() + 1));
        intent.putExtra(gameActivity.getString(R.string.towerUpgrCost), tower.getCosts(tower.getLevel() + 1));
        MenuUpgradeAndSell.game = this;
        gameActivity.startActivity(intent);
    }

    public int[] upgradeTower(Position pos, int level) {
        Field field = mapStructure.getField(pos);
        Optional<ATower> tower = matchField.getTower(field);
        if (tower.isPresent()) {
            TowerType type = tower.get().getTowerType();
            matchField.removeTower(tower.get());
            mapStructure.getField(field.getFieldPosition()).setFieldDescription(FieldDescription.FREE);
            setMoney(getMoney()+tower.get().getCosts(1));
            buildTower(type, level, pos);
            numberOfUpgrades++;

            return new int[]{tower.get().getDamage(level+1), tower.get().getRange(level+1), tower.get().getFireRate(level+1), tower.get().getCosts(level+1)};
        }
        return null;
    }
}