Android Open Source - feup-lpoo-android-tower-defense Enemy






From Project

Back to project page feup-lpoo-android-tower-defense.

License

The source code is released under:

MIT License

If you think the Android project feup-lpoo-android-tower-defense listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package pt.up.fe.lpoo.towerdefense;
//from   w w  w  .  jav  a 2s  . com
import java.util.ArrayList;
import java.util.Collections;
import java.lang.Math;

/**
 * Abstract class to define Enemy objects
 * @author Joao Marinheiro
 * @author Luis Cleto
 * @see GameObj
 */
public abstract class Enemy extends GameObj{

  private static final double MOVE_REACTION_TIME = 0.02;
  protected int centerX, centerY, speedX, speedY;
  protected int movementSpeed, health, damage;
  protected ArrayList<Tile> path;
  protected ArrayList<StatusEffect> statusEffects;
  protected int curTileIndex;
  /** indicates whether the enemy is to be removed from the enemy list or not */
  public boolean toRemove = false;
  protected boolean dead = false;
  protected boolean isAttacking = false;
  protected int scoreWorth, moneyWorth;
  private float elapsedSeconds;
  private float attackCooldown;
  protected double reactionTimeModifier = 1.0;
  private boolean resettingToTilePosition =false;
  protected int piercingArmor, crushingArmor, magicArmor;
  private boolean usingUniquePath = false;
  private boolean firstUpdate = true;

  /**
   * Damage types allowed by the game
   */
  public enum DamageType{
    Piercing, Crushing, Magic
  }

  private static final int TOWER_TERRAIN = 0;
  private static final int PATH = 1;
  private static final int MONSTER_ENTRANCE = 2;
  private static final int MONSTER_GOAL = 3;

  /**
   * Constructor for the Enemy objects. Sets the position of the enemy at the beggining of the path
   * @param health hp of the enemy
   * @param moveSpeed number of pixels the enemy can move 50 times per second
   * @param score score the user will gain if he defeats the enemy
   * @param money money the user will gain if he defeats the enemy
   * @param path path the enemy will follow
   * @param damage base damage the enemy will deal to defense objects
   * @param attCooldown cooldown between enemy attacks on defense objects
   */
  public Enemy(int health, int moveSpeed, int score, int money, ArrayList<Tile> path, int damage, float attCooldown){
    this.statusEffects = new ArrayList<StatusEffect>();
    this.elapsedSeconds = 0;
    this.path = path;
    this.curTileIndex = 0;
    this.attackCooldown = attCooldown;
    this.centerX = path.get(0).getCenterX();
    this.centerY = path.get(0).getCenterY();
    this.health = health;
    this.movementSpeed = moveSpeed;
    this.scoreWorth = score;
    this.moneyWorth = money;
    this.speedX = 0;
    this.speedY = 0;
    this.damage = damage;
  }

  public boolean isDead(){
    return dead;
  }
  public int getHealth(){
    return health;
  }
  /**
   * Updates the status effects first.
   * 
   * If the enemy is not frozen, increments the elapsed time counter and if its greater than the enemy's reaction
   *  time moves the enemy if it can move, attacks the defense object blocking the path otherwise.
   * @param deltaTime elapsed time since the last update
   */
  public void update(float deltaTime) {
    if(!toRemove){
      if(firstUpdate){
        firstUpdate = false;
        determineDirection();
      }
      updateStatusEffects(deltaTime);
      if(hasStatusEffect(StatusEffect.EffectType.Frozen))
        return;
      elapsedSeconds = elapsedSeconds + (deltaTime/100.0f);


      if(curTileIndex < path.size()-1){
        if(path.get(curTileIndex+1).hasDefenseObject()){
          if(!path.get(curTileIndex+1).getDefenseObject().isCrossable()){
            isAttacking = true;
            while(elapsedSeconds >= attackCooldown*reactionTimeModifier 
                && !path.get(curTileIndex+1).getDefenseObject().toRemove){

              elapsedSeconds -= attackCooldown*reactionTimeModifier;
              path.get(curTileIndex+1).getDefenseObject().receiveDamage(damage);
            }
            return;
          }
        }
      }
      if(isAttacking)
        elapsedSeconds = (deltaTime/100.0f);

      isAttacking = false;
      int lastPosX = centerX;
      int lastPosY = centerY;

      while(elapsedSeconds >= MOVE_REACTION_TIME*reactionTimeModifier){
        elapsedSeconds -= MOVE_REACTION_TIME*reactionTimeModifier;
        if(resettingToTilePosition){
          resetToPositionMove();
          break;
        }
        centerX += speedX;
        centerY += speedY;


        if(hasArrivedAtNextTile(lastPosX, lastPosY, centerX, centerY)){
          centerX = path.get(curTileIndex+1).getCenterX();
          centerY = path.get(curTileIndex+1).getCenterY();
          advanceInPath();
          if(toRemove)
            return;
        }
      }
    }
  }

