Loot Generation Sprint 2 - UQcsse3200/2026-studio-1 GitHub Wiki

Loot Generation - Sprint 2

Overview

Sprint 2 expanded the game's loot system by adding random loot spawning, new consumables, improved weapon functionality, weapon tiers and shield/blocking mechanics.

The work connects the existing item, inventory, combat and terrain systems so that loot can be generated and used directly in the game.

Main Features

Loot Generation

Loot in a level is chosen by a weighted loot table and scattered over random ground tiles, and it changes every run.

The loot table (LootTable)

  • One table holds every potion and weapon at tiers 1 to 3, plus the shield.
  • Each entry has a weight, and it is picked with a probability of its weight over the total. Rarity is simply a smaller weight: tier 1 is weighted 60, tier 2 is 30 and tier 3 is 10, so a tier 3 item turns up about once every ten rolls. The shield is weighted 10.
  • A rolled item comes out with its tier already applied.
  • The table's random numbers come from a seed, so the same seed always rolls the same items.

Where loot goes

  • A map can list its own loot spots under spawns.loot in its JSON. Those are always used first, so a map author can place loot by hand.
  • None of the current maps list any, so LevelGameArea scatters 8 items over random ground tiles instead. LootSpawnFinder finds every tile loot can rest on. A tile counts when:
    • it is open space, with a floor, wall or platform directly below it, so loot is never inside a wall, floating in mid-air or on a hazard
    • the tile directly above it is not a floor or wall, so loot never ends up in a gap sealed inside the ground
    • the player can get to it from their spawn, found by a breadth-first search over the tiles the player can move through, so walled-off pockets are skipped
    • it is not a ladder tile
  • LootPlacement.pickRandomSpots shuffles those tiles and takes 8 different ones, so no two items share a tile.
  • The shield is always placed on the first chosen spot, so it cannot be missed. Every other spot gets one item rolled from the table.
  • A map with no open ground at all falls back to a starter row of one of everything beside the player.

Different every run, but replayable

  • A new seed is picked every time a level loads, so the loot changes from run to run.
  • The seed is written to the game log as Loot seed for <map name>: <seed>. If a run shows a bug, that seed reproduces the exact same loot and positions.
