McRogueFace/roguelike_tutorial/PART_6_8_IMPLEMENTATION_PLA...

12 KiB

McRogueFace Tutorial Parts 6-8: Implementation Plan

Date: Monday, July 28, 2025
Target Delivery: Tuesday, July 29, 2025

Executive Summary

This document outlines the implementation plan for Parts 6-8 of the McRogueFace roguelike tutorial, adapting the libtcod Python tutorial to McRogueFace's architecture. The key discovery is that Python classes can successfully inherit from mcrfpy.Entity and store custom attributes, enabling a clean, Pythonic implementation.

Key Architectural Insights

Entity Inheritance Works!

class GameEntity(mcrfpy.Entity):
    def __init__(self, x, y, **kwargs):
        super().__init__(x=x, y=y, **kwargs)
        # Custom attributes work perfectly!
        self.hp = 10
        self.inventory = []
        self.any_attribute = "works"

This completely changes our approach from wrapper patterns to direct inheritance.


Part 6: Doing (and Taking) Some Damage

Overview

Implement a combat system with HP tracking, damage calculation, and death mechanics using entity inheritance.

Core Components

1. CombatEntity Base Class

class CombatEntity(mcrfpy.Entity):
    """Base class for entities that can fight and take damage"""
    def __init__(self, x, y, hp=10, defense=0, power=1, **kwargs):
        super().__init__(x=x, y=y, **kwargs)
        # Combat stats as direct attributes
        self.hp = hp
        self.max_hp = hp
        self.defense = defense
        self.power = power
        self.is_alive = True
        self.blocks_movement = True
        
    def calculate_damage(self, attacker):
        """Simple damage formula: power - defense"""
        return max(0, attacker.power - self.defense)
        
    def take_damage(self, damage, attacker=None):
        """Apply damage and handle death"""
        self.hp = max(0, self.hp - damage)
        
        if self.hp == 0 and self.is_alive:
            self.is_alive = False
            self.on_death(attacker)
            
    def on_death(self, killer=None):
        """Handle death - override in subclasses"""
        self.sprite_index = self.sprite_index + 180  # Corpse offset
        self.blocks_movement = False

2. Entity Types

class PlayerEntity(CombatEntity):
    """Player: HP=30, Defense=2, Power=5"""
    def __init__(self, x, y, **kwargs):
        kwargs['sprite_index'] = 64  # Hero sprite
        super().__init__(x=x, y=y, hp=30, defense=2, power=5, **kwargs)
        self.entity_type = "player"

class OrcEntity(CombatEntity):
    """Orc: HP=10, Defense=0, Power=3"""
    def __init__(self, x, y, **kwargs):
        kwargs['sprite_index'] = 65  # Orc sprite
        super().__init__(x=x, y=y, hp=10, defense=0, power=3, **kwargs)
        self.entity_type = "orc"

class TrollEntity(CombatEntity):
    """Troll: HP=16, Defense=1, Power=4"""
    def __init__(self, x, y, **kwargs):
        kwargs['sprite_index'] = 66  # Troll sprite
        super().__init__(x=x, y=y, hp=16, defense=1, power=4, **kwargs)
        self.entity_type = "troll"

3. Combat Integration

  • Extend on_bump() from Part 5 to include combat
  • Add attack animations (quick bump toward target)
  • Console messages initially, UI messages in Part 7
  • Death changes sprite and removes blocking

Key Differences from Original Tutorial

  • No Fighter component - stats are direct attributes
  • No AI component - behavior in entity methods
  • Integrated animations for visual feedback
  • Simpler architecture overall

Part 7: Creating the Interface

Overview

Add visual UI elements including health bars, message logs, and colored feedback for combat events.

Core Components

1. Health Bar