  private void resetToPositionMove() {
    if(centerX < path.get(curTileIndex).getCenterX()){
      centerX += movementSpeed;
      if(!(centerX < path.get(curTileIndex).getCenterX()))
        resettingToTilePosition = false;
    }
    else if(centerX > path.get(curTileIndex).getCenterX()){
      centerX -= movementSpeed;
      if(!(centerX > path.get(curTileIndex).getCenterX()))
        resettingToTilePosition = false;
    }
    else if(centerY < path.get(curTileIndex).getCenterY()){
      centerY += movementSpeed;
      if(!(centerY < path.get(curTileIndex).getCenterY()))
        resettingToTilePosition = false;
    }
    else if(centerY > path.get(curTileIndex).getCenterY()){
      centerY -= movementSpeed;
      if(!(centerY > path.get(curTileIndex).getCenterY()))
        resettingToTilePosition = false;
    }
    else
      resettingToTilePosition = false;
    if(!resettingToTilePosition){
      this.centerX = path.get(curTileIndex).getCenterX();
      this.centerY = path.get(curTileIndex).getCenterY();
    }
  }

  private void resetReactionTime(){
    this.reactionTimeModifier = 1.0;
  }
  
  /**
   * Checks if the enemy is currently affected by a certain type of status effect
   * @param type type of status effect to check for
   * @return returns true if the enemy has the status effect, false otherwise
   */
  public boolean hasStatusEffect(StatusEffect.EffectType type) {
    for(int i=0; i < statusEffects.size(); i++){
      if(statusEffects.get(i).type == type && !statusEffects.get(i).toRemove)
        return true;
    }
    return false;
  }
  /**
   * Returns an array with all of the status effect on the enemy object
   */
  @Override
  public ArrayList<StatusEffect> getStatusEffects(){
    return statusEffects;
  }
  /**
   * Adds a new status effect to the enemy
   * @param newEffect status effect to be added
   */
  public void addStatusEffect(StatusEffect newEffect){
    for(int i=0; i < statusEffects.size(); i++){
      if(newEffect.type == statusEffects.get(i).type){
        if(newEffect.getRemainingTime() > statusEffects.get(i).getRemainingTime())
          statusEffects.set(i, newEffect);
        return;
      }
    }
    statusEffects.add(newEffect);
  }
  
  /**
   * Updates the status effects of the enemy, dealing damage when necessary and updating the reaction time
   *  modifier
   * @param deltaTime elapsed time since the last update
   */
  public void updateStatusEffects(float deltaTime) {
    for(int i=0; i < statusEffects.size(); i++){
      statusEffects.get(i).update(deltaTime);
      if(reactionTimeModifier < statusEffects.get(i).getReactionTimeModifier())
        reactionTimeModifier = statusEffects.get(i).getReactionTimeModifier();
      damage(statusEffects.get(i).getDamage(), statusEffects.get(i).getDamageType());
      if(statusEffects.get(i).toRemove){
        if(statusEffects.get(i).type == StatusEffect.EffectType.Slowed)
          resetReactionTime();
        statusEffects.remove(i);
        i--;
      }
    }
  }
  public double getReactionTimeModifier(){
    return reactionTimeModifier;
  }

  private boolean hasArrivedAtNextTile(int lastPosX, int lastPosY, int newPosX, int newPosY){
    if(curTileIndex >= path.size()-1){
      LevelScreen.gameElems.livesLeft--;
      toRemove = true;
      return false;
    }
    int tileCenterX = path.get(curTileIndex+1).getCenterX();
    int tileCenterY = path.get(curTileIndex+1).getCenterY();

    if(lastPosX == newPosX && 
        ((lastPosY <= tileCenterY && newPosY > tileCenterY) || (lastPosY >= tileCenterY && newPosY < tileCenterY)))
      return true;
    else if(lastPosY == newPosY && 
        ((lastPosX <= tileCenterX && newPosX > tileCenterX) || (lastPosX >= tileCenterX && newPosX < tileCenterX)))
      return true;
    else
      return false;
  }

