297 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Markdown
		
	
	
	
# Part 8: Items and Inventory
 | 
						|
 | 
						|
## Overview
 | 
						|
 | 
						|
Part 8 transforms our roguelike into a proper loot-driven game by adding items that can be collected, managed, and used. We implement a flexible inventory system with capacity limits, create consumable items like healing potions, and build UI for inventory management.
 | 
						|
 | 
						|
## What's New in Part 8
 | 
						|
 | 
						|
### Parent-Child Entity Architecture
 | 
						|
 | 
						|
#### Flexible Entity Ownership
 | 
						|
Entities now have parent containers, allowing them to exist in different contexts:
 | 
						|
```python
 | 
						|
class Entity(mcrfpy.Entity):
 | 
						|
    def __init__(self, parent: Optional[Union[GameMap, Inventory]] = None):
 | 
						|
        self.parent = parent
 | 
						|
    
 | 
						|
    @property
 | 
						|
    def gamemap(self) -> Optional[GameMap]:
 | 
						|
        """Get the GameMap through the parent chain"""
 | 
						|
        if isinstance(self.parent, Inventory):
 | 
						|
            return self.parent.gamemap
 | 
						|
        return self.parent
 | 
						|
```
 | 
						|
 | 
						|
Benefits:
 | 
						|
- Items can exist in the world or in inventories
 | 
						|
- Clean ownership transfer when picking up/dropping
 | 
						|
- Automatic visibility management
 | 
						|
 | 
						|
### Inventory System
 | 
						|
 | 
						|
#### Container-Based Design
 | 
						|
The inventory acts like a specialized entity container:
 | 
						|
```python
 | 
						|
class Inventory:
 | 
						|
    def __init__(self, capacity: int):
 | 
						|
        self.capacity = capacity
 | 
						|
        self.items: List[Item] = []
 | 
						|
        self.parent: Optional[Actor] = None
 | 
						|
    
 | 
						|
    def add_item(self, item: Item) -> None:
 | 
						|
        if len(self.items) >= self.capacity:
 | 
						|
            raise Impossible("Your inventory is full.")
 | 
						|
        
 | 
						|
        # Transfer ownership
 | 
						|
        self.items.append(item)
 | 
						|
        item.parent = self
 | 
						|
        item.visible = False  # Hide from map
 | 
						|
```
 | 
						|
 | 
						|
Features:
 | 
						|
- Capacity limits (26 items for letter selection)
 | 
						|
- Clean item transfer between world and inventory
 | 
						|
- Automatic visual management
 | 
						|
 | 
						|
### Item System
 | 
						|
 | 
						|
#### Item Entity Class
 | 
						|
Items are entities with consumable components:
 | 
						|
```python
 | 
						|
class Item(Entity):
 | 
						|
    def __init__(self, consumable: Optional = None):
 | 
						|
        super().__init__(blocks_movement=False)
 | 
						|
        self.consumable = consumable
 | 
						|
        if consumable:
 | 
						|
            consumable.parent = self
 | 
						|
```
 | 
						|
 | 
						|
#### Consumable Components
 | 
						|
Modular system for item effects:
 | 
						|
```python
 | 
						|
class HealingConsumable(Consumable):
 | 
						|
    def activate(self, action: ItemAction) -> None:
 | 
						|
        if consumer.hp >= consumer.max_hp:
 | 
						|
            raise Impossible("You are already at full health.")
 | 
						|
        
 | 
						|
        amount_recovered = min(self.amount, consumer.max_hp - consumer.hp)
 | 
						|
        consumer.hp += amount_recovered
 | 
						|
        self.consume()  # Remove item after use
 | 
						|
```
 | 
						|
 | 
						|
### Exception-Driven Feedback
 | 
						|
 | 
						|
#### Clean Error Handling
 | 
						|
Using exceptions for user feedback:
 | 
						|
```python
 | 
						|
class Impossible(Exception):
 | 
						|
    """Action cannot be performed"""
 | 
						|
    pass
 | 
						|
 | 
						|
class PickupAction(Action):
 | 
						|
    def perform(self, engine: Engine) -> None:
 | 
						|
        if not items_here:
 | 
						|
            raise Impossible("There is nothing here to pick up.")
 | 
						|
        
 | 
						|
        try:
 | 
						|
            inventory.add_item(item)
 | 
						|
            engine.message_log.add_message(f"You picked up the {item.name}!")
 | 
						|
        except Impossible as e:
 | 
						|
            engine.message_log.add_message(str(e))
 | 
						|
```
 | 
						|
 | 
						|
