Repository navigation
Consumables
Consumables restore health, provide temporary protection or stat boosts, affect enemies, and attract nearby items. Each successful use removes one item from the player's inventory.
Items are identified through ItemCatalog. The inventory stores their quantities and four consumable-slot assignments, while the selection and effect components handle choosing and using them.
- Collect a nearby ground item with E, or buy supplies from the Merchant.
- Press I to open the inventory book. On its Consumables page, select an item and press Space to equip or unequip it.
- Close the book with I or Escape.
- Press Tab to select a consumable slot, then Q to use its item.
| Context | Control | Action |
|---|---|---|
| Gameplay | E | Request pickup of a nearby item. NPC interaction takes priority. |
| Gameplay | I | Open the inventory book. |
| Gameplay | Tab | Cycle through all four consumable slots, including empty slots. |
| Gameplay | Q | Use the item in the selected slot; an empty slot does nothing. |
| Inventory book | Q / E | Change book page. |
| Inventory book | WASD / arrows, then Space | Select an item, then equip or unequip it. |
| Inventory book | I / Escape | Close the book. |
The four slots hold assignable item types. Picking up a newly owned consumable type fills the first empty slot when one is available. Additional types remain in the backpack. Shop purchases and Item Draw rewards add stock without automatically assigning a slot; equip a newly purchased type through the book.
Using the last copy clears that item's slot. Selection remains on the same slot until the player cycles again. Unequipping an item keeps its inventory quantity. See Inventory UI (Team Five) for book equipment controls and Consumable Slots for the four-slot display and selection behaviour.
These are the nine built-in consumables and their default effects:
| Item | Catalog ID | Default effect |
|---|---|---|
| Small Health Potion | HEALTH_POTION |
Immediately restores up to 25 HP, capped at maximum health. |
| Medium Health Potion | MEDIUM_HEALTH_POTION |
Immediately restores up to 50 HP, capped at maximum health. |
| Large Health Potion | LARGE_HEALTH_POTION |
Immediately restores up to 100 HP, capped at maximum health. |
| Shield | SHIELD |
Blocks damage routed through the status-effect system for 8 seconds. |
| Speed Potion | SPEED_POTION |
Applies a 1.5× movement-speed multiplier for 8 seconds. |
| Strength Potion | STRENGTH_POTION |
Applies a 1.5× attack multiplier for 8 seconds. |
| Freeze Bomb | FREEZE_BOMB |
Freezes eligible enemies currently on screen for 3 seconds. |
| Burn Vial | BURN_VIAL |
Burns eligible enemies currently on screen: 2 damage per tick, a 1-second tick cooldown, and a 6-second duration. |
| Magnet Potion | MAGNET_POTION |
Pulls nearby Gold and consumables for 10 seconds, within a 4-world-unit radius. |
Healing at full health is rejected without consuming the potion. Reusing Shield, Speed, Strength or Magnet replaces that item's previous player effect with a fresh duration. Repeated uses of the same potion do not compound its multiplier or add the old remaining duration.
Freeze and Burn select targets when used. A valid use consumes one item even if no eligible enemy is on screen. Each use applies independent effects to its targets; those effects are managed by the enemies' status controllers rather than the player's potion-refresh map. Burn timing follows the shared Burning update loop, so the configured cooldown and duration do not guarantee a fixed total number of damage ticks.
ConsumableSelectionComponent tracks the selected slot. ConsumableEffectComponent owns validation, inventory consumption and effect application.
An existing player can receive a use request through its event handler:
player.getEvents().trigger(
ConsumableEffectComponent.USE_REQUEST, ItemIds.SPEED_POTION);Code that needs the result can call boolean tryUse(String id) on the player's ConsumableEffectComponent. Both routes use the same validation and consumption path. Callers should submit the request without removing stock first.
- Check that the effect component is active, the item ID is valid, and the player is alive and owns the item.
- Create one concrete item through
ItemCatalog.create(id, 1)and check that it is aConsumableItem. - Call the item's
canUse(stats, effects, time)to validate its specific requirements. - Remove exactly one unit through
InventoryComponent.removeConsumable(id). - Apply the item with
use(stats, time). If it returns a player timed effect, replace the previous effect for that item ID and register the new one with the status controller. - Emit
itemUsedwith the item ID.
Invalid IDs, unavailable stock, dead or disposed players, and rejected item-specific checks leave the inventory unchanged. The base consumable check requires a live status controller and a time source. Instant healing has its own health check; Freeze and Burn additionally need the world camera and entity service, while Magnet needs the entity service.
These events use the player entity's event handler:
| Event | Parameters | Purpose |
|---|---|---|
cycleConsumable |
None | Advance the selected consumable-slot index. |
selectedConsumableChanged |
String itemId |
Notify observers of the selected type; the ID can be null for an empty slot. |
useSelectedConsumable |
None | Resolve the selected slot and request use of its item. |
useConsumable |
String itemId |
Request validation and use through ConsumableEffectComponent. |
consumableInventoryChanged |
String itemId, Integer count |
Notify observers of the final inventory count. |
itemUsed |
String itemId |
Notify observers after successful effect application. |
itemUsed is a notification. Its listeners must not consume another item or initiate the same use again.
ConsumableItem defines the shared item contract:
| Method | Responsibility |
|---|---|
boolean canUse(CombatStatsComponent stats, StatusEffectsControllerComponent effects, GameTime time) |
Check whether the item can be used in the current state. |
TimedStatusEffect use(CombatStatsComponent stats, GameTime time) |
Apply the effect and return a player timed effect, or null when application is immediate or targets enemies. |
void pickUp(Entity player) |
Add the item's quantity to the player's inventory through addConsumable. |
InstantHealingPotion applies capped healing immediately. The catalog registers its small, medium and large variants. It also supports positive custom healing amounts through derived IDs such as HEALTH_POTION_40; these custom IDs are accepted by the catalog but are not included in its built-in ID list.
ShieldPotion returns the shared Shield timed effect. Its damage handler sets intercepted damage to zero. Protection relies on the normal combat/status-effect damage path.
Speed and Strength extend StatBoostPotion, which returns a timed stat multiplier. The shared controller combines active multipliers, and combat stats calculate their effective values from those multipliers. The potions do not permanently overwrite the player's base stats.
Freeze and Burn use StrategyOnscreen to find enemies whose centres lie inside the world camera's viewport bounds. This targeting does not perform a distance-radius or line-of-sight check.
Freeze adds the shared FrozenEffect to eligible enemy status controllers, immobilising the enemy and applying zero multipliers to its effective stats for the duration. Burn adds VialBurning, which extends the existing Burning effect and provides feedback when a burn tick actually reduces health.
MagnetPotion returns MagnetEffect. During updates, it scans registered ground-item entities and pulls currency and consumables towards the player at 6 world units per second. Charms are excluded.
Items are collected within 0.4 world units, or when the current movement step would reach them. Collection calls the item's normal pickUp(player) method and schedules the ground entity for disposal after the update loop.
TimedStatusEffect records an expiry deadline using GameTime.getTime(). The status controller updates and removes completed effects. Player death clears the controller's effects, and disposing the consumable component removes its tracked player effects.
Pausing world updates does not pause this elapsed-time clock. Remaining potion duration can therefore decrease while the inventory or shop is open; expired effects are cleaned up when controller updates resume.
The effect component exposes long getRemainingMs(String id) and float getRemainingFraction(String id) for the actual tracked player effect. The fraction is between zero and one. isShielded() and getShieldRemainingMs() provide shield-specific queries.
The Consumable Slots display shows four equipped positions along the bottom of the screen, with item icons, quantities, selection and Tab / Q hints. Its Speed Potion ring reads the actual remaining fraction from ConsumableEffectComponent; the ring is shown only while that type remains assigned to a slot.
PlayerFactory attaches the following feedback components:
| Component | Feedback |
|---|---|
HealingPotionFeedbackComponent |
An expanding pulse, rising crosses and the actual amount of HP restored. |
ShieldPotionFeedbackComponent |
A barrier around the player while the consumable shield is active. |
SpeedPotionAfterimageComponent |
Afterimages while the player moves with an active Speed Potion. |
BurnVialFeedbackComponent |
Animated flames tracking enemies affected by the vial's burn. |
Freeze and Burn also request white and fire screen flashes through the render service. These visual components display the results of item use; inventory consumption and gameplay effects remain in their owning components.
The consumable implementation and its integration with inventory and UI include the following test classes:
ItemCatalogTestConsumableItemTestConsumableUseIntegrationTestConsumableSelectionComponentTestFreezeBombUseTestBurnVialUseIntegrationTestMagnetUseIntegrationTestInventoryComponentTestConsumableHotbarDisplayTestSpeedPotionTimerRingTestHealingPotionFeedbackComponentTestShieldPotionFeedbackComponentTestSpeedPotionAfterimageComponentTestBurnVialFeedbackComponentTestConsumableRoomAssetsIntegrationTest