  /**
   * Moves to the next tile of the path
   */
  public void advanceInPath(){
    curTileIndex++;
    if(curTileIndex >= path.size()-1){
      LevelScreen.gameElems.livesLeft--;
      toRemove = true;
      return;
    }
    determineDirection();
  }

  private void determineDirection() {
    int tileCenterX,tileCenterY, nextTileCenterX, nextTileCenterY;
    if(curTileIndex < path.size()-1){
      tileCenterX = path.get(curTileIndex).getCenterX();
      tileCenterY = path.get(curTileIndex).getCenterY();
      nextTileCenterX = path.get(curTileIndex+1).getCenterX();
      nextTileCenterY = path.get(curTileIndex+1).getCenterY();
    }
    else if(curTileIndex < path.size()){
      tileCenterX = centerX;
      tileCenterY = centerY;
      nextTileCenterX = path.get(curTileIndex).getCenterX();
      nextTileCenterY = path.get(curTileIndex).getCenterY();
    }
    else
      return;

    if(tileCenterX == nextTileCenterX){
      speedX = 0;
      if(tileCenterY > nextTileCenterY)
        speedY = -movementSpeed;
      else
        speedY = movementSpeed;
    }
    else{
      speedY = 0;
      if(tileCenterX > nextTileCenterX)
        speedX = -movementSpeed;
      else
        speedX = movementSpeed;
    }
  }

  private void die(){
    dead = true;
    centerX = -100;
    centerY = -100;
    movementSpeed = 0;
    LevelScreen.gameElems.curScore += scoreWorth;
    LevelScreen.gameElems.currentBalance += moneyWorth;
    toRemove = true;
  }
  
  /**
   * @return returns the distance left for the enemy to reach its goal (in pixels)
   */
  public int getDistToGoal(){
    return Math.abs(path.get(curTileIndex).getCenterX()-centerX)+Math.abs(path.get(curTileIndex).getCenterY()-centerY)
        +(path.size()-1-curTileIndex)*Tile.TILE_SIDE;
  }

  /**
   * Indicates whether the object is part of the ground terrain. Default for an enemy is false.
   * 
   * @return returns true if the object is part of the ground terrain, false otherwise.
   */
  @Override
  public boolean isGroundObject(){
    return false;
  }

  /**
   * Deals damage of a given type to the enemy. Sets the enemy dead if health goes below 0.
   * @param hitDamage base value of the damage
   * @param type type of the damage
   */
  public void damage(int hitDamage, DamageType type){

    if(type == DamageType.Piercing)
      health -= hitDamage/piercingArmor;
    else if (type == DamageType.Crushing)
      health -= hitDamage/crushingArmor;
    else
      health -= hitDamage/magicArmor;

    if(health <= 0)
      die();
  }

  public void setPath(ArrayList<Tile> newPath){
    this.path = newPath;
  }

  public int getSpeedX() {
    return speedX;
  }
  public int getSpeedY() {
    return speedY;
  }

  @Override
  public int getCenterX() {
    return centerX;
  }

  @Override
  public int getCenterY() {
    return centerY;
  }
  /**
   * @return returns the x position of the upper left corner of the enemy
   */
  @Override
  public int getX() {
    return centerX-Tile.TILE_SIDE/2;
  }
  /**
   * @return returns the y position of the upper left corner of the enemy
   */
  @Override
  public int getY() {
    return centerY-Tile.TILE_SIDE/2 - (int)((getTileHeight()-1)*Tile.TILE_SIDE);
  }
  
  protected float getTileHeight(){
    return ((float)getCurrentImage().getHeight() / (float)Tile.TILE_SIDE);
  }

