Skip to content

Weapon System

Sarah Preston edited this page Oct 6, 2026 · 1 revision

Weapon System

Overview

A weapon is a WeaponItem: an inventory item with a type, a tier and a set of statistics. The same class is used by the player (a weapon held in the inventory and swung with the attack key) and by enemies (a weapon handed to an attack component, see Melee Attacks and Ranged Attacks).

All of the classes are in components/loot/.

Class Kind Responsibility
WeaponType enum the five kinds of weapon
WeaponTier enum the three tiers, each holding the statistics of every weapon type at that tier and a loot weight
WeaponStats interface what a block of weapon statistics can be asked for
WeaponStatsPreset class one weapon type's statistics at one tier
WeaponItem class the weapon itself: an Item with a type, a tier, statistics and a windup
WeaponGenerator class builds a WeaponItem from a type and a tier number
WeaponUpgrader class builds the next tier of a weapon
WeaponUpgradeEffect class the Upgrade Stone: upgrades the weapon the player is holding

Weapon Types

Type Used for
SWORD melee
DAGGER a small weapon that stacks to 20
AXE melee; the hardest hitting and the longest reach
BOW ranged; fires projectiles
NATURAL an enemy attack that is part of its body (a bite, a stomp, a gaze); see Natural Weapons below

Attack components check the type when they are built:

  • MeleeAttackComponent rejects a BOW.
  • RangedAttackComponent rejects a SWORD, a DAGGER and an AXE.
  • Both accept NATURAL.

Tiers and Statistics

There are three tiers. Each tier holds one WeaponStatsPreset per weapon type, with these statistics: damage, attack speed, knockback, range and projectile count.

Tier 1 (loot weight 60)

Type Damage Attack speed Knockback Range Projectiles
Sword 10 1.0 1.0 2.0 1
Bow 7 1.0 0.5 8.0 1
Dagger 3 1.0 0.5 1.0 1
Axe 12 1.0 1.3 2.3 1

Tier 2 (loot weight 30)

Type Damage Attack speed Knockback Range Projectiles
Sword 20 1.2 1.5 2.5 1
Bow 4 1.2 0.75 10.0 5
Dagger 6 1.3 0.75 1.2 1
Axe 23 1.2 1.8 2.8 1

Tier 3 (loot weight 10)

Type Damage Attack speed Knockback Range Projectiles
Sword 30 1.5 2.0 3.0 1
Bow 3 1.5 1.0 12.0 10
Dagger 9 1.6 1.0 1.4 1
Axe 34 1.5 2.3 3.3 1

Things to notice:

  • A higher tier bow fires more projectiles, each doing less damage: 1 x 7, then 5 x 4, then 10 x 3.
  • The axe hits harder, knocks back farther and reaches further than the sword at every tier.
  • The loot weight makes a tier 1 weapon six times as likely to drop as a tier 3 one. LootTable reads it through getLootWeight().

WeaponTier.fromTierNumber(n) finds a tier by its number and throws IllegalArgumentException for a number outside 1 to 3. getStats(type) throws for a null type and for NATURAL, which has no tier statistics.

Weapon Item

WeaponItem extends Item with the item type WEAPON. It can be built three ways.

From a tier (what WeaponGenerator uses). The damage and the other statistics are looked up from the tier.

public WeaponItem(String name, WeaponType weaponType, WeaponTier weaponTier, int quantity, int maxQuantity, float windupDuration) {}

From a damage value, with no tier statistics. The tier is reported as 1, and attack speed, knockback and range are reported as 0.

public WeaponItem(String name, WeaponType weaponType, int damage, int quantity, int maxQuantity, float windupDuration) {}

public WeaponItem(String name, WeaponType weaponType, int damage, int quantity, int maxQuantity) {}

As a natural weapon, described below.

The constructors throw IllegalArgumentException for a null type, a null tier, or a damage of zero or below.

Windup

windupDuration is the delay, in seconds, between an attack being started and the hit landing. It belongs to the weapon because it matches the weapon's swing animation. An attack component reads it with getWindupDuration() and refuses a weapon whose windup is not shorter than the attack's cooldown. setWindupDuration changes it; the Minotaur's factory sets its axe's windup to 0.

Weapon Generator

new WeaponGenerator().generateWeapon(WeaponType.AXE, 2)

builds a tier 2 axe. The generator sets the name, the stack size, the windup and the sell price for each type:

Type Name Stack size Windup Sell price
Sword Basic Sword 10 2 s 10 x tier
Bow Basic Bow 10 3 s 8 x tier
Dagger Basic Dagger 20 1 s 6 x tier
Axe Basic Axe 2 3 s 12 x tier

It throws IllegalArgumentException for a null type, a tier of zero or below, a tier above 3, and for NATURAL.

Natural Weapons

Some enemies attack with their body and carry no weapon. So that these attacks can use the same attack components as an armed enemy, the factory builds a natural weapon:

WeaponItem fists = WeaponItem.natural("Cyclops Fists", config.baseAttack, config.melee.windup);

A natural weapon has the type NATURAL, a quantity of 1, and exactly the damage and windup it is given. It has no tier statistics. It is never added to the enemy's inventory, so it never drops, and it is never offered as loot.

Enemy Natural weapons
Cyclops Cyclops Fists, Cyclops Rock Throw, Cyclops Laser
Medusa Medusa Bite, Medusa Gaze
Cerberus Cerberus Bite
Zeus Zeus Lightning (his sword is a real, generated weapon)

