Arrow Selection Wheel Sprint 3 - UQcsse3200/2026-studio-2 GitHub Wiki

Arrow Selection Wheel Sprint 3

The backend for this feature was built in Sprint 2 and is documented on Arrow Selection Wheel. Sprint 3 added the on-screen display, cut the wheel down to the three arrow types the player can actually carry, and fixed the wheel going out of sync when arrows run out.

What changed this sprint

Change Why
Wheel reduced from four types to three Poison had no matching ItemType, so that wedge could be highlighted but never selected
ArrowWheelDisplay added The wheel had no visuals, so the player could not see what they were selecting
getWheelTypes() added The display and the direction maths now read the same list, so they cannot drift apart
Fallback when arrows run out The wheel could keep reporting an arrow the player could no longer fire
PoisonBuff end time changed to long A millisecond timestamp was stored in a float and lost precision over a long session

Three arrow types

Wedge Position Inventory item
Standard top STANDARD_ARROW
Fire 120 degrees clockwise FIRE_ARROW
Ice 240 degrees clockwise ICE_ARROW

ArrowType.forDirection() splits 360 degrees evenly across whatever is in WHEEL_TYPES, so adding or removing a type needs no change to the direction maths. The list is exposed so the UI reads the same source of truth:

ArrowType[] types = ArrowType.getWheelTypes();  // {STANDARD, FIRE, ICE}, clockwise from the top

Rope arrow (ArrowType.GRAPPLE) is a real arrow item but is deliberately left off the wheel, because it is a movement tool rather than a combat arrow. It stays on the hotbar.

Drawing the wheel

ArrowWheelDisplay renders the wheel while it is open. It lays its wedges out from getWheelTypes(), so the wedge drawn under the pointer is always the one forDirection() will return.

  • The wedge under the pointer is drawn brighter and slightly larger.
  • A type the player has no arrows for is greyed out.
  • Each label carries its ammo count, for example Fire x3.
  • The centre shows the type currently selected.

Running out of arrows

When the last arrow of the selected type is spent, InventoryComponent empties the slot and moves its selection to the next occupied slot on its own. That slot can hold a potion, which used to leave the wheel showing an arrow the player could no longer fire.

ArrowWheelComponent now listens for inventorySelectionChanged and falls back to an arrow type the player still holds, firing arrowSelected so the display updates without the player reopening the wheel. If the player has no arrows at all, the selection is left alone and nothing throws.

This needed no change to ItemUseComponent, which is shared across the team.

Read API

Method Returns
isOpen() whether the wheel should be drawn
getHighlighted() the type under the pointer, or null in the centre deadzone
getSelected() the type the bow currently fires
isAvailable(type) whether the type can be chosen (unlocked and in the inventory)
setAvailable(type, unlocked) locks or unlocks a type

Events

Event When
openArrowWheel / closeArrowWheel input, on TAB down and up
arrowWheelPointerMoved input, with the pointer's offset from the wheel centre
arrowWheelOpened / arrowWheelClosed the wheel opened or closed
arrowHighlighted the pointer moved onto a different wedge
arrowSelected a type was chosen, or fallen back to
arrowSelectionRejected the highlighted type could not be selected
inventorySelectionChanged listened for, to re-sync when arrows run out

Testing

Test class Covers
ArrowWheelComponentTest opening and closing, pointer highlighting, availability, inventory slot selection, out-of-ammo fallback
ArrowTypeTest wedge geometry for three types, the centre deadzone, directions between wedge centres
ArrowWheelDisplayTest ammo labels, greyed-out and highlighted wedge colours

ArrowWheelComponent sits at 96.1% line coverage and ArrowType at 97.0%, against a project average of 58.3%.

Known gaps

  • ArrowWheelDisplay is a placeholder drawn with ShapeRenderer. The UI team is replacing it with a styled version (PR #212).
  • POISON and POTION remain in ArrowType for the thrown poison flask, but neither is a selectable arrow. They should either be documented as such or moved out of the enum.