  /**
   * Verifies if the enemy's path is still valid (enemies may be left outside the path after a PathCluster update).
   *  If it isn't valid, calculates a new path
   * @param tileGrid grid with all the map's tiles
   * @param affectedTile the tile that was altered, causing an update
   * @see PathCluster
   */
  public void verifyValidPath(ArrayList<ArrayList<Tile>> tileGrid, Tile affectedTile){
    if(toRemove)
      return;
    boolean isOnPath = false;
    int gridX = centerX/Tile.TILE_SIDE;
    int gridY = (centerY-LevelScreen.INFO_BAR_HEIGHT)/Tile.TILE_SIDE;
    Tile curTile = tileGrid.get(gridY).get(gridX);

    int prevYSpeed = speedY; int prevXSpeed = speedX;

    if(usingUniquePath && (pathContainsTile(affectedTile) || !affectedTile.hasDefenseObject())){
      path = Enemy.getPathToPoint(gridX, gridY, path.get(path.size()-1).getGridX(),
          path.get(path.size()-1).getGridY(), tileGrid, getTileWeights());
      curTileIndex = 0;
      isOnPath = true;
    }
    else{
      for(int i=0; i < path.size(); i++){
        if(path.get(i).equals(curTile)){
          curTileIndex = i;
          isOnPath = true;
          break;
        }
      }
    }

    if(!isOnPath){
      path = Enemy.getPathToPoint(gridX, gridY, path.get(path.size()-1).getGridX(),
          path.get(path.size()-1).getGridY(), tileGrid, getTileWeights());
      curTileIndex = 0;
      usingUniquePath = true;
    }

    determineDirection();
    if(speedY*prevYSpeed == 0 && prevXSpeed*speedX == 0 && (centerX != path.get(curTileIndex).getCenterX() || 
        centerY != path.get(curTileIndex).getCenterY())){ //changed movement direction and is not centered
      resettingToTilePosition = true;
    }
  }
  
  private boolean pathContainsTile(Tile tile) {
    for(int i=0; i < path.size(); i++){
      if(path.get(i).equals(tile))
        return true;
    }
    return false;
  }

  /**
   * @return returns an array with the weights for each type of tile for the enemy (used in the pathfinding)
   */
  public abstract Float[] getTileWeights();


  //using Manhattan heuristics
  private static float heuristicFunction(int origX, int origY, int destX, int destY)
  {
    float difX = Math.abs(origX-destX);
    float difY = Math.abs(origY-destY);

    return difX+difY;
  }

  private static int isOnList(ArrayList<Tile> list, Tile tile)
  {
    for(int i = 0; i < list.size(); i++)
    {
      if(list.get(i) == tile)
        return i;
    }

    return -1;
  }

