Crystal Class Code Details - UQdeco2800/2022-studio-1 GitHub Wiki

Crystal Creation:

CrystalFactory: The essential class that defines the crystal entity and contains the method to create said entity.

path: source/core/build/classes/java/main/com/deco2800/game/entities/factories/CrystalFactory.class

Variables:

crystalStats: reads the Crystal Configuration JSON file and stores initial Crystal statistics such as Health, Base Attack and Level

Methods:

createCrystal: Creates the crystal entity with necessary qualities and components such as health, level and physics component.

CrystalConfig: Extends BaseEntityConfig class to define the properties stored in crystal config files to be loaded by the Crystal Factory


Sprint # 2:

Upgrade of Crystal

Upgrade of Crystal can be triggered by the player clicking on the crystal, this feature will be integrated with a small pop up(similar to building ui) in future sprints. As users start clicking on the screen, the touchDown function in keyboardPlayerInput class is triggered. touchDown will then call the crystalClicked function in crystal factory which uses the camera component to determine where the player is clicking. If the click is within bounds of the Crystal entity, crystalClicked will return true and upgradeCrystal will be called. upgradeCrystal will then access the level of Crystal entity and call triggerCrystal to spawn crystal with new texture to match Crystal level. Max health and level of the crystal will also be increased accordingly.

Restoration of Crystal Health

Restoration of Crystal health is achieved with recoverCrystalHealth function in the crystal factory. recoverCrystalHealth uses a timer to keep track of time and service locator to get the current status of the daynightcycle service. If the current status returned is dawn, day or dusk, it will increase the current crystal health by 10. This process is repeated every 3 seconds until the arrival of night or crystal health is fully restored.

Crystal health bar UI

The functionality of Crystal health bar is achieved through the health bar component. A conditional statement is added to healthbar component class to ensure the health bar does not appear above the Crystal entity. The healthbar is then accessed in playerStatsDisplay and displayed on the top left corner of the screen. Some customisation of colours along with stacking a healthbar design on top of the actual healthbar is done to align the design of healthbar with overall aesthetic of the game.


Sprint # 4:

In sprint 4, how the Crystal is upgraded is changed dramatically as a UI Pop Up is implemented to improve the overall usability of the Crystal feature. Animations and a screen shake effect are also implemented to increase visual enjoyment factor and add to the "Juiciness" of the game.

Determine if Crystal is clicked

In the touchUp() function within KeyboardPlayerInput.java, getClickedEntity() from the UGS service is used to retrieve the entity being clicked on by the player.

Entity clickedEntity = ServiceLocator.getUGSService().getClickedEntity();

If clickedEntity happens to be Crystal, makeCrystalPopUp() from MainGameBuildingInterface.class is called to display a UI Pop Up which enables the player to upgrade the Crystal. However, if the Crystal is clicked but the Crystal has already reach its maximum level, makeCrystalPopUp2() from MainGameBuildingInterface.class is called to display a different Pop Up to notify the player. A timer task is implemented to ensure the "Crystal has reached maximum level" notification is displayed on the screen for approximately 3 seconds before disappearing.

if (clickedEntity == crystal) {
          if (crystal.getComponent(CombatStatsComponent.class).getLevel() < 3) {
            PopUp = ServiceLocator.getEntityService().getNamedEntity("ui").getComponent(MainGameBuildingInterface.class).makeCrystalPopUp(true, screenX, screenY);
            isVisible = true;
          }
          else {
            PopUp = ServiceLocator.getEntityService().getNamedEntity("ui").getComponent(MainGameBuildingInterface.class).makeCrystalNoti(true);
            isVisible = true;
            final int[] i = {0};
            Timer time = new Timer();
            TimerTask showUI = new TimerTask() {
              @Override
              public void run() {
                if (i[0] == 4){
                  PopUp.remove();
                  isVisible = false;
                }
                i[0]++;
              }
            };
            time.scheduleAtFixedRate(showUI, 150, 150);
          }
        }

Upgrade UI Pop Up

The Crystal Upgrade UI Pop Up is handled by the makeCrystalPopUp() function in MainGameBuildingInterface.java. Components of said UI Pop Up such as Crystal image, Crystal health and buttons is customised based on the current level of the Crystal and the amount of Gold in possession of the player. For example, if the player has insufficient gold to upgrade the crystal, the Upgrade button will turn red, signifying that the Crystal is unable to be upgraded.

 if (level == 1){
            cost = "500";
            health = "+200";
            crystalhealth = new Image(ServiceLocator.getResourceService().getAsset("images/crystalhealth3.png", Texture.class));
            crystalImage = new Image(
                    ServiceLocator.getResourceService().getAsset("images/crystal2.0.png", Texture.class));
            if (playerGold >= 500) {
                button = new Texture(Gdx.files.internal("images/upgrade500.2.png"));
            }
            else {
                button = new Texture(Gdx.files.internal("images/upgradeFail500.png"));
            }

        } else {
            cost = "1500";
            health = "+300";
            crystalhealth = new Image(ServiceLocator.getResourceService().getAsset("images/crystalhealth4.png", Texture.class));
            crystalImage = new Image(
                    ServiceLocator.getResourceService().getAsset("images/crystal_level3.png", Texture.class));
            if (playerGold >= 1500) {
                button = new Texture(Gdx.files.internal("images/upgrade1500.2.png"));
            }
            else {
                button = new Texture(Gdx.files.internal("images/upgradeFail1500.png"));
            }        }