Benefits:
 | 
						|
- Consistent error messaging
 | 
						|
- Clean control flow
 | 
						|
- Centralized feedback handling
 | 
						|
 | 
						|
### Inventory UI
 | 
						|
 | 
						|
#### Modal Inventory Screen
 | 
						|
Interactive inventory management:
 | 
						|
```python
 | 
						|
class InventoryEventHandler(BaseEventHandler):
 | 
						|
    def create_ui(self) -> None:
 | 
						|
        # Semi-transparent background
 | 
						|
        self.background = mcrfpy.Frame(pos=(100, 100), size=(400, 400))
 | 
						|
        self.background.fill_color = mcrfpy.Color(0, 0, 0, 200)
 | 
						|
        
 | 
						|
        # List items with letter keys
 | 
						|
        for i, item in enumerate(inventory.items):
 | 
						|
            item_caption = mcrfpy.Caption(
 | 
						|
                pos=(20, 80 + i * 20),
 | 
						|
                text=f"{chr(ord('a') + i)}) {item.name}"
 | 
						|
            )
 | 
						|
```
 | 
						|
 | 
						|
Features:
 | 
						|
- Letter-based selection (a-z)
 | 
						|
- Separate handlers for use/drop
 | 
						|
- ESC to cancel
 | 
						|
- Visual feedback
 | 
						|
 | 
						|
### Enhanced Actions
 | 
						|
 | 
						|
#### Item Actions
 | 
						|
New actions for item management:
 | 
						|
```python
 | 
						|
class PickupAction(Action):
 | 
						|
    """Pick up items at current location"""
 | 
						|
 | 
						|
class ItemAction(Action):
 | 
						|
    """Base for item usage actions"""
 | 
						|
    
 | 
						|
class DropAction(ItemAction):
 | 
						|
    """Drop item from inventory"""
 | 
						|
```
 | 
						|
 | 
						|
Each action:
 | 
						|
- Self-validates
 | 
						|
- Provides feedback
 | 
						|
- Triggers enemy turns
 | 
						|
 | 
						|
## Architecture Improvements
 | 
						|
 | 
						|
### Component Relationships
 | 
						|
Parent-based component system:
 | 
						|
```python
 | 
						|
# Components know their parent
 | 
						|
consumable.parent = item
 | 
						|
item.parent = inventory
 | 
						|
inventory.parent = actor
 | 
						|
actor.parent = gamemap
 | 
						|
gamemap.engine = engine
 | 
						|
```
 | 
						|
 | 
						|
Benefits:
 | 
						|
- Access to game context from any component
 | 
						|
- Clean ownership transfer
 | 
						|
- Simplified entity lifecycle
 | 
						|
 | 
						|
### Input Handler States
 | 
						|
Modal UI through handler switching:
 | 
						|
```python
 | 
						|
# Main game
 | 
						|
engine.current_handler = MainGameEventHandler(engine)
 | 
						|
 | 
						|
# Open inventory
 | 
						|
engine.current_handler = InventoryActivateHandler(engine)
 | 
						|
 | 
						|
# Back to game
 | 
						|
engine.current_handler = MainGameEventHandler(engine)
 | 
						|
```
 | 
						|
 | 
						|
### Entity Lifecycle Management
 | 
						|
Proper creation and cleanup:
 | 
						|
```python
 | 
						|
# Item spawning
 | 
						|
item = entity_factories.health_potion(x, y, texture)
 | 
						|
item.place(x, y, dungeon)
 | 
						|
 | 
						|
# Pickup
 | 
						|
inventory.add_item(item)  # Removes from map
 | 
						|
 | 
						|
# Drop
 | 
						|
inventory.drop(item)  # Returns to map
 | 
						|
 | 
						|
# Death
 | 
						|
actor.die()  # Drops all items
 | 
						|
```
 | 
						|
 | 
						|
## Key Implementation Details
 | 
						|
 | 
						|
### Visibility Management
 | 
						|
Items hide/show based on container:
 | 
						|
