Structures Polishing - UQdeco2800/2022-studio-1 GitHub Wiki

Summary

Following the completion of sprint 3, it was realised that much of the planned features for structures had yet to be implemented. As such it was a high priority for the team to complete the integration of the following features described below.

Fixes

Implementing buildings damaging enemies

As this is a critical part of the game (the game being a tower-defense game), this was judged as being a critical feature to implement

Implementing building rotation

By pressing r users can rotate buildings, allowing for greater control over building placement. This was an important feature to implement based off of user feedback from previous sprints, where user's who were familiar with other tower defence games were frustrated by lack of implementation of this feature.

The Turret

The turret has the ability to shoot at the closest target within its view distance. This is completed via a ShootMultipleTask component added to the AITaskComponent of the turret entity.

The ShootMultipleTask subscribes to the DayNightCycleService to only update targets at night, via the updateTargets method:

 private void updateTargets(DayNightCycleStatus partOfDay) {
                System.out.println("day night cycle change: " + partOfDay.name());
                if (partOfDay == DayNightCycleStatus.NIGHT) {

                        targets.clear();
                        Map<String, Entity> namedEntities = ServiceLocator.getEntityService().getAllNamedEntities();

                        for (Entry<String, Entity> entry : namedEntities.entrySet()) {
                                if (entry.getKey().contains("Enemy") && !entry.getValue().getName().contains("eelP")) {
                                        targets.add(entry.getValue());
                                        System.out.println("target added: " + entry.getValue().getName());
                                }
                        }

                        target = targets.size() > 0 ? targets.get(0) : null;
                }
        }

Then, every time the game is updated, the turret calculates the closest enemy in its list of targets, and uses the ProjectileFactory to create a projectile.

Integration of buildings with shop

This feature revolved around essentially making buildings purchasable from the shop, and ensuring that upon purchasing a building, the correct amount of currency was deducted from the player. This feature was judged as being an important feature to implement.

Integration of buildings with building UI

This feature involved ensuring that player has the ability to upgrade and sell buildings from the building UI popup. This feature was judged as being important to implement, so it is obvious to the player how they can upgrade / sell buildings.

The relevant code snippet for the upgrade button is shown below:

        upgradeButton.addListener(
            new ChangeListener() {
                @Override
                public void changed(ChangeEvent changeEvent, Actor actor) {
                    Entity player = ServiceLocator.getEntityService().getNamedEntity("player");
                    //Obtain reference to player, for some reason it was being accessed as 'entity'
                    int playerGold = player.getComponent(InventoryComponent.class).getGold();
                    if (playerGold > 2000) {
                        logger.info("Sufficient resources");

                        //Get building and convert it's position to gridPoint2

                        Vector2 worldPos = clickedStructure.getPosition();
                        int worldX = Math.round(worldPos.x);
                        int worldY = Math.round(worldPos.y);

                        GridPoint2 position = ServiceLocator.getEntityService().getNamedEntity("terrain")
                                .getComponent(TerrainComponent.class).worldToTilePosition(worldX, worldY);

                        position.y += 1;
                        StructureFactory.upgradeStructure(position, clickedStructure.getName());
                        logger.info("Upgrade Button clicked");

                        if (!clickedStructure.getName().contains("wall")) {
                            //Subtract currency from inventory if ! wall
                            player.getComponent(InventoryComponent.class).addGold(-1 * 2000);
                            PlayerStatsDisplay.updateItems();
                        }

                        BuildingUI.remove();

                    } else {
                        logger.info("Insufficient resource!");
                        Sound filesound = Gdx.audio.newSound(
                                Gdx.files.internal("sounds/purchase_fail.mp3"));
                        filesound.play();
                    }
                }});

The relevant code snippet for the sell button is shown below:

sellButton.addListener(
                new ChangeListener() {
                    @Override
                    public void changed(ChangeEvent changeEvent, Actor actor) {
                        logger.debug("Sell button clicked");

                        StructureFactory.handleBuildingDestruction(clickedStructure.getName());
                        PlayerStatsDisplay.updateItems();
                        // Remove building entity
                        BuildingUI.remove();
                    }
                });

Visualisation of Build mode

The completion of this feature would allow players to see where they can/cannot build. Completing this considered important as it was an important part of the user-experience which had not yet been implemented.

The relevant code snippet for visualising the build mode is shown below:

/**
   * Draw the coloured tiles around a structure to show the player where they can build a structure
   * @param centerCoord location of the structure in gridPoint2
   * @param entityType type of entity being checked
   */
  public static void drawVisualFeedback(GridPoint2 centerCoord, String entityType) {
    HashMap<GridPoint2, String> surroundingTiles = ServiceLocator.getUGSService().getSurroundingTiles(centerCoord, entityType);
    for (GridPoint2 mapPos: surroundingTiles.keySet()) {
      String entityName = "visual" + mapPos.toString();
      Entity visualTile;
      if (surroundingTiles.get(mapPos).equals("empty")) {
        visualTile = StructureFactory.createVisualFeedbackTile(entityName, "images/65x33_tiles/validTile.png");
      } else {
        visualTile = StructureFactory.createVisualFeedbackTile(entityName, "images/65x33_tiles/invalidTile.png");
      }
      ServiceLocator.getEntityService().registerNamed(entityName, visualTile);
      Vector2 worldLoc = ServiceLocator.getEntityService().getNamedEntity("terrain").getComponent(TerrainComponent.class).tileToWorldPosition(mapPos);
      float tileSize = ServiceLocator.getEntityService().getNamedEntity("terrain").getComponent(TerrainComponent.class).getTileSize();
      worldLoc.x -= tileSize/4;
      worldLoc.y -= tileSize/8;
      visualTile.setPosition(worldLoc);
    }
  }

Sequence Diagram

A sequence diagram for the scenario where the user clicks on the upgrade button within the buildingUI is shown below:

image

The steps in this scenario are described by:

  1. User clicks on upgrade button
  2. The upgradeButton obtains the entity service from the service locator
  3. Obtains reference to player from entity service
  4. Gets the player's gold
  5. If the player's gold is above 2000:
  • Logs "sufficient resources"
  • Upgrades the structure
  • If the structure name != wall, deducts 2000 gold from player
  • Updates the playerStatsDisplay
  1. If the player's gold is below 2000:
  • Logs "insufficient resources"
  • Creates a new filesound "sounds/purchase_fail.mp3"
  • Plays the filesound

Future Expansion

In general for future expansion, further research amongst users should be conducted in order to better understand whether they required any further features to be iterated upon, and whether the changes made above need any further improvements.