CombatStatsChangePopup - UQcsse3200/2024-studio-2 GitHub Wiki
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.
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.
- 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 | |
Hunger | |
Poisoning |
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 CombatStatsChangePopup
s 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);
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))
));
}
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);
}
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
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()