```python
 | 
						|
def add_item(self, item):
 | 
						|
    item.visible = False  # Hide when in inventory
 | 
						|
 | 
						|
def drop(self, item):
 | 
						|
    item.visible = True  # Show when on map
 | 
						|
```
 | 
						|
 | 
						|
### Inventory Capacity
 | 
						|
Limited to alphabet keys:
 | 
						|
```python
 | 
						|
if len(inventory.items) >= 26:
 | 
						|
    raise Impossible("Your inventory is full.")
 | 
						|
```
 | 
						|
 | 
						|
### Item Generation
 | 
						|
Procedural item placement:
 | 
						|
```python
 | 
						|
def place_entities(room, dungeon, max_monsters, max_items, texture):
 | 
						|
    # Place 0-2 items per room
 | 
						|
    number_of_items = random.randint(0, max_items)
 | 
						|
    
 | 
						|
    for _ in range(number_of_items):
 | 
						|
        if space_available:
 | 
						|
            item = entity_factories.health_potion(x, y, texture)
 | 
						|
            item.place(x, y, dungeon)
 | 
						|
```
 | 
						|
 | 
						|
## Files Modified
 | 
						|
 | 
						|
- `game/entity.py`: Added parent system, Item class, inventory to Actor
 | 
						|
- `game/inventory.py`: New inventory container system
 | 
						|
- `game/consumable.py`: New consumable component system
 | 
						|
- `game/exceptions.py`: New Impossible exception
 | 
						|
- `game/actions.py`: Added PickupAction, ItemAction, DropAction
 | 
						|
- `game/input_handlers.py`: Added InventoryEventHandler classes
 | 
						|
- `game/engine.py`: Added current_handler, inventory UI methods
 | 
						|
- `game/procgen.py`: Added item generation
 | 
						|
- `game/entity_factories.py`: Added health_potion factory
 | 
						|
- `game/ui.py`: Updated help text with inventory controls
 | 
						|
- `main.py`: Updated to Part 8, handler management
 | 
						|
 | 
						|
## What's Next
 | 
						|
 | 
						|
Part 9 will add ranged attacks and targeting:
 | 
						|
- Targeting UI for selecting enemies
 | 
						|
- Ranged damage items (lightning staff)
 | 
						|
- Area-of-effect items (fireball staff)
 | 
						|
- Confusion effects
 | 
						|
 | 
						|
## Learning Points
 | 
						|
 | 
						|
1. **Container Architecture**: Entity ownership through parent relationships
 | 
						|
2. **Component Systems**: Modular, reusable components with parent references
 | 
						|
3. **Exception Handling**: Clean error propagation and user feedback
 | 
						|
4. **Modal UI**: State-based input handling for different screens
 | 
						|
5. **Item Systems**: Flexible consumable architecture for varied effects
 | 
						|
6. **Lifecycle Management**: Proper entity creation, transfer, and cleanup
 | 
						|
 | 
						|
## Running Part 8
 | 
						|
 | 
						|
```bash
 | 
						|
cd simple_tcod_tutorial/build
 | 
						|
./mcrogueface scripts/main.py
 | 
						|
```
 | 
						|
 | 
						|
New features to try:
 | 
						|
- Press G to pick up healing potions
 | 
						|
- Press I to open inventory and use items
 | 
						|
- Press O to drop items from inventory
 | 
						|
- Heal yourself when injured in combat
 | 
						|
- Manage limited inventory space (26 slots)
 | 
						|
- Items drop from dead enemies
 | 
						|
 | 
						|
## Design Principles
 | 
						|
 | 
						|
### Flexibility Through Composition
 | 
						|
- Items gain behavior through consumable components
 | 
						|
- Easy to add new item types
 | 
						|
- Reusable effect system
 | 
						|
 | 
						|
### Clean Ownership Transfer
 | 
						|
- Entities always have clear parent
 | 
						|
- Automatic visibility management
 | 
						|
- No orphaned entities
 | 
						|
 | 
						|
### User-Friendly Feedback
 | 
						|
- Clear error messages
 | 
						|
- Consistent UI patterns
 | 
						|
- Intuitive controls
 | 
						|
 | 
						|
The inventory system provides the foundation for equipment, spells, and complex item interactions in future parts! |