feat(api): implement inventory service with equipment system
Add InventoryService for managing character inventory, equipment, and consumable usage. Key features: - Add/remove items with inventory capacity checks - Equipment slot validation (weapon, off_hand, helmet, chest, gloves, boots, accessory_1, accessory_2) - Level and class requirement validation for equipment - Consumable usage with instant and duration-based effects - Combat-specific consumable method returning effects for combat system - Bulk operations (add_items, get_items_by_type, get_equippable_items) Design decision: Uses full Item object storage (not IDs) to support procedurally generated items with unique identifiers. Files added: - /api/app/services/inventory_service.py (560 lines) - /api/tests/test_inventory_service.py (51 tests passing) Task 2.3 of Phase 4 Combat Implementation complete.
This commit is contained in:
@@ -708,6 +708,149 @@ success = service.soft_delete_message(
|
||||
- **Consumable:** One-time use (potions, scrolls)
|
||||
- **Quest Item:** Story-related, non-tradeable
|
||||
|
||||
---
|
||||
|
||||
## Procedural Item Generation (Affix System)
|
||||
|
||||
The game uses a Diablo-style procedural item generation system where weapons and armor
|
||||
are created by combining base templates with random affixes.
|
||||
|
||||
### Core Models
|
||||
|
||||
#### Affix
|
||||
|
||||
Represents a prefix or suffix that modifies an item's stats and name.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `affix_id` | str | Unique identifier |
|
||||
| `name` | str | Display name ("Flaming", "of Strength") |
|
||||
| `affix_type` | AffixType | PREFIX or SUFFIX |
|
||||
| `tier` | AffixTier | MINOR, MAJOR, or LEGENDARY |
|
||||
| `description` | str | Affix description |
|
||||
| `stat_bonuses` | Dict[str, int] | Stat modifications |
|
||||
| `damage_bonus` | int | Flat damage increase |
|
||||
| `defense_bonus` | int | Flat defense increase |
|
||||
| `resistance_bonus` | int | Flat resistance increase |
|
||||
| `damage_type` | DamageType | For elemental affixes |
|
||||
| `elemental_ratio` | float | Portion of damage converted to element |
|
||||
| `crit_chance_bonus` | float | Critical hit chance modifier |
|
||||
| `crit_multiplier_bonus` | float | Critical damage modifier |
|
||||
| `allowed_item_types` | List[str] | Item types this affix can apply to |
|
||||
| `required_rarity` | str | Minimum rarity required (for legendary affixes) |
|
||||
|
||||
**Methods:**
|
||||
- `applies_elemental_damage() -> bool` - Check if affix adds elemental damage
|
||||
- `is_legendary_only() -> bool` - Check if requires legendary rarity
|
||||
- `can_apply_to(item_type, rarity) -> bool` - Check if affix can be applied
|
||||
|
||||
#### BaseItemTemplate
|
||||
|
||||
Foundation template for procedural item generation.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `template_id` | str | Unique identifier |
|
||||
| `name` | str | Base item name ("Dagger") |
|
||||
| `item_type` | str | "weapon" or "armor" |
|
||||
| `description` | str | Template description |
|
||||
| `base_damage` | int | Starting damage value |
|
||||
| `base_defense` | int | Starting defense value |
|
||||
| `base_resistance` | int | Starting resistance value |
|
||||
| `base_value` | int | Base gold value |
|
||||
| `damage_type` | str | Physical, fire, etc. |
|
||||
| `crit_chance` | float | Base critical chance |
|
||||
| `crit_multiplier` | float | Base critical multiplier |
|
||||
| `required_level` | int | Minimum level to use |
|
||||
| `min_rarity` | str | Minimum rarity this generates as |
|
||||
| `drop_weight` | int | Relative drop probability |
|
||||
|
||||
**Methods:**
|
||||
- `can_generate_at_rarity(rarity) -> bool` - Check if template supports rarity
|
||||
- `can_drop_for_level(level) -> bool` - Check level requirement
|
||||
|
||||
### Item Model Updates for Generated Items
|
||||
|
||||
The `Item` dataclass includes fields for tracking generated items:
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `applied_affixes` | List[str] | IDs of affixes on this item |
|
||||
| `base_template_id` | str | ID of base template used |
|
||||
| `generated_name` | str | Full name with affixes (e.g., "Flaming Dagger of Strength") |
|
||||
| `is_generated` | bool | True if procedurally generated |
|
||||
|
||||
**Methods:**
|
||||
- `get_display_name() -> str` - Returns generated_name if available, otherwise base name
|
||||
|
||||
### Generation Enumerations
|
||||
|
||||
#### ItemRarity
|
||||
|
||||
Item quality tiers affecting affix count and value:
|
||||
|
||||
| Value | Affix Count | Value Multiplier |
|
||||
|-------|-------------|------------------|
|
||||
| `COMMON` | 0 | 1.0× |
|
||||
| `UNCOMMON` | 0 | 1.5× |
|
||||
| `RARE` | 1 | 2.5× |
|
||||
| `EPIC` | 2 | 5.0× |
|
||||
| `LEGENDARY` | 3 | 10.0× |
|
||||
|
||||
#### AffixType
|
||||
|
||||
| Value | Description |
|
||||
|-------|-------------|
|
||||
| `PREFIX` | Appears before item name ("Flaming Dagger") |
|
||||
| `SUFFIX` | Appears after item name ("Dagger of Strength") |
|
||||
|
||||
#### AffixTier
|
||||
|
||||
Affix power level, determines eligibility by item rarity:
|
||||
|
||||
| Value | Description | Available For |
|
||||
|-------|-------------|---------------|
|
||||
| `MINOR` | Basic affixes | RARE+ |
|
||||
| `MAJOR` | Stronger affixes | RARE+ (higher weight at EPIC+) |
|
||||
| `LEGENDARY` | Most powerful affixes | LEGENDARY only |
|
||||
|
||||
### Item Generation Service
|
||||
|
||||
**Location:** `/app/services/item_generator.py`
|
||||
|
||||
**Usage:**
|
||||
```python
|
||||
from app.services.item_generator import get_item_generator
|
||||
from app.models.enums import ItemRarity
|
||||
|
||||
generator = get_item_generator()
|
||||
|
||||
# Generate specific item
|
||||
item = generator.generate_item(
|
||||
item_type="weapon",
|
||||
rarity=ItemRarity.EPIC,
|
||||
character_level=5
|
||||
)
|
||||
|
||||
# Generate random loot drop with luck influence
|
||||
item = generator.generate_loot_drop(
|
||||
character_level=10,
|
||||
luck_stat=12
|
||||
)
|
||||
```
|
||||
|
||||
**Related Loaders:**
|
||||
- `AffixLoader` (`/app/services/affix_loader.py`) - Loads affix definitions from YAML
|
||||
- `BaseItemLoader` (`/app/services/base_item_loader.py`) - Loads base templates from YAML
|
||||
|
||||
**Data Files:**
|
||||
- `/app/data/affixes/prefixes.yaml` - Prefix definitions
|
||||
- `/app/data/affixes/suffixes.yaml` - Suffix definitions
|
||||
- `/app/data/base_items/weapons.yaml` - Weapon templates
|
||||
- `/app/data/base_items/armor.yaml` - Armor templates
|
||||
|
||||
---
|
||||
|
||||
### Ability
|
||||
|
||||
Abilities represent actions that can be taken in combat (attacks, spells, skills, item uses).
|
||||
|
||||
@@ -402,6 +402,111 @@ effects_applied:
|
||||
|
||||
---
|
||||
|
||||
## Procedural Item Generation
|
||||
|
||||
### Overview
|
||||
|
||||
Weapons and armor are procedurally generated using a Diablo-style affix system.
|
||||
Items are created by combining:
|
||||
1. **Base Template** - Defines item type, base stats, level requirement
|
||||
2. **Affixes** - Prefixes and suffixes that add stats and modify the name
|
||||
|
||||
### Generation Process
|
||||
|
||||
1. Select base template (filtered by level, rarity)
|
||||
2. Determine affix count based on rarity (0-3)
|
||||
3. Roll affix tier based on rarity weights
|
||||
4. Select random affixes avoiding duplicates
|
||||
5. Combine stats and generate name
|
||||
|
||||
### Rarity System
|
||||
|
||||
| Rarity | Affixes | Value Multiplier | Color |
|
||||
|--------|---------|------------------|-------|
|
||||
| COMMON | 0 | 1.0× | Gray |
|
||||
| UNCOMMON | 0 | 1.5× | Green |
|
||||
| RARE | 1 | 2.5× | Blue |
|
||||
| EPIC | 2 | 5.0× | Purple |
|
||||
| LEGENDARY | 3 | 10.0× | Orange |
|
||||
|
||||
### Affix Distribution
|
||||
|
||||
| Rarity | Affix Count | Distribution |
|
||||
|--------|-------------|--------------|
|
||||
| RARE | 1 | 50% prefix OR 50% suffix |
|
||||
| EPIC | 2 | 1 prefix AND 1 suffix |
|
||||
| LEGENDARY | 3 | Mix (2+1 or 1+2) |
|
||||
|
||||
### Affix Tiers
|
||||
|
||||
Higher rarity items have better chances at higher tier affixes:
|
||||
|
||||
| Rarity | MINOR | MAJOR | LEGENDARY |
|
||||
|--------|-------|-------|-----------|
|
||||
| RARE | 80% | 20% | 0% |
|
||||
| EPIC | 30% | 70% | 0% |
|
||||
| LEGENDARY | 10% | 40% | 50% |
|
||||
|
||||
### Name Generation Examples
|
||||
|
||||
- **COMMON:** "Dagger"
|
||||
- **RARE (prefix):** "Flaming Dagger"
|
||||
- **RARE (suffix):** "Dagger of Strength"
|
||||
- **EPIC:** "Flaming Dagger of Strength"
|
||||
- **LEGENDARY:** "Blazing Glacial Dagger of the Titan"
|
||||
|
||||
### Luck Influence
|
||||
|
||||
Player's LUK stat affects rarity rolls for loot drops:
|
||||
|
||||
**Base chances at LUK 8:**
|
||||
- COMMON: 50%
|
||||
- UNCOMMON: 30%
|
||||
- RARE: 15%
|
||||
- EPIC: 4%
|
||||
- LEGENDARY: 1%
|
||||
|
||||
**Luck Bonus:**
|
||||
Each point of LUK above 8 adds +0.5% to higher rarity chances.
|
||||
|
||||
**Examples:**
|
||||
- LUK 8 (baseline): 1% legendary chance
|
||||
- LUK 12: ~3% legendary chance
|
||||
- LUK 16: ~5% legendary chance
|
||||
|
||||
### Service Usage
|
||||
|
||||
```python
|
||||
from app.services.item_generator import get_item_generator
|
||||
from app.models.enums import ItemRarity
|
||||
|
||||
generator = get_item_generator()
|
||||
|
||||
# Generate item of specific rarity
|
||||
sword = generator.generate_item(
|
||||
item_type="weapon",
|
||||
rarity=ItemRarity.EPIC,
|
||||
character_level=5
|
||||
)
|
||||
|
||||
# Generate random loot with luck bonus
|
||||
loot = generator.generate_loot_drop(
|
||||
character_level=10,
|
||||
luck_stat=15
|
||||
)
|
||||
```
|
||||
|
||||
### Data Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `/app/data/base_items/weapons.yaml` | 13 weapon templates |
|
||||
| `/app/data/base_items/armor.yaml` | 12 armor templates |
|
||||
| `/app/data/affixes/prefixes.yaml` | 18 prefix affixes |
|
||||
| `/app/data/affixes/suffixes.yaml` | 11 suffix affixes |
|
||||
|
||||
---
|
||||
|
||||
## Quest System (Future)
|
||||
|
||||
### Quest Types
|
||||
|
||||
Reference in New Issue
Block a user