A listener is then added to the Upgrade Button to determine if the button is being clicked. If the Upgrade is being clicked and the player has sufficient gold to upgrade the Crystal, the upgradeCrystal function from CrystalService is called. Otherwise, an upgrade failed sound effect will be played.

 upgradeButton.addListener(
                new ChangeListener() {
                    @Override
                    public void changed(ChangeEvent changeEvent, Actor actor) {
                        logger.info("Upgrade Button clicked");

                        if (level == 1 && playerGold >= 500) {
                            logger.info("Sufficient gold to upgrade crystal");
                            CrystalService.upgradeCrystal();
                            //entity.getEvents().trigger("screenShake");
                            CrystalUI.remove();
                        }
                        else if (level == 2 && playerGold >= 1500) {

                            logger.info("Sufficient gold to upgrade crystal");
                            CrystalService.upgradeCrystal();
                            //entity.getEvents().trigger("screenShake");
                            CrystalUI.remove();
                        }

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

Crystal Upgrade

The upgradeCrystal() function in CrystalService.java handles the changing of Crystal sprite, deduction of player gold, increase of Crystal health and incrementing Crystal level appropriately based on the current level of the crystal. It then calls the screenShake() function in the same class (CrystalService).

public static void upgradeCrystal() {
        Entity crystal = ServiceLocator.getEntityService().getNamedEntity("crystal");
        int level = crystal.getComponent(CombatStatsComponent.class).getLevel();
        Entity player = ServiceLocator.getEntityService().getNamedEntity("player");
        if (level == 1) {
            triggerCrystal("images/crystal_level2.png");
            player.getComponent(InventoryComponent.class).addGold(-500);
            PlayerStatsDisplay.updateItems();
            crystal.getComponent(CombatStatsComponent.class).setMaxHealth(1200);

        } else if (level == 2) {
            ServiceLocator.getEntityService().getNamedEntity("crystal2").dispose();
            triggerCrystal("images/crystal_level3.png");
            ServiceLocator.getEntityService().unregisterNamed("crystal2");
            player.getComponent(InventoryComponent.class).addGold(-1500);
            PlayerStatsDisplay.updateItems();
            crystal.getComponent(CombatStatsComponent.class).setMaxHealth(1500);
        }
        // upgrading only increases max health and does not impact current health
        // crystal.getComponent(CombatStatsComponent.class).setHealth(1000+(100*level));
        crystal.getComponent(CombatStatsComponent.class).setLevel(level + 1);
        screenShake();
    }

Screen Shake Effect

A screen shake effect is triggered to imitate the vibrations as more land emerge from the water as the Crystal is being upgraded. This is achieved by using a timer task and moving the in-game camera repeatedly to create an effect as if the screen is shaking. The power(force) of the shaking effect starts off weak and gradually increase as the "power" variable is incremented on every shake. Finally, after the final shake incrementMapLvl() from TerrainComponent.class is called and new areas of land are revealed to the player.

    public static void screenShake(){
        Entity cam = ServiceLocator.getEntityService().getNamedEntity("camera");
        CameraComponent cameraComp = cam.getComponent(CameraComponent.class);
        OrthographicCamera camera = (OrthographicCamera) cameraComp.getCamera();
        long currentGameTime = ServiceLocator.getTimeSource().getTime();
        Entity player = ServiceLocator.getEntityService().getNamedEntity("player");

        final int[] shakeNum = {0};
        final int[] power = {1};


        Timer time = new Timer();
        TimerTask shake = new TimerTask() {
            @Override
            public void run() {
                camera.update();

                if (shakeNum[0] == 8){
                    /* Expand the map! */
                    Entity terrain = ServiceLocator.getEntityService().getNamedEntity("terrain");
                    terrain.getComponent(TerrainComponent.class).incrementMapLvl();
                    time.cancel();
                }
                if ( !interval) {
                    camera.translate(power[0], power[0]);
                    interval = true;
                } else {
                    camera.translate(-power[0],-power[0]);
                    interval = false;
                }
                power[0]++;
                shakeNum[0]++;

            }
        };
        time.scheduleAtFixedRate(shake, 150, 150);
    }

The Sequence Diagram of the Crystal Upgrade is as follows: CrystalSequence