Skip to content

Give each running power-up its own HUD indicator - #127

Merged
dmccoystephenson merged 2 commits into
mainfrom
feature/power-up-status-ui
Aug 9, 2026
Merged

Give each running power-up its own HUD indicator#127
dmccoystephenson merged 2 commits into
mainfrom
feature/power-up-status-ui

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

  • Each running power-up is now given its own HUD indicator in both UIs, replacing the single "<label>: <n>s" text line that identified a power-up only by name and stepped down once a second.
  • An indicator carries the symbol the power-up is drawn with on the grid, its label, the whole seconds left, and a duration meter that drains as the timer does — filled in the power-up's own color in the graphical UI, drawn with the same block characters as the level-progress bar in the text UI.
  • Ophidian.getActivePowerUpStatuses() now returns records of plain data (symbol, color, secondsRemaining, durationSeconds, fractionRemaining) instead of (label, seconds) tuples, so nothing is pre-formatted by gameplay and each renderer presents an indicator in its own idiom. The UI/gameplay decoupling asked for in PR Add text-based UI option with clean architecture, comprehensive testing, CI/CD verification, and performance optimizations #95 is preserved.
  • fractionRemaining is what makes activation and expiry read smoothly: it falls continuously across frames where the whole seconds beside it only change once a second, and it returns to a full meter when an already-running power-up is collected again. Animated transitions beyond that are left to Add Menu Transitions and Animations #79, which is scoped to animations.
  • getPowerUpColor() and getPowerUpTextSymbol() were added alongside the existing getPowerUpHudLabel(), so the HUD is not made to read a registry dictionary directly nor to hold a PowerUp entity that has already been removed from the board.
  • config.gray was added for the unfilled track of a HUD meter.
  • The power-up paragraph in README.md was updated to describe what an indicator shows.

Test plan

  • python -m pytest — 231 passed (223 before, 8 added)
  • New coverage: symbol/color on a status record, fractionRemaining mid-run and after a refresh, the text meter's format and its full/empty ends, the graphical meter being drawn and draining
  • Existing HUD-geometry tests updated for the taller indicator row and re-verified
  • python -m compileall src
  • black and autoflake (via format.sh) report no changes

Closes #72

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

Both HUDs listed a running power-up as one line of text, "<label>: <n>s",
which identified it only by name and stepped down once a second. Each
power-up now gets an indicator carrying the symbol it is drawn with on the
grid and a meter that drains with its timer - filled in the power-up's own
color in the graphical UI, block characters in the text UI.

getActivePowerUpStatuses() hands over plain data (symbol, color, seconds
left, fraction of the duration left) rather than a formatted string, so
each renderer presents an indicator in its own idiom and gameplay stays
out of the UI.

Closes #72

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review (posted as a comment rather than as a Review object, because the reviews API is not available to this session; each finding is anchored below as path:line — finding).

The change does what it says: getActivePowerUpStatuses() hands over plain data and both renderers format it, so the UI/gameplay decoupling asked for in PR #95 is kept — no renderer is made to import the power-up registry, and no gameplay code is made to build a display string. The row geometry was re-derived rather than assumed, and the previously-passing geometry assertions were updated to match instead of being loosened. Three findings, all minor; none of them changes behaviour that a player sees.

src/textui/textrenderer.py:166 — formatDurationMeter is not defensive about an out-of-range fraction. filled = int(cells * fractionRemaining) is correct for every value gameplay produces, since getActivePowerUpStatuses() clamps to at most 1 and ActivePowerUps.remainingSeconds() never goes negative. It is a public method of the renderer, though, and a fraction above 1 would make "░" * (cells - filled) collapse to an empty string and silently widen the bar. Clamping filled into 0..cells here would make the renderer safe for any caller instead of relying on its one current one.

src/ophidian.py:869 — docstring line break splits "power-up" as "power / -up". A wrapping artifact in the new getActivePowerUpStatuses() docstring: the line ends on the power and the next begins -up is already recognized by. Worth rewrapping, since the surrounding docstrings in this file are otherwise carefully wrapped.

tests/rendering/test_hud_and_banner.py:175 — meter geometry and the power-up's color are restated as literals in the tests. width // 2 - 70, the + 9 vertical offset and (0, 0, 255) are each written out by hand in the two new meter tests, duplicating POWER_UP_INDICATOR_METER_WIDTH and the PowerUpType.SPEED registry entry. Changing either constant would fail these tests with a pixel-color mismatch rather than pointing at what actually moved. Deriving the sample points from the constants, and the expected color from getPowerUpDefinition, keeps the assertion about what is drawn rather than about numbers that happen to agree today. The same applies at line 197.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

formatDurationMeter now clamps the cell count instead of trusting the
fraction it is given, so a value outside 0..1 can't collapse the empty
half of the bar and change its width. The pygame meter tests derive their
sample points from POWER_UP_INDICATOR_METER_WIDTH and their expected color
from the registry, rather than restating both as pixel literals.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson
dmccoystephenson merged commit 7ae70f1 into main Aug 9, 2026
@dmccoystephenson
dmccoystephenson deleted the feature/power-up-status-ui branch August 9, 2026 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Power-up Status UI

1 participant