8.0 KiB
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:
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:
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:
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:
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:
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:
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:
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:
# 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:
# 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:
# 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:
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:
if len(inventory.items) >= 26:
raise Impossible("Your inventory is full.")
Item Generation
Procedural item placement:
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 Actorgame/inventory.py: New inventory container systemgame/consumable.py: New consumable component systemgame/exceptions.py: New Impossible exceptiongame/actions.py: Added PickupAction, ItemAction, DropActiongame/input_handlers.py: Added InventoryEventHandler classesgame/engine.py: Added current_handler, inventory UI methodsgame/procgen.py: Added item generationgame/entity_factories.py: Added health_potion factorygame/ui.py: Updated help text with inventory controlsmain.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
- Container Architecture: Entity ownership through parent relationships
- Component Systems: Modular, reusable components with parent references
- Exception Handling: Clean error propagation and user feedback
- Modal UI: State-based input handling for different screens
- Item Systems: Flexible consumable architecture for varied effects
- Lifecycle Management: Proper entity creation, transfer, and cleanup
Running Part 8
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!