  /**
   * Calculates the shortest path between two tiles, using weights for each type of tile. Uses the A* algorithm.
   * @param origX x position of the starting tile
   * @param origY y position of the starting tile
   * @param destX x position of the destination tile
   * @param destY y position of the destination tile
   * @param tileMap grid with all the map's tiles
   * @param tileWeights array with the tileweights to be used for determining the path 
   * @return returns an array with the path between the two points
   */
  public static ArrayList<Tile> getPathToPoint(int origX, int origY, int destX, int destY, ArrayList<ArrayList<Tile> > tileMap, Float[] tileWeights)
  {
    ArrayList<Tile> openList = new ArrayList<Tile>();
    ArrayList<Tile> closedList = new ArrayList<Tile>();

    //resets the necessary values of the tiles
    for(int j = 0; j < tileMap.size(); j++)
    {
      ArrayList<Tile> row = tileMap.get(j);

      for(int i = 0; i < row.size(); i++)
      {
        Tile column = row.get(i);

        column.setDistanceToStart(Float.MAX_VALUE);
        column.setDistanceToEnd(heuristicFunction(i, j, destX, destY));
        column.setPath(null);
      }
    }

    //sets the starting tile's distance to 0 and adds it to the open list
    Tile startingTile = tileMap.get(origY).get(origX);
    startingTile.setDistanceToStart(0);

    openList.add(startingTile);
    Collections.sort(openList);

    while(!openList.isEmpty())
    {
      Tile activeTile = openList.get(0);


      //if we're at the destination tile
      if(activeTile.getGridX() == destX && activeTile.getGridY() == destY)
      {
        ArrayList<Tile> shortestPath = getPath(tileMap, destX, destY);
        return shortestPath;
      }

      Tile neighbor;
      int neighborX, neighborY;
      float weight = Float.MAX_VALUE;

      //checks node on top
      neighborX = activeTile.getGridX();
      neighborY = activeTile.getGridY()-1;

      if(neighborY >= 0 && neighborY < tileMap.size())
      {  
        neighbor =  tileMap.get(neighborY).get(neighborX);

        switch(neighbor.getType())
        {
        case 'X':
          weight = tileWeights[Enemy.TOWER_TERRAIN];
          break;
        case '_':
          weight = tileWeights[Enemy.PATH];
          break;
        case 'O':
          weight = tileWeights[Enemy.MONSTER_GOAL];
          break;
        case 'E':
          weight = tileWeights[Enemy.MONSTER_ENTRANCE];
          break;
        }
        if(neighbor.hasDefenseObject()){
          if(!neighbor.getDefenseObject().isCrossable())
            weight += 50;
          else
            weight += 10;  
        }

        if(isOnList(openList, neighbor) == -1 && isOnList(closedList, tileMap.get(neighborY).get(neighborX)) == -1)
          openList.add(neighbor);

        if(activeTile.getDistanceToStart() + weight < neighbor.getDistanceToStart())
        {
          neighbor.setPath(activeTile);
          neighbor.setDistanceToStart(activeTile.getDistanceToStart() + weight);
        }
      }

      //checks node on bottom
      neighborX = activeTile.getGridX();
      neighborY = activeTile.getGridY()+1;

      if(neighborY >= 0 && neighborY < tileMap.size())
      {  
        neighbor =  tileMap.get(neighborY).get(neighborX);

        switch(neighbor.getType())
        {
        case 'X':
          weight = tileWeights[Enemy.TOWER_TERRAIN];
          break;
        case '_':
          weight = tileWeights[Enemy.PATH];
          break;
        case 'O':
          weight = tileWeights[Enemy.MONSTER_GOAL];
          break;
        case 'E':
          weight = tileWeights[Enemy.MONSTER_ENTRANCE];
          break;
        }
        if(neighbor.hasDefenseObject()){
          if(!neighbor.getDefenseObject().isCrossable())
            weight += 50;
          else
            weight += 10;  
        }

        if(isOnList(openList, neighbor) == -1 && isOnList(closedList, tileMap.get(neighborY).get(neighborX)) == -1)
          openList.add(neighbor);

        if(activeTile.getDistanceToStart() + weight < neighbor.getDistanceToStart())
        {
          neighbor.setPath(activeTile);
          neighbor.setDistanceToStart(activeTile.getDistanceToStart() + weight);
        }
      }

      //checks node on left
      neighborX = activeTile.getGridX()-1;
      neighborY = activeTile.getGridY();

      if(neighborX >= 0 && neighborX < tileMap.get(neighborY).size())
      {  
        neighbor =  tileMap.get(neighborY).get(neighborX);

        switch(neighbor.getType())
        {
        case 'X':
          weight = tileWeights[Enemy.TOWER_TERRAIN];
          break;
        case '_':
          weight = tileWeights[Enemy.PATH];
          break;
        case 'O':
          weight = tileWeights[Enemy.MONSTER_GOAL];
          break;
        case 'E':
          weight = tileWeights[Enemy.MONSTER_ENTRANCE];
          break;
        }
        if(neighbor.hasDefenseObject()){
          if(!neighbor.getDefenseObject().isCrossable())
            weight += 50;
          else
            weight += 10;  
        }

        if(isOnList(openList, neighbor) == -1 && isOnList(closedList, tileMap.get(neighborY).get(neighborX)) == -1)
          openList.add(neighbor);

        if(activeTile.getDistanceToStart() + weight < neighbor.getDistanceToStart())
        {
          neighbor.setPath(activeTile);
          neighbor.setDistanceToStart(activeTile.getDistanceToStart() + weight);
        }
      }


      //checks node on right
      neighborX = activeTile.getGridX()+1;
      neighborY = activeTile.getGridY();

      if(neighborX >= 0 && neighborX < tileMap.get(neighborY).size())
      {  
        neighbor =  tileMap.get(neighborY).get(neighborX);

        switch(neighbor.getType())
        {
        case 'X':
          weight = tileWeights[Enemy.TOWER_TERRAIN];
          break;
        case '_':
          weight = tileWeights[Enemy.PATH];
          break;
        case 'O':
          weight = tileWeights[Enemy.MONSTER_GOAL];
          break;
        case 'E':
          weight = tileWeights[Enemy.MONSTER_ENTRANCE];
          break;
        }
        if(neighbor.hasDefenseObject()){
          if(!neighbor.getDefenseObject().isCrossable())
            weight += 50;
          else
            weight += 10;  
        }

        if(isOnList(openList, neighbor) == -1 && isOnList(closedList, tileMap.get(neighborY).get(neighborX)) == -1)
          openList.add(neighbor);

        if(activeTile.getDistanceToStart() + weight < neighbor.getDistanceToStart())
        {
          neighbor.setPath(activeTile);
          neighbor.setDistanceToStart(activeTile.getDistanceToStart() + weight);
        }
      }

      closedList.add(openList.remove(0));
      Collections.sort(openList);
    }

    return null;

  }