class HealthBar:
    """Health bar that reads entity HP directly"""
    def __init__(self, entity, pos=(10, 740), size=(200, 20)):
        self.entity = entity  # Direct reference!
        
        # Background (dark red)
        self.bg = mcrfpy.Frame(pos=pos, size=size)
        self.bg.fill_color = mcrfpy.Color(64, 16, 16)
        
        # Foreground (green)
        self.fg = mcrfpy.Frame(pos=pos, size=size)
        self.fg.fill_color = mcrfpy.Color(0, 96, 0)
        
        # Text overlay
        self.text = mcrfpy.Caption(
            pos=(pos[0] + 5, pos[1] + 2),
            text=f"HP: {entity.hp}/{entity.max_hp}"
        )
        
    def update(self):
        """Update based on entity's current HP"""
        ratio = self.entity.hp / self.entity.max_hp
        self.fg.w = int(self.bg.w * ratio)
        self.text.text = f"HP: {self.entity.hp}/{self.entity.max_hp}"
        
        # Color changes at low health
        if ratio < 0.25:
            self.fg.fill_color = mcrfpy.Color(196, 16, 16)  # Red
        elif ratio < 0.5:
            self.fg.fill_color = mcrfpy.Color(196, 196, 16)  # Yellow

2. Message Log

class MessageLog:
    """Scrolling message log for combat feedback"""
    def __init__(self, pos=(10, 600), size=(400, 120), max_messages=6):
        self.frame = mcrfpy.Frame(pos=pos, size=size)
        self.messages = []  # List of (text, color) tuples
        self.captions = []  # Pre-allocated Caption pool
        
    def add_message(self, text, color=None):
        """Add message with optional color"""
        # Handle duplicate detection (x2, x3, etc.)
        # Update caption display

3. Color System

class Colors:
    # Combat colors
    PLAYER_ATTACK = mcrfpy.Color(224, 224, 224)
    ENEMY_ATTACK = mcrfpy.Color(255, 192, 192)
    PLAYER_DEATH = mcrfpy.Color(255, 48, 48)
    ENEMY_DEATH = mcrfpy.Color(255, 160, 48)
    HEALTH_RECOVERED = mcrfpy.Color(0, 255, 0)

UI Layout

  • Health bar at bottom of screen
  • Message log above health bar
  • Direct binding to entity attributes
  • Real-time updates during gameplay

Part 8: Items and Inventory

Overview

Implement items as entities, inventory management, and a hotbar-style UI for item usage.

Core Components

1. Item Entities

class ItemEntity(mcrfpy.Entity):
    """Base class for pickupable items"""
    def __init__(self, x, y, name, sprite, **kwargs):
        kwargs['sprite_index'] = sprite
        super().__init__(x=x, y=y, **kwargs)
        self.item_name = name
        self.blocks_movement = False
        self.item_type = "generic"

class HealingPotion(ItemEntity):
    """Consumable healing item"""
    def __init__(self, x, y, healing_amount=4):
        super().__init__(x, y, "Healing Potion", sprite=33)
        self.healing_amount = healing_amount
        self.item_type = "consumable"
        
    def use(self, user):
        """Use the potion - returns (success, message)"""
        if hasattr(user, 'hp'):
            healed = min(self.healing_amount, user.max_hp - user.hp)
            if healed > 0:
                user.hp += healed
                return True, f"You heal {healed} HP!"

2. Inventory System

class InventoryMixin:
    """Mixin for entities with inventory"""
    def __init__(self, *args, capacity=10, **kwargs):
        super().__init__(*args, **kwargs)
        self.inventory = []
        self.inventory_capacity = capacity
        
    def pickup_item(self, item):
        """Pick up an item entity"""
        if len(self.inventory) >= self.inventory_capacity:
            return False, "Inventory full!"
        self.inventory.append(item)
        item.die()  # Remove from grid
        return True, f"Picked up {item.item_name}."

3. Inventory UI

class InventoryDisplay:
    """Hotbar-style inventory display"""
    def __init__(self, entity, pos=(200, 700), slots=10):
        # Create slot frames and sprites
        # Number keys 1-9, 0 for slots
        # Highlight selected slot
        # Update based on entity.inventory