Upgrading

WeaponUpgrader.upgrade(weapon) returns a new weapon one tier higher and leaves the original untouched.

  • A dagger becomes a sword of the next tier. Every other type stays the same type.
  • Upgrading a null weapon throws IllegalArgumentException.
  • Upgrading a tier 3 weapon throws IllegalStateException, with the weapon's type in the message.

WeaponUpgradeEffect is the Upgrade Stone's effect. It upgrades the weapon the player last held, and it upgrades the whole stack: 20 tier 1 daggers become 20 tier 2 swords. The stone is kept, not used up, when there is no weapon to upgrade, the weapon is already tier 3, or there is no room in the inventory for the upgraded weapons.

Who Uses a Weapon's Statistics

  • Enemies take only the damage and the windup from the weapon. Range, knockback and cooldown are the enemy's own config values.
  • The player attacks through WeaponAttackComponent, which swings whichever weapon is in the active inventory slot and reads the weapon's projectile count for a bow.
Enemy Generated weapon
Skeleton sword, tier 1
Ranged Skeleton bow, tier 1
Minotaur axe, tier 2
Centaur bow, tier 1
Zeus sword, tier 2
Harpy sword, tier 1
Ranged Harpy bow, tier 1

A generated weapon sits in the enemy's inventory and is dropped when the enemy dies.

Adding a Weapon Type

  1. Add a value to WeaponType.
  2. Add a WeaponStatsPreset for it to each tier in WeaponTier, and register it in the tier's constructor.
  3. Add a case for it to WeaponGenerator.generateWeapon, with its name, stack size, windup and sell price.
  4. Decide which attack components accept it, and update their constructor checks.
  5. Add its tier values to WeaponStatsPresetTest.

Weapon System: Testing Plan

Ten test classes in components/loot/ cover the weapon system, with 107 tests in total.

Test class Tests What it covers
WeaponItemTest 9 Building a weapon item
WeaponTierTest 9 Tier numbers and looking up statistics
WeaponStatsPresetTest 17 The statistics of every type at every tier
WeaponTierLootWeightTest 3 The loot weight of each tier
WeaponGeneratorTest 19 Generating each weapon type
WeaponGeneratorTierTest 3 Generated weapons carry the right tier
WeaponAxeTest 23 The axe, end to end
WeaponUpgraderTest 7 Upgrading a weapon
WeaponUpgradeEffectTest 13 The Upgrade Stone
WeaponInventoryIntegrationTest 4 Weapons in the inventory

WeaponAxeTest (23 tests)

The axe was added this sprint, so it has one class that follows it through every part of the system.

Generation and Statistics Tests

Tests were used to ensure the following situations were working:

  • An axe above the top tier, or at tier zero or below, is rejected
  • Each generated axe has the tier it was asked for
  • The tier 1 and tier 3 axe statistics are exposed through the item
  • The axe hits harder than the sword at every tier
  • The axe knocks back farther and reaches further than the sword at every tier

Upgrade Tests

Tests were used to ensure the following situations were working:

  • A tier 1 axe upgrades to a tier 2 axe, and an axe can be upgraded through every tier
  • Upgrading a tier 3 axe is rejected, and the message names the weapon
  • The upgraded axe has more damage and a higher sell price
  • The original axe is left untouched

Attack Component Tests

Tests were used to ensure the following situations were working:

  • MeleeAttackComponent accepts an axe and reports its damage
  • MeleeAttackComponent accepts an axe with its windup set to zero, as the Minotaur's is
  • MeleeAttackComponent rejects an axe whose windup is not below the cooldown
  • RangedAttackComponent rejects an axe

Player Swing Tests

Tests were used to ensure the following situations were working:

  • Swinging an axe announces an axe swing carrying the axe's damage, and does not throw
  • A tier 3 axe carries its tier 3 damage in the swing
  • An axe does not announce a sword swing
  • Swinging an axe does not spawn an arrow or a dagger
  • The axe in the active inventory slot is the one that is swung

Inventory Tests

Tests were used to ensure the following situations were working:

  • Two axes of the same tier stack, and a third starts a second stack
  • Axes of different tiers do not stack

The other test classes

Tests were used to ensure the following situations were working:

  • WeaponItemTest: a weapon item stores its type and damage, and rejects a null type and a damage of zero or below
  • WeaponTierTest: a tier is found by its number, a number outside 1 to 3 is rejected, and statistics are returned for each weapon type
  • WeaponStatsPresetTest: every statistic of every weapon type at every tier matches the tables on this page
  • WeaponTierLootWeightTest: each tier has its loot weight, and lower tiers are more likely
  • WeaponGeneratorTest and WeaponGeneratorTierTest: each type is generated with the right name, statistics and tier, and a null type or an invalid tier is rejected
  • WeaponUpgraderTest: a weapon upgrades by one tier, a dagger becomes a sword, and a null weapon or a tier 3 weapon is rejected
  • WeaponUpgradeEffectTest: the Upgrade Stone upgrades the held weapon and the whole stack, and is kept when there is nothing to upgrade
  • WeaponInventoryIntegrationTest: weapons are added to, stacked in and read back from the inventory

LootTableTest (13 tests) covers weapons being offered as loot by tier weight, with natural weapons left out.

Clone this wiki locally