  private static ArrayList<Tile> getPath(ArrayList<ArrayList<Tile>> tileMap, int destX, int destY) 
  {
    if(destY < 0 || destY >= tileMap.size())
      return null;

    if(destX < 0 || destY >= tileMap.get(destY).size())
      return null;

    ArrayList<Tile> shortestPath = new ArrayList<Tile>();

    Tile tile = tileMap.get(destY).get(destX);
    shortestPath.add(tile);

    while(tile.getPath() != null)
    {
      tile = tile.getPath();
      shortestPath.add(tile);
    }

    Collections.reverse(shortestPath);

    return shortestPath;
  }

  /**
   * @return returns true if the enemy can fly, false otherwise
   */
  public abstract boolean canFly();

}




Java Source Code List

pt.up.fe.lpoo.framework.Audio.java
pt.up.fe.lpoo.framework.FileIO.java
pt.up.fe.lpoo.framework.Game.java
pt.up.fe.lpoo.framework.Graphics.java
pt.up.fe.lpoo.framework.Image.java
pt.up.fe.lpoo.framework.Input.java
pt.up.fe.lpoo.framework.Music.java
pt.up.fe.lpoo.framework.Pool.java
pt.up.fe.lpoo.framework.Screen.java
pt.up.fe.lpoo.framework.Sound.java
pt.up.fe.lpoo.framework.implementation.AndroidAudio.java
pt.up.fe.lpoo.framework.implementation.AndroidFastRenderView.java
pt.up.fe.lpoo.framework.implementation.AndroidFileIO.java
pt.up.fe.lpoo.framework.implementation.AndroidGame.java
pt.up.fe.lpoo.framework.implementation.AndroidGraphics.java
pt.up.fe.lpoo.framework.implementation.AndroidImage.java
pt.up.fe.lpoo.framework.implementation.AndroidInput.java
pt.up.fe.lpoo.framework.implementation.AndroidMusic.java
pt.up.fe.lpoo.framework.implementation.AndroidSound.java
pt.up.fe.lpoo.framework.implementation.MultiTouchHandler.java
pt.up.fe.lpoo.framework.implementation.SingleTouchHandler.java
pt.up.fe.lpoo.framework.implementation.TouchHandler.java
pt.up.fe.lpoo.towerdefense.Animation.java
pt.up.fe.lpoo.towerdefense.Assets.java
pt.up.fe.lpoo.towerdefense.BarricadeDefense.java
pt.up.fe.lpoo.towerdefense.BoulderProjectile.java
pt.up.fe.lpoo.towerdefense.BoulderTower.java
pt.up.fe.lpoo.towerdefense.BuildingOption.java
pt.up.fe.lpoo.towerdefense.DefenseObject.java
pt.up.fe.lpoo.towerdefense.DescriptionObject.java
pt.up.fe.lpoo.towerdefense.Enemy.java
pt.up.fe.lpoo.towerdefense.FreezeTower.java
pt.up.fe.lpoo.towerdefense.GameElements.java
pt.up.fe.lpoo.towerdefense.GameObj.java
pt.up.fe.lpoo.towerdefense.Golem.java
pt.up.fe.lpoo.towerdefense.InstructionsScreen.java
pt.up.fe.lpoo.towerdefense.LevelScreen.java
pt.up.fe.lpoo.towerdefense.LevelSelectionScreen.java
pt.up.fe.lpoo.towerdefense.LoadingScreen.java
pt.up.fe.lpoo.towerdefense.MainMenuScreen.java
pt.up.fe.lpoo.towerdefense.Messages.java
pt.up.fe.lpoo.towerdefense.MusicPlayer.java
pt.up.fe.lpoo.towerdefense.PathCluster.java
pt.up.fe.lpoo.towerdefense.PoisonTower.java
pt.up.fe.lpoo.towerdefense.Projectile.java
pt.up.fe.lpoo.towerdefense.Skull.java
pt.up.fe.lpoo.towerdefense.Slime.java
pt.up.fe.lpoo.towerdefense.SplashLoadingScreen.java
pt.up.fe.lpoo.towerdefense.StatusEffect.java
pt.up.fe.lpoo.towerdefense.TarPoolDefense.java
pt.up.fe.lpoo.towerdefense.TextParser.java
pt.up.fe.lpoo.towerdefense.Tile.java
pt.up.fe.lpoo.towerdefense.TowerDefenseGame.java
pt.up.fe.lpoo.towerdefense.TurretProjectile.java
pt.up.fe.lpoo.towerdefense.TurretTower.java
pt.up.fe.lpoo.towerdefense.Wave.java