Key Features

  • Items exist as entities on the grid
  • Direct inventory attribute on player
  • Hotkey-based usage (1-9, 0)
  • Visual hotbar display
  • Item effects (healing, future: damage boost, etc.)

Implementation Timeline

Tuesday Morning (Priority 1: Core Systems)

  1. 8:00-9:30: Implement CombatEntity and entity types
  2. 9:30-10:30: Add combat to bump interactions
  3. 10:30-11:30: Basic health display (text or simple bar)
  4. 11:30-12:00: ItemEntity and pickup system

Tuesday Afternoon (Priority 2: Integration)

  1. 1:00-2:00: Message log implementation
  2. 2:00-3:00: Full health bar with colors
  3. 3:00-4:00: Inventory UI (hotbar)
  4. 4:00-5:00: Testing and bug fixes

Tuesday Evening (Priority 3: Polish)

  1. 5:00-6:00: Combat animations and effects
  2. 6:00-7:00: Sound integration (use CoS splat sounds)
  3. 7:00-8:00: Additional item types
  4. 8:00-9:00: Documentation and cleanup

Testing Strategy

Automated Tests

# tests/test_part6_combat.py
- Test damage calculation
- Test death mechanics
- Test combat messages

# tests/test_part7_ui.py
- Test health bar updates
- Test message log scrolling
- Test color system

# tests/test_part8_inventory.py
- Test item pickup/drop
- Test inventory capacity
- Test item usage

Visual Tests

  • Screenshot combat states
  • Verify UI element positioning
  • Check animation smoothness

File Structure

roguelike_tutorial/
├── part_6.py          # Combat implementation
├── part_7.py          # UI enhancements
├── part_8.py          # Inventory system
├── combat.py          # Shared combat utilities
├── ui_components.py   # Reusable UI classes
├── colors.py          # Color definitions
└── items.py           # Item definitions

Risk Mitigation

Potential Issues

  1. Performance: Many UI updates per frame

    • Solution: Update only on state changes
  2. Entity Collection Bugs: Known segfault issues

    • Solution: Use index-based access when needed
  3. Animation Timing: Complex with turn-based combat

    • Solution: Queue animations, process sequentially

Fallback Options

  1. Start with console messages, add UI later
  2. Simple health numbers before bars
  3. Basic inventory list before hotbar

Success Criteria

Part 6

  • Entities can have HP and take damage
  • Death changes sprite and walkability
  • Combat messages appear
  • Player can kill enemies

Part 7

  • Health bar shows current/max HP
  • Messages appear in scrolling log
  • Colors differentiate message types
  • UI updates in real-time

Part 8

  • Items can be picked up
  • Inventory has capacity limit
  • Items can be used/consumed
  • Hotbar shows inventory items

Notes for Implementation

  1. Keep It Simple: Start with minimum viable features
  2. Build Incrementally: Test each component before integrating
  3. Use Part 5: Leverage existing entity interaction system
  4. Document Well: Clear comments for tutorial purposes
  5. Visual Feedback: McRogueFace excels at animations - use them!

Comparison with Original Tutorial

What We Keep

  • Same combat formula (power - defense)
  • Same entity stats (Player, Orc, Troll)
  • Same item types (healing potions to start)
  • Same UI elements (health bar, message log)

What's Different

  • Direct inheritance instead of components
  • Integrated animations and visual effects
  • Hotbar inventory instead of menu
  • Built-in sound support
  • Cleaner architecture overall

What's Better

  • More Pythonic with real inheritance
  • Better visual feedback
  • Smoother animations
  • Simpler to understand
  • Leverages McRogueFace's strengths

Conclusion

This implementation plan leverages McRogueFace's support for Python entity inheritance to create a clean, intuitive tutorial series. By using direct attributes instead of components, we simplify the architecture while maintaining all the functionality of the original tutorial. The addition of animations, sound effects, and rich UI elements showcases McRogueFace's capabilities while keeping the code beginner-friendly.

The Tuesday delivery timeline is aggressive but achievable by focusing on core functionality first, then integration, then polish. The modular design allows for easy testing and incremental development.