niwa99/Tower-Defense

View on GitHub
app/src/main/java/de/dhbw/map/matchfield/MatchField.java

Summary

Maintainability
C
1 day
Test Coverage
package de.dhbw.map.matchfield;

import android.view.View;
import android.widget.ImageView;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.Optional;

import de.dhbw.ImageElevation;
import de.dhbw.R;
import de.dhbw.activities.GameActivity;
import de.dhbw.game.EnemyType;
import de.dhbw.map.objects.enemy.AEnemy;
import de.dhbw.map.objects.enemy.BossTank;
import de.dhbw.map.objects.enemy.Car;
import de.dhbw.map.objects.enemy.EnemyView;
import de.dhbw.map.objects.tower.ATower;
import de.dhbw.map.structure.Field;
import de.dhbw.util.AdvancedTimer;
import de.dhbw.util.Position;
import pl.droidsonroids.gif.GifImageView;

public class MatchField {

    private final GameActivity gameActivity;
    private List<AEnemy> enemies;
    private List<ATower> towers;

    //responsible for all enemy movements
    private AdvancedTimer matchFieldTimer = new AdvancedTimer();

    /**
     * Constructor
     * @param gameActivity
     */
    public MatchField(GameActivity gameActivity) {
        this.gameActivity = gameActivity;
        enemies = new ArrayList<>();
        towers = new ArrayList<>();
    }

    /**
     * Add a tower to the matchField.
     * @param tower
     */
    public void addTower(ATower tower) {
        ImageView baseImage = tower.getBaseImage();
        Optional<ImageView> headImage = tower.getHeadImage();

        gameActivity.addView(baseImage);
        if(headImage.isPresent()){
            gameActivity.addView(headImage.get());

        }
        if(tower.getLevel()>1) {
            gameActivity.addView(tower.getStarlvlTwo());
        }
        if(tower.getLevel()>2) {
            gameActivity.addView(tower.getStarlvlThree());
        }
        if(tower.getLevel()>3) {
            gameActivity.addView(tower.getStarlvlFour());
        }
        towers.add(tower);
        startTowerFire(tower);
        gameActivity.getGame().increaseNumberOfBuiltTowers();
    }

    /**
     * Add an enemy to the matchField.
     * @param enemy
     */
    public void addEnemy(AEnemy enemy) {
        EnemyView enemyView = enemy.getEnemyView();
        enemyView.setPosition(new Position(-500,-500));
        gameActivity.addView(enemyView.getLayout());
        enemies.add(enemy);
        startEnemyMovement(enemy);

        if(enemy instanceof BossTank){
            Car car = ((BossTank) enemy).getCar();
            EnemyView carView = car.getEnemyView();
            carView.setPosition(new Position(-500,-500));
            enemies.add(car);
            gameActivity.addView(carView.getLayout());
        }
    }