flowchart TD
    A[Level loads] --> B[Pick a new loot seed and log it]
    B --> C{Does the map list loot spots?}
    C -- yes --> D[Use the map's loot spots]
    C -- no --> E[LootSpawnFinder finds every open, reachable tile standing on ground]
    E --> F[pickRandomSpots takes 8 different tiles at random]
    F --> G{Any tiles found?}
    G -- no --> H[Starter row beside the player]
    G -- yes --> I[Shield on the first spot]
    D --> I
    I --> J[Every other spot gets one item rolled from LootTable]
Loading

Classes

Class Responsibility
LootTable Weighted, seeded table of potions, weapons and the shield. createDefault(seed) builds the standard table
LootSpawnFinder Finds every tile on a map where loot can rest on the ground and the player can reach it
LootPlacement Picks random distinct spots, and pairs each spot with a rolled item
LevelGameArea Chooses the spots for a level, logs the seed and spawns the loot

Testing

  • LootTableTest: same seed gives the same loot, a different seed gives different loot, entries are picked in proportion to their weight, higher tiers are rarer, every potion can drop, and invalid entries are rejected.
  • LootSpawnFinderTest: loot goes on tiles above floors and platforms, never inside walls, in mid-air, on hazards, on ladders, on the bottom row, in a gap sealed inside the ground, under a low ceiling, or in a room the player cannot reach. A map with no player spawn still uses every room.
  • LootPlacementTest: one item per spot, the requested number of distinct random spots, every candidate used when there are fewer, the same seed picks the same spots, a different seed picks different ones, and the map's own spot list is never reordered.
  • On the real starting map (level1-greek.json) the finder returns 334 valid tiles spread across the whole level, none of them with solid ground above, so loot is spread out and always reachable.

Known limitation: reachability follows open tiles rather than jumps, so a ledge the player cannot actually jump up to can still be chosen. Maps that need exact control can list their own loot spots.

Fixed: loot used to spawn inside gaps sealed in the ground between a map's areas, because only the tile below a spot was checked. The tile-above and reachability checks were added to fix this (#194, PR #195).

Potions and Consumables

Full details are on the Potions page.

  • Added Regeneration (heals once per second) and Resistance (reduces incoming damage) potions, bringing the total to five.
  • Fixed the buff potions doing nothing in the running game: the player was missing PlayerBuffComponent, so there was nothing to apply them to.
  • Replaced accidental stacking with a deliberate rule: one buff per stat, the strongest wins, drinking the same potion again refreshes its timer, and a weaker potion is left in the inventory.
  • Gave every potion its own 32x32 sprite. Previously the Damage potion drew the poison sprite and the Speed potion drew the strength sprite.
  • Exposed the damage, speed and incoming damage multipliers for the weapon, movement and shield systems to read. Health, Regeneration and the Damage potion (for melee) work in game now. Speed, Resistance and the Damage potion on arrows and daggers are waiting on those systems to read the values.
  • Tests cover every potion, the stacking rule, expiry, tier scaling and the player's setup.

Weapon Rendering

  • Equipped weapons attach to the player's hand and follow player movement.
  • Added facing-based weapon positioning.
  • Added sword movement and swing animation.
  • Added bow aiming and draw/release animation.
  • Added weapon swapping so the rendered weapon updates with the active inventory slot.
  • Added dagger rendering and throwing behaviour.

Weapon Rendering and In-Hand Attachment

The weapon rendering system was expanded to make equipped weapons visually connected to the player and responsive to the player's movement, facing direction and inventory selection. The changes focused on improving how weapons are displayed and handled during gameplay, while keeping the rendering system connected to the existing inventory and player systems.

Sword Rendering and Movement

  • Added in-hand sword rendering so the equipped sword follows the player's position.
  • Added different hand positioning depending on the direction the player is facing.
  • Added sword swing animation during attacks.
  • Added movement-based rotation and positioning to make the sword move naturally while the player is walking.
  • Adjusted the sword's size and positioning to keep it aligned correctly with the player.

Weapon Swapping

  • Connected the displayed weapon to the player's active inventory slot.
  • When the active slot changes, the currently displayed weapon is removed and replaced with the newly selected weapon.
  • Added support for switching between different weapon types during gameplay.
  • Added weapon-specific positioning so each weapon is displayed correctly when equipped.

Bow Rendering and Animation

  • Added in-hand bow rendering and positioning.
  • Added aiming based on the player's input direction.
  • Added bow rotation so the weapon follows the current aiming direction.
  • Added a draw animation where the bow moves backwards while being charged.
  • Added release behaviour to return the bow to its normal position after firing.
  • Integrated the bow movement with the player's facing and movement behaviour.

Dagger Rendering and Throwing

  • Added dagger as a supported weapon type within the rendering system.
  • Added a dedicated dagger sprite and in-hand positioning.
  • Added dagger throwing behaviour when the weapon is used.
  • Added inventory updates when a dagger is thrown to ensure the remaining quantity is tracked correctly.
  • Added movement and lifetime handling for thrown daggers.
  • The thrown dagger is handled separately from the in-hand weapon once it leaves the player's position.

Integration and Testing

The weapon rendering changes were tested alongside the player movement and inventory systems to ensure that the displayed weapon remains consistent with the currently selected item. Testing covered weapon switching, player facing, movement, sword animation, bow aiming and draw/release behaviour, as well as dagger throwing.

Weapon Combat

  • Added projectile movement and collision handling.
  • Weapon damage is routed through the equipped weapon rather than only using the player's base attack.
  • Weapon combat integrates with the existing combat statistics and buff systems.

Weapon Tier and Damage Scaling

  • Added three weapon tiers: Tier 1, Tier 2 and Tier 3.
  • Added tier-specific damage, attack speed, knockback and range values for swords and bows.
  • Updated WeaponItem so tiered weapons use the statistics associated with their tier.
  • Updated WeaponItem.getDamage() to return the damage associated with the weapon's tier.
  • Updated WeaponGenerator to generate tiered swords and bows.
  • Added a visual tier indicator to the weapon display.
  • The weapon display shows the weapon name, type, tier and damage.
  • Added unit tests for weapon tier statistics, damage scaling and tiered weapon generation.
  • Added tests for the tier loot weights of 60, 30 and 10.
  • Ran the game locally and verified the Tier 1 sword display shows Tier 1 and 10 damage.

Shields and Blocking

  • Added a shield component for blocking incoming damage.
  • Shield behaviour includes activation and duration handling.
  • Shield functionality integrates with the player's health and combat systems.

Item Sell Prices

Generated weapons and consumable loot are assigned sell prices based on their tier. This allows loot collected during gameplay to retain its value when later sold through the shop.

  • Sword: 10 × tier
  • Bow: 8 × tier
  • Dagger: 6 × tier
  • Health Potion: 10 × tier
  • Damage Potion: 15 × tier
  • Speed Potion: 15 × tier

Loot Visual Scaling

Weapon and consumable loot sprites are rendered at a consistent smaller size so that they are visually proportionate to the player and surrounding map. The scaling preserves each item's texture aspect ratio, preventing tall weapon sprites such as swords from appearing disproportionately large.

Integration

The Sprint 2 features were tested together in the running game to verify that loot, inventory, weapons, combat, potions and shields work together.

Related Tasks

  • Potions and Loot Spawning (#90)
  • Weapon Rendering and Bow Motion (#92)
  • Weapon Combat Functionality (#123)
  • Weapon Tier and Damage Scaling (#95)
  • Shield / Blocking (#100)

Related Documentation

⚠️ **GitHub.com Fallback** ⚠️