CombatStatsChangePopup - UQcsse3200/2024-studio-2 GitHub Wiki

Overview

The CombatStatsChangePopup class provides a visual representation of changes in stats (health and hunger) for both the player and animal during combat, covering all stats changes caused by combat moves. The stats changes are displayed briefly as an entity is affected by individual combat moves, before they are animated and fade out of the screen. A demonstration of how it works can be seen from the video testing.

Design

The combat system follows a turn-based approach where players select their moves, and the outcomes are displayed on screen. Stat changes are visualized using color-coded text that appears above the entities involved in combat.

CombatStatsChangePopup

  • Displays stat changes (health and hunger) for both player and enemy
  • Uses color-coded text to represent different types of stat changes:
    • Red: Health decrease
    • Green: Health increase
    • Orange: Hunger change
  • Shows status effect names with unique colors:
    • Maroon: Bleeding
    • Forest green: Poisoned
    • Yellow: Shocked
Animation Change Type Image
Health image
Hunger image
Poisoning image

Usage

The CombatStatsChangePopup UIComponent is added to the entity in the CombatManager class, which handles calling combinations of player and enemy moves:

    entity.addComponent(new CombatStatsChangePopup());

This allows the display of CombatStatsChangePopups to be triggered from the CombatManager as follows, passing in the stats change:

    entity.getEvents().trigger("enemyHungerStatsChangePopup", statsChange.getHungerChange());
    entity.getEvents().trigger("enemyHealthStatsChangePopup", statsChange.getHealthChange());
    entity.getEvents().trigger("playerHungerStatsChangePopup", statsChange.getHungerChange());
    entity.getEvents().trigger("playerHealthStatsChangePopup", statsChange.getHealthChange());

For special moves, the change in health, hunger, and playerStats are passed in. The playerStats is used to determine the type of status effects (i.e. BLEEDING, POISONED, or SHOCKED) applied, so that they can be displayed along with their corresponding popup text color:

    entity.getEvents().trigger("statusEffectStatsChangePopup", -healthReduction, -hungerReduction, playerStats);

Key methods

createStatsChangePopup()

This method creates and animates the popups, according to the popupText and textColor specified. It also takes an isPlayer argument to determine the boundaries in which to randomly display the popups (either on the Player or Enemy side of the screen).

    private void createStatsChangePopup(CharSequence popupText, Color textColor, boolean isPlayer) {
        // Create the label
        Label statsChangePopup = new Label(popupText, skin, "title", textColor);
        statsChangePopup.setFontScale(Math.max(Gdx.graphics.getHeight() / 1000f, 1f));
        statsChangePopups.add(statsChangePopup);
        stage.addActor(statsChangePopup);

        // Randomize position within defined boundaries
        float randomX, randomY;
        randomY = MathUtils.random(MIN_Y, MAX_Y);
        if (isPlayer) {
            randomX = MathUtils.random(PLAYER_MIN_X, PLAYER_MAX_X);
        } else {
            randomX = MathUtils.random(ENEMY_MIN_X, ENEMY_MAX_X);
        }
        statsChangePopup.setPosition(randomX * Gdx.graphics.getWidth(), randomY * Gdx.graphics.getHeight());

        float staggerDelay = 0.5f * statsChangePopups.size();

        // Label animations
        statsChangePopup.addAction(Actions.sequence(
                Actions.hide(),
                Actions.delay(staggerDelay),
                Actions.show(),
                Actions.moveBy(0, 50, 1f),
                Actions.fadeOut(1f),
                Actions.run(() -> disposePopup(statsChangePopup))
        ));
    }

createHealthStatsChangePopup() and createHungerStatsChangePopup()

These functions process the numeric changes in stats and calls createStatsChangePopup() to display the formatted string in the pre-defined text color based on stats type.

    public void createHealthStatsChangePopup(int statsDiff, boolean isPlayer) {
        if (statsDiff == 0 || Math.abs(statsDiff) > 10000) return;
        String statsDiffText = (statsDiff > 0) ? "+" + statsDiff : Integer.toString(statsDiff);
        CharSequence popupText = String.format("HP%s", statsDiffText);
        // Swap health change isPlayer because target applies damage to itself
        createStatsChangePopup(popupText, (statsDiff > 0) ? Color.GREEN : Color.RED, (statsDiff > 0) == isPlayer);
    }

    private void createHungerStatsChangePopup(int statsDiff, boolean isPlayer) {
        if (statsDiff == 0) return;
        String statsDiffText = (statsDiff > 0) ? "+" + statsDiff : Integer.toString(statsDiff);
        CharSequence popupText = String.format("HGR%s", statsDiffText);
        createStatsChangePopup(popupText, Color.ORANGE, isPlayer);
    }

UML Diagrams

Class Diagram

classDiagram
    class CombatManager {
        -Entity player
        -Entity enemy
        -CombatStatsComponent playerStats
        -CombatStatsComponent enemyStats
        -Action playerAction
        -Action enemyAction
        +executeMoveCombination(Action, Action)
        +handleStatusEffects()
        +checkCombatEnd()
    }
    class CombatStatsChangePopup {
        +create()
        +update()
        -displayStatChange(Entity, String, int)
    }
    class UIComponent {
        +create()
        +render()
    }
    class Component {
        +create()
        +update()
    }
    class Entity {
        +addComponent(Component)
        +getComponent(Class)
    }
    CombatManager --|> Component
    CombatStatsChangePopup --|> UIComponent
    UIComponent --|> Component
    CombatManager --> Entity : manages
    CombatManager --> CombatStatsChangePopup : uses
    Entity --> Component : has
Loading

Sequence Diagram

sequenceDiagram
    participant Player
    participant CombatManager
    participant Enemy
    participant CombatStatsChangePopup

    Player->>CombatManager: Select Action
    CombatManager->>Enemy: Select Action
    CombatManager->>CombatManager: executeMoveCombination()
    CombatManager->>CombatStatsChangePopup: Trigger stat changes
    CombatStatsChangePopup->>Player: Display player stat changes
    CombatStatsChangePopup->>Enemy: Display enemy stat changes
    CombatManager->>CombatManager: handleStatusEffects()
    CombatManager->>CombatStatsChangePopup: Trigger status effect changes
    CombatStatsChangePopup->>Player: Display status effect changes
    CombatManager->>CombatManager: checkCombatEnd()
Loading
⚠️ **GitHub.com Fallback** ⚠️