    /**
     * all enemies are moving in an own thread. The speed is defined by the time this task is repeated.
     * An enemy moves only one pixel each time. Speed is 1 second - (int) speed of the enemy
     */
    public void startEnemyMovement(AEnemy enemy) {
        if(matchFieldTimer.isCanceled()){
            return;
        }
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    if (enemy.isAlive() && !enemy.reachedTarget()) {
                        enemy.move(gameActivity.getGame().getMapStructure());
                    } else if (enemy.reachedTarget()) {
                        removeEnemiesInTarget(enemy);
                    } else if(!enemy.isAlive()){
                        removeDeadEnemy(enemy);
                    }

                }
            };
            enemy.setTimerTask(timerTask);
            matchFieldTimer.scheduleAtFixedRate(timerTask, 0, 1000 - enemy.getSpeed());
    }

    /**
     * Run a slow-TimerTask if the enemy has a slowness effect.
     * @param enemy
     */
    public void slowEnemy(AEnemy enemy){
        if(matchFieldTimer.isCanceled()){
            return;
        }
        enemy.getTimerTask().cancel();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                if (enemy.isAlive() && !enemy.reachedTarget()) {
                    enemy.move(gameActivity.getGame().getMapStructure());
                    if(enemy.isFullSpeed()){
                        cancel();
                        startEnemyMovement(enemy);
                    }else{
                        slowEnemy(enemy);
                    }
                } else if (enemy.reachedTarget()) {
                    removeEnemiesInTarget(enemy);
                } else if(!enemy.isAlive()){
                    removeDeadEnemy(enemy);
                }
            }
        };
        enemy.setTimerTask(timerTask);
        matchFieldTimer.schedule(timerTask, (1000 - enemy.getSpeed()) + enemy.getAndReduceSlowness());
    }

    /**
     * all towers are shooting in an own thread. The speed is defined by the time this task is repeated.
     * int fireRate defines how much seconds the tower sleeps between two shoots
     */
    public void startTowerFire(ATower tower) {
        if(matchFieldTimer.isCanceled()){
            return;
        }
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                if (enemies.size() > 0) {
                    tower.fire(new ArrayList<>(enemies));
                    tower.setLastTimeActionMillis(System.currentTimeMillis());
                }else{
                    checkForFinishedGame();
                }
            }
        };
        tower.setTask(timerTask);
        matchFieldTimer.scheduleAtFixedRate(timerTask, tower.getDelay(), tower.getFireRate() * 1000);
    }

    public void checkForFinishedGame(){
        if(gameActivity.getGame().allEnemiesSpawned()){
            stopTimer(true);
        }
    }


    /**
     * Pause the game timers.
     */
    public void pauseTimers(){
        final long time = System.currentTimeMillis();
        towers.stream().forEach(t -> t.calculateDelay(time));
        enemies.stream().forEach(e -> e.setPaused(true));
        stopTimer();
    }

    /**
     * Continue the game timers.
     */
    public void continueTimers(){
        matchFieldTimer = new AdvancedTimer();
        enemies.stream().forEach(e -> {
            if (e.getSlowness() == 0) {
                startEnemyMovement(e);
            } else {
                e.slowDown(e.getSlowness());
            }
            e.setPaused(false);
        });
        towers.stream().forEach(t -> startTowerFire(t));
    }

    /**
     * Stop the matchField timer and call win-/lose-actions.
     * @param isWinner
     */
    public void stopTimer(boolean isWinner) {
        stopTimer();
        if (isWinner) {
            gameActivity.getGame().winActions();
        } else {
            gameActivity.getGame().loseActions();
        }
    }

    /**
     * Stop the matchField timer (if game is stopped unregularly).
     */
    public void stopTimer() {
        if(!matchFieldTimer.isCanceled()) {
            matchFieldTimer.cancel();
        }
    }

    /**
     * Remove an enemy from the matchField and create explosion animation.
     * @param enemy
     */
    public void removeDeadEnemy(AEnemy enemy) {
        if (!enemy.isAlive()) {
            enemy.getTimerTask().cancel();
            removeImageViewOfEnemy(enemy);
            explode(enemy);
            spawnCarIfEnemyIsBossTank(enemy);
            gameActivity.getGame().addMoney(enemy.getValue());
            enemies.remove(enemy);
            gameActivity.getGame().increaseNumberOfEnemiesKilled();
            System.out.println(enemy.getLabel() + " is dead now");
        }
    }

    private void spawnCarIfEnemyIsBossTank(AEnemy enemy){
        if(enemy.getType()== EnemyType.BOSS_TANK){
            BossTank boss = (BossTank) enemy;
            Car car = boss.getCar();
            car.setHealthpoints(boss.getBossTankHealthpointsByLevel(boss.getLevel())/4);
            car.moveToPosition(enemy.getPosition());
            car.setProgress(enemy.getProgress()-2);
            startEnemyMovement(car);
        }
    }

    /**
     * Remove enemies which reached the target.
     * @param enemy
     */
    public void removeEnemiesInTarget(AEnemy enemy) {
        removeImageViewOfEnemy(enemy);
        enemy.getTimerTask().cancel();
        enemies.remove(enemy);
        if (!gameActivity.getGame().decreaseLifePoints(enemy.getLifePointsCosts())) {
            stopTimer(false);
        }
        System.out.println(enemy.getLabel() + " reached the target");
    }

    /**
     * Remove the imageView of an enemy.
     * @param enemy
     */
    private void removeImageViewOfEnemy(AEnemy enemy) {
        if (enemies.size() == 1 && gameActivity.getGame().allEnemiesSpawned()) {
            stopTimer(true);
        }
        gameActivity.removeView(enemy.getEnemyView().getLayout());
    }

    /**
     * Create an explosion animation for an enemy.
     * @param enemy
     */
    private void explode(AEnemy enemy) {
        if(gameActivity.getGame().isAnimationOn()) {
            GifImageView gif = new GifImageView(gameActivity);
            gif.setLayoutParams(gameActivity.getMapFrameLayout().getLayoutParams());
            gif.setX(enemy.getPositionX() - Math.round(gameActivity.getResources().getDisplayMetrics().widthPixels/2.2));
            gif.setY(enemy.getPositionY() - Math.round(gameActivity.getResources().getDisplayMetrics().heightPixels/2.2));
            gif.setScaleX(0.2f);
            gif.setScaleY(0.2f);
            gif.setImageResource(R.drawable.explosion_gif);
            gif.setElevation(ImageElevation.ANIMATION.elevation);
            gameActivity.addView(gif);
            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    gameActivity.removeView(gif);
                }
            }, 500);
        }
    }

    /**
     * Get a tower by uuid.
     * @param towerUUID
     * @return tower
     */
    private Optional<ATower> getTower(UUID towerUUID) {
        return towers.stream().filter(tower -> tower.getId() == towerUUID).findAny();
    }

    /**
     * Get a tower by field.
     * @param field
     * @return tower
     */
    public Optional<ATower> getTower(Field field) {
        return towers.stream().filter(t -> t.getField().equals(field)).findAny();
    }

    /**
     * Remove a tower by uuid.
     * @param towerUUID
     * @return true if tower got removed
     */
    private boolean removeTower(UUID towerUUID) {
        Optional<ATower> tower = getTower(towerUUID);
        if (tower.isPresent()) {
            towers.remove(tower.get());
            return true;
        }
        return false;
    }

    /**
     * Remove a tower by tower object
     * @param tower
     */
    public void removeTower(ATower tower) {
        tower.getTask().cancel();
        towers.remove(tower);

        ImageView baseImage = tower.getBaseImage();
        Optional<ImageView> headImage = tower.getHeadImage();
        gameActivity.removeView(baseImage);
        if(headImage.isPresent()){
            gameActivity.removeView(headImage.get());

        }
        if(tower.getLevel()>1) {
            gameActivity.removeView(tower.getStarlvlTwo());
        }
        if(tower.getLevel()>2) {
            gameActivity.removeView(tower.getStarlvlThree());
        }
    }

    /**
     * Get an enemy by uuid.
     * @param enemyUUID
     * @return enemy
     */
    private Optional<AEnemy> getEnemy(UUID enemyUUID) {
        return enemies.stream().filter(enemy -> enemy.getUuid() == enemyUUID).findAny();
    }

    /**
     * Get the uuids of all enemies as list.
     * @return uuids as list
     */
    private List<UUID> getEnemyUUIDs() {
        return enemies.stream().map(AEnemy::getUuid).collect(Collectors.toList());
    }

    /**
     * Remove an enemy by uuid.
     * @param enemyUUID
     * @return true if enemy got removed
     */
    private boolean removeEnemy(UUID enemyUUID) {
        Optional<AEnemy> enemy = getEnemy(enemyUUID);
        if (enemy.isPresent()) {
            enemies.remove(enemy.get());
            return true;
        }
        return false;
    }

    /**
     * Calculate the distance between two points (x1, y1) and (x2, y2)
     * @param firstX
     * @param firstY
     * @param secondX
     * @param secondY
     * @return the distance in px
     */
    public static int getDistance(int firstX, int firstY, int secondX, int secondY) {
        return (int) Math.round(Math.sqrt(Math.pow(Math.abs(firstX-secondX), 2) + Math.pow(Math.abs(firstY-secondY), 2)));
    }
}