feat(equipment): remove the requiredLevel equipment gate - owned items are always equippable

This commit is contained in:
Bastian Wagner
2026-08-21 13:34:10 +02:00
parent 83eef5d9e5
commit 413fafd574
3 changed files with 8 additions and 24 deletions

View File

@@ -4,7 +4,6 @@ export type EquipmentErrorCode =
| 'CHARACTER_ITEM_NOT_FOUND'
| 'ITEM_NOT_OWNED'
| 'ITEM_NOT_EQUIPPABLE'
| 'ITEM_LEVEL_REQUIREMENT_NOT_MET'
| 'INVALID_EQUIPMENT_SLOT'
| 'CHARACTER_IN_COMBAT';
@@ -42,14 +41,6 @@ export function itemNotEquippable(): EquipmentDomainError {
);
}
export function itemLevelRequirementNotMet(): EquipmentDomainError {
return new EquipmentDomainError(
'ITEM_LEVEL_REQUIREMENT_NOT_MET',
HttpStatus.BAD_REQUEST,
"The character does not meet this item's level requirement.",
);
}
// Defensive: slot is always derived from the item definition server-side, so
// this is unreachable in practice (spec §27 still names it explicitly).
export function invalidEquipmentSlot(): EquipmentDomainError {

View File

@@ -143,7 +143,6 @@ function itemDefinition(overrides: Partial<ItemDefinition> = {}): ItemDefinition
equipmentSlot: EquipmentSlot.WEAPON,
rarity: ItemRarity.COMMON,
tier: 1,
requiredLevel: 1,
weaponDamage: 8,
bonusHp: 0,
bonusAttack: 0,
@@ -160,7 +159,6 @@ function character(overrides: Partial<Character> = {}): Character {
return {
id: CHARACTER_ID,
name: 'Aric Duskwalker',
level: 1,
baseHp: 100,
baseAttack: 6,
currentHp: 100,
@@ -300,29 +298,28 @@ describe('EquipmentService', () => {
);
});
it('rejects equipping an item above the character level', async () => {
const highLevelHelm = itemDefinition({
it('equips an owned item regardless of the item content that used to carry a level requirement', async () => {
const highTierHelm = itemDefinition({
id: BANDIT_HOOD_ITEM_ID,
key: 'bandit-hood',
equipmentSlot: EquipmentSlot.HEAD,
requiredLevel: 5,
tier: 5,
});
const { service } = createHarness({
itemDefinitions: [highLevelHelm],
itemDefinitions: [highTierHelm],
characterItems: [
{
id: BANDIT_HOOD_ITEM_ID,
characterId: CHARACTER_ID,
itemDefinitionId: highLevelHelm.id,
itemDefinitionId: highTierHelm.id,
quantity: 1,
} as CharacterItem,
],
});
await expectEquipmentDomainError(
service.equip(CHARACTER_ID, BANDIT_HOOD_ITEM_ID),
'ITEM_LEVEL_REQUIREMENT_NOT_MET',
);
const result = await service.equip(CHARACTER_ID, BANDIT_HOOD_ITEM_ID);
expect(result.slots.HEAD?.characterItemId).toBe(BANDIT_HOOD_ITEM_ID);
});
it('rejects equipping a non-equippable item', async () => {

View File

@@ -12,7 +12,6 @@ import {
characterInCombat,
characterItemNotFound,
characterNotFound,
itemLevelRequirementNotMet,
itemNotEquippable,
itemNotOwned,
} from './equipment.errors';
@@ -102,9 +101,6 @@ export class EquipmentService {
if (!definition.equipmentSlot) {
throw itemNotEquippable();
}
if (definition.requiredLevel > character.level) {
throw itemLevelRequirementNotMet();
}
const existing = await equipmentRepo.findOne({
where: { characterId, slot: definition.equipmentSlot },