29 KiB
Phase 4: Combat & Progression Systems - Implementation Plan
Status: In Progress - Week 2 Complete, Week 3 Next Timeline: 4-5 weeks Last Updated: November 27, 2025 Document Version: 1.3
Completion Summary
Week 1: Combat Backend - COMPLETE
| Task | Description | Status | Tests |
|---|---|---|---|
| 1.1 | Verify Combat Data Models | ✅ Complete | - |
| 1.2 | Implement Combat Service | ✅ Complete | 25 tests |
| 1.3 | Implement Damage Calculator | ✅ Complete | 39 tests |
| 1.4 | Implement Effect Processor | ✅ Complete | - |
| 1.5 | Implement Combat Actions | ✅ Complete | - |
| 1.6 | Combat API Endpoints | ✅ Complete | 19 tests |
| 1.7 | Manual API Testing | ⏭️ Skipped | - |
Files Created:
/api/app/models/enemy.py- EnemyTemplate, LootEntry dataclasses/api/app/services/enemy_loader.py- YAML-based enemy loading/api/app/services/combat_service.py- Combat orchestration service/api/app/services/damage_calculator.py- Damage formula calculations/api/app/api/combat.py- REST API endpoints/api/app/data/enemies/*.yaml- 6 sample enemy definitions/api/tests/test_damage_calculator.py- 39 tests/api/tests/test_enemy_loader.py- 25 tests/api/tests/test_combat_service.py- 25 tests/api/tests/test_combat_api.py- 19 tests
Total Tests: 108 passing
Week 2: Inventory & Equipment - COMPLETE
| Task | Description | Status | Tests |
|---|---|---|---|
| 2.1 | Item Data Models (Affixes) | ✅ Complete | 24 tests |
| 2.2 | Item Data Files (YAML) | ✅ Complete | - |
| 2.2.1 | Item Generator Service | ✅ Complete | 35 tests |
| 2.3 | Inventory Service | ✅ Complete | 24 tests |
| 2.4 | Inventory API Endpoints | ✅ Complete | 25 tests |
| 2.5 | Character Stats Calculation | ✅ Complete | 17 tests |
| 2.6 | Equipment-Combat Integration | ✅ Complete | 140 tests |
| 2.7 | Combat Loot Integration | ✅ Complete | 59 tests |
Files Created/Modified:
/api/app/models/items.py- Item with affix support, spell_power field/api/app/models/affixes.py- Affix, BaseItemTemplate dataclasses/api/app/models/stats.py- spell_power_bonus, updated damage formula/api/app/models/combat.py- Combatant weapon properties/api/app/services/item_generator.py- Procedural item generation/api/app/services/inventory_service.py- Equipment management/api/app/services/damage_calculator.py- Refactored to use stats properties/api/app/services/combat_service.py- Equipment integration/api/app/api/inventory.py- REST API endpoints
Total Tests (Week 2): 324+ passing
Overview
This phase implements the core combat and progression systems for Code of Conquest, enabling turn-based tactical combat, inventory management, equipment, skill trees, and the NPC shop. This is a prerequisite for the story progression and quest systems.
Key Deliverables:
- Turn-based combat system (API + UI)
- Inventory & equipment management
- Skill tree visualization and unlocking
- XP and leveling system
- NPC shop
Phase Structure
| Sub-Phase | Duration | Focus |
|---|---|---|
| Phase 4A | 2-3 weeks | Combat Foundation |
| Phase 4B | 1-2 weeks | Skill Trees & Leveling |
| Phase 4C | 3-4 days | NPC Shop |
Total Estimated Time: 4-5 weeks (~140-175 hours)
Phase 4A: Combat Foundation (Weeks 1-3)
Week 1: Combat Backend & Data Models ✅ COMPLETE
Task 1.1: Verify Combat Data Models ✅ COMPLETE
Files: /api/app/models/combat.py, effects.py, abilities.py, stats.py
Verified: Combatant, CombatEncounter dataclasses, effect types (BUFF, DEBUFF, DOT, HOT, STUN, SHIELD), stacking logic, YAML ability loading, serialization methods.
Task 1.2: Implement Combat Service ✅ COMPLETE
File: /api/app/services/combat_service.py
Implemented: CombatService class with initiate_combat(), process_action(), initiative rolling, turn management, death checking, combat end detection.
Task 1.3: Implement Damage Calculator ✅ COMPLETE
File: /api/app/services/damage_calculator.py
Implemented: calculate_physical_damage(), calculate_magical_damage(), apply_damage() with shield absorption. Physical formula: weapon.damage + (STR/2) - defense. 39 unit tests.
Task 1.4: Implement Effect Processor ✅ COMPLETE
File: /api/app/models/effects.py
Implemented: tick() method for DOT/HOT damage/healing, duration tracking, stat modifiers via get_effective_stats().
Task 1.5: Implement Combat Actions ✅ COMPLETE
File: /api/app/services/combat_service.py
Implemented: _execute_attack(), _execute_spell(), _execute_item(), _execute_defend() with mana costs, cooldowns, effect application.
Task 1.6: Combat API Endpoints ✅ COMPLETE
File: /api/app/api/combat.py
Endpoints:
POST /api/v1/combat/start- Initiate combatPOST /api/v1/combat/<combat_id>/action- Take actionGET /api/v1/combat/<combat_id>/state- Get statePOST /api/v1/combat/<combat_id>/flee- Attempt fleePOST /api/v1/combat/<combat_id>/enemy-turn- Enemy AIGET /api/v1/combat/enemies- List templates (public)GET /api/v1/combat/enemies/<id>- Enemy details (public)
19 integration tests passing.
Task 1.7: Manual API Testing ⏭️ SKIPPED
Covered by 108 comprehensive automated tests.
Week 2: Inventory & Equipment System ✅ COMPLETE
Task 2.1: Item Data Models ✅ COMPLETE
Files: /api/app/models/items.py, affixes.py, enums.py
Implemented: Item dataclass with affix support (applied_affixes, base_template_id, generated_name, is_generated), Affix model (PREFIX/SUFFIX types, MINOR/MAJOR/LEGENDARY tiers), BaseItemTemplate for procedural generation. 24 tests.
Task 2.2: Item Data Files ✅ COMPLETE
Directory: /api/app/data/
Created:
base_items/weapons.yaml- 13 weapon templatesbase_items/armor.yaml- 12 armor templates (cloth/leather/chain/plate)affixes/prefixes.yaml- 18 prefixes (elemental, material, quality, legendary)affixes/suffixes.yaml- 11 suffixes (stat bonuses, animal totems, legendary)items/consumables/potions.yaml- Health/mana potions (small/medium/large)
Task 2.2.1: Item Generator Service ✅ COMPLETE
Files: /api/app/services/item_generator.py, affix_loader.py, base_item_loader.py
Implemented Diablo-style procedural generation:
- Affix distribution: COMMON/UNCOMMON (0), RARE (1), EPIC (2), LEGENDARY (3)
- Name generation: "Flaming Dagger of Strength"
- Tier weights by rarity (RARE: 80% MINOR, EPIC: 70% MAJOR, LEGENDARY: 50% LEGENDARY)
- Luck-influenced rarity rolling
35 tests.
Task 2.3: Implement Inventory Service ✅ COMPLETE
File: /api/app/services/inventory_service.py
Implemented: add_item(), remove_item(), equip_item(), unequip_item(), use_consumable(), use_consumable_in_combat(). Full object storage for generated items. Validation for slots, levels, item types. 24 tests.
Task 2.4: Inventory API Endpoints ✅ COMPLETE
File: /api/app/api/inventory.py
Endpoints:
GET /api/v1/characters/<id>/inventory- Get inventory + equippedPOST /api/v1/characters/<id>/inventory/equip- Equip itemPOST /api/v1/characters/<id>/inventory/unequip- Unequip itemPOST /api/v1/characters/<id>/inventory/use- Use consumableDELETE /api/v1/characters/<id>/inventory/<item_id>- Drop item
25 tests.
Task 2.5: Update Character Stats Calculation ✅ COMPLETE
Files: /api/app/models/stats.py, character.py
Added damage_bonus, defense_bonus, resistance_bonus fields to Stats. Updated get_effective_stats() to populate from equipped weapon/armor. 17 tests.
Task 2.6: Equipment-Combat Integration ✅ COMPLETE
Files: stats.py, items.py, character.py, combat.py, combat_service.py, damage_calculator.py
Key changes:
- Damage scaling:
int(STR * 0.75) + damage_bonus(wasSTR // 2) - Added
spell_powersystem for magical weapons - Combatant weapon properties (crit_chance, crit_multiplier, elemental support)
- DamageCalculator uses
stats.damagedirectly (removedweapon_damageparam)
140 tests.
Task 2.7: Combat Loot Integration ✅ COMPLETE
Files: combat_loot_service.py, static_item_loader.py, app/models/enemy.py
Implemented hybrid loot system:
- Static drops (consumables, materials) via
StaticItemLoader - Procedural drops (equipment) via
ItemGenerator - Difficulty bonuses: EASY +0%, MEDIUM +5%, HARD +15%, BOSS +30%
- Enemy variants: goblin_scout, goblin_warrior, goblin_chieftain
59 tests.
Week 3: Combat UI
Task 3.1: Create Combat Template (1 day / 8 hours)
Objective: Build HTMX-powered combat interface
File: /public_web/templates/game/combat.html
Layout:
┌─────────────────────────────────────────────────────────────┐
│ COMBAT ENCOUNTER │
├───────────────┬─────────────────────────┬───────────────────┤
│ │ │ │
│ YOUR │ COMBAT LOG │ TURN ORDER │
│ CHARACTER │ │ ─────────── │
│ ───────── │ Goblin attacks you │ 1. Aragorn ✓ │
│ HP: ████ 80 │ for 12 damage! │ 2. Goblin │
│ MP: ███ 60 │ │ 3. Orc │
│ │ You attack Goblin │ │
│ ENEMY │ for 18 damage! │ ACTIVE EFFECTS │
│ ───────── │ CRITICAL HIT! │ ─────────── │
│ Goblin │ │ 🛡️ Defending │
│ HP: ██ 12 │ Goblin is stunned! │ (1 turn) │
│ │ │ │
│ │ ───────────────── │ │
│ │ ACTION BUTTONS │ │
│ │ ───────────────── │ │
│ │ [Attack] [Spell] │ │
│ │ [Item] [Defend] │ │
│ │ │ │
└───────────────┴─────────────────────────┴───────────────────┘
Implementation:
{% extends "base.html" %}
{% block title %}Combat - Code of Conquest{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/combat.css') }}">
{% endblock %}
{% block content %}
<div class="combat-container">
<h1 class="combat-title">⚔️ COMBAT ENCOUNTER</h1>
<div class="combat-grid">
{# Left Panel - Combatants #}
<aside class="combat-panel combat-combatants">
<div class="combatant-card player-card">
<h3>{{ character.name }}</h3>
<div class="hp-bar">
<div class="hp-fill" style="width: {{ (character.current_hp / character.stats.max_hp * 100)|int }}%"></div>
<span class="hp-text">HP: {{ character.current_hp }} / {{ character.stats.max_hp }}</span>
</div>
<div class="mp-bar">
<div class="mp-fill" style="width: {{ (character.current_mp / character.stats.max_mp * 100)|int }}%"></div>
<span class="mp-text">MP: {{ character.current_mp }} / {{ character.stats.max_mp }}</span>
</div>
</div>
<div class="vs-divider">VS</div>
{% for enemy in enemies %}
<div class="combatant-card enemy-card" id="enemy-{{ loop.index0 }}">
<h3>{{ enemy.name }}</h3>
<div class="hp-bar">
<div class="hp-fill enemy" style="width: {{ (enemy.current_hp / enemy.stats.max_hp * 100)|int }}%"></div>
<span class="hp-text">HP: {{ enemy.current_hp }} / {{ enemy.stats.max_hp }}</span>
</div>
{% if enemy.current_hp > 0 %}
<button class="btn btn-target" onclick="selectTarget('{{ enemy.combatant_id }}')">
Target
</button>
{% else %}
<span class="defeated-badge">DEFEATED</span>
{% endif %}
</div>
{% endfor %}
</aside>
{# Middle Panel - Combat Log & Actions #}
<section class="combat-panel combat-main">
<div class="combat-log" id="combat-log">
<h3>Combat Log</h3>
<div class="log-entries">
{% for entry in combat_log[-10:] %}
<div class="log-entry">{{ entry }}</div>
{% endfor %}
</div>
</div>
<div class="combat-actions" id="combat-actions">
<h3>Your Turn</h3>
<div class="action-buttons">
<button class="btn btn-action btn-attack"
hx-post="/combat/{{ combat_id }}/action"
hx-vals='{"action_type": "attack", "ability_id": "basic_attack", "target_id": ""}'
hx-target="#combat-container"
hx-swap="outerHTML">
⚔️ Attack
</button>
<button class="btn btn-action btn-spell"
onclick="openSpellMenu()">
✨ Cast Spell
</button>
<button class="btn btn-action btn-item"
onclick="openItemMenu()">
🎒 Use Item
</button>
<button class="btn btn-action btn-defend"
hx-post="/combat/{{ combat_id }}/action"
hx-vals='{"action_type": "defend"}'
hx-target="#combat-container"
hx-swap="outerHTML">
🛡️ Defend
</button>
</div>
</div>
</section>
{# Right Panel - Turn Order & Effects #}
<aside class="combat-panel combat-sidebar">
<div class="turn-order">
<h3>Turn Order</h3>
<ol>
{% for combatant_id in turn_order %}
<li class="{% if loop.index0 == current_turn_index %}active-turn{% endif %}">
{{ get_combatant_name(combatant_id) }}
{% if loop.index0 == current_turn_index %}✓{% endif %}
</li>
{% endfor %}
</ol>
</div>
<div class="active-effects">
<h3>Active Effects</h3>
{% for effect in character.active_effects %}
<div class="effect-badge {{ effect.effect_type }}">
{{ effect.name }} ({{ effect.duration }})
</div>
{% endfor %}
</div>
</aside>
</div>
</div>
{# Modal Container #}
<div id="modal-container"></div>
{% endblock %}
{% block scripts %}
<script>
let selectedTargetId = null;
function selectTarget(targetId) {
selectedTargetId = targetId;
// Update UI to show selected target
document.querySelectorAll('.btn-target').forEach(btn => {
btn.classList.remove('selected');
});
event.target.classList.add('selected');
}
function openSpellMenu() {
// TODO: Open modal with spell selection
}
function openItemMenu() {
// TODO: Open modal with item selection
}
// Auto-scroll combat log to bottom
const logDiv = document.querySelector('.log-entries');
if (logDiv) {
logDiv.scrollTop = logDiv.scrollHeight;
}
</script>
{% endblock %}
Also create /public_web/static/css/combat.css
Acceptance Criteria:
- 3-column layout works
- Combat log displays messages
- HP/MP bars update dynamically
- Action buttons trigger HTMX requests
- Turn order displays correctly
- Active effects shown
Task 3.2: Combat HTMX Integration (1 day / 8 hours)
Objective: Wire combat UI to API via HTMX
File: /public_web/app/views/combat.py
Implementation:
"""
Combat Views
Routes for combat UI.
"""
from flask import Blueprint, render_template, request, g, redirect, url_for
from app.services.api_client import APIClient, APIError
from app.utils.auth import require_auth
from app.utils.logging import get_logger
logger = get_logger(__file__)
combat_bp = Blueprint('combat', __name__)
@combat_bp.route('/<combat_id>')
@require_auth
def combat_view(combat_id: str):
"""Display combat interface."""
api_client = APIClient()
try:
# Get combat state
response = api_client.get(f'/combat/{combat_id}/state')
combat_state = response['result']
return render_template(
'game/combat.html',
combat_id=combat_id,
combat_state=combat_state,
turn_order=combat_state['turn_order'],
current_turn_index=combat_state['current_turn_index'],
combat_log=combat_state['combat_log'],
character=combat_state['combatants'][0], # Player is first
enemies=combat_state['combatants'][1:] # Rest are enemies
)
except APIError as e:
logger.error(f"Failed to load combat {combat_id}: {e}")
return redirect(url_for('game.play'))
@combat_bp.route('/<combat_id>/action', methods=['POST'])
@require_auth
def combat_action(combat_id: str):
"""Process combat action (HTMX endpoint)."""
api_client = APIClient()
action_data = {
'action_type': request.form.get('action_type'),
'ability_id': request.form.get('ability_id'),
'target_id': request.form.get('target_id'),
'item_id': request.form.get('item_id')
}
try:
# Submit action to API
response = api_client.post(f'/combat/{combat_id}/action', json=action_data)
result = response['result']
# Check if combat ended
if result['combat_state']['status'] in ['victory', 'defeat']:
return redirect(url_for('combat.combat_results', combat_id=combat_id))
# Re-render combat view with updated state
return render_template(
'game/combat.html',
combat_id=combat_id,
combat_state=result['combat_state'],
turn_order=result['combat_state']['turn_order'],
current_turn_index=result['combat_state']['current_turn_index'],
combat_log=result['combat_state']['combat_log'],
character=result['combat_state']['combatants'][0],
enemies=result['combat_state']['combatants'][1:]
)
except APIError as e:
logger.error(f"Combat action failed: {e}")
return render_template('partials/error.html', error=str(e))
@combat_bp.route('/<combat_id>/results')
@require_auth
def combat_results(combat_id: str):
"""Display combat results (victory/defeat)."""
api_client = APIClient()
try:
response = api_client.get(f'/combat/{combat_id}/results')
results = response['result']
return render_template(
'game/combat_results.html',
victory=results['victory'],
xp_gained=results['xp_gained'],
gold_gained=results['gold_gained'],
loot=results['loot']
)
except APIError as e:
logger.error(f"Failed to load combat results: {e}")
return redirect(url_for('game.play'))
Register blueprint in /public_web/app/__init__.py:
from app.views.combat import combat_bp
app.register_blueprint(combat_bp, url_prefix='/combat')
Acceptance Criteria:
- Combat view loads from API
- Action buttons submit to API
- Combat state updates dynamically
- Combat results shown at end
- Errors handled gracefully
Task 3.3: Inventory UI (1 day / 8 hours)
Objective: Add inventory accordion to character panel
File: /public_web/templates/game/partials/character_panel.html
Add Inventory Section:
{# Existing character panel code #}
{# Add Inventory Accordion #}
<div class="panel-accordion" data-accordion="inventory">
<button class="panel-accordion-header" onclick="togglePanelAccordion(this)">
<span>Inventory <span class="count">({{ character.inventory|length }}/{{ inventory_max }})</span></span>
<span class="accordion-icon">▼</span>
</button>
<div class="panel-accordion-content">
<div class="inventory-grid">
{% for item in inventory %}
<div class="inventory-item {{ item.rarity }}"
hx-get="/inventory/{{ character.character_id }}/item/{{ item.item_id }}"
hx-target="#modal-container"
hx-swap="innerHTML">
<img src="{{ item.icon_url or '/static/img/items/default.png' }}" alt="{{ item.name }}">
<span class="item-name">{{ item.name }}</span>
</div>
{% endfor %}
</div>
</div>
</div>
{# Equipment Section #}
<div class="panel-accordion" data-accordion="equipment">
<button class="panel-accordion-header" onclick="togglePanelAccordion(this)">
<span>Equipment</span>
<span class="accordion-icon">▼</span>
</button>
<div class="panel-accordion-content">
<div class="equipment-slots">
<div class="equipment-slot">
<label>Weapon:</label>
{% if character.equipped.weapon %}
<span class="equipped-item">{{ get_item_name(character.equipped.weapon) }}</span>
<button class="btn-small"
hx-post="/inventory/{{ character.character_id }}/unequip"
hx-vals='{"slot": "weapon"}'
hx-target="#character-panel"
hx-swap="outerHTML">
Unequip
</button>
{% else %}
<span class="empty-slot">Empty</span>
{% endif %}
</div>
<div class="equipment-slot">
<label>Helmet:</label>
{# Similar for helmet, chest, boots, etc. #}
</div>
</div>
</div>
</div>
Create /public_web/templates/game/partials/item_modal.html:
<div class="modal-overlay" onclick="closeModal()">
<div class="modal-content" onclick="event.stopPropagation()">
<div class="modal-header">
<h2 class="item-name {{ item.rarity }}">{{ item.name }}</h2>
<button class="modal-close" onclick="closeModal()">×</button>
</div>
<div class="modal-body">
<p class="item-description">{{ item.description }}</p>
<div class="item-stats">
{% if item.item_type == 'weapon' %}
<p><strong>Damage:</strong> {{ item.damage }}</p>
<p><strong>Crit Chance:</strong> {{ (item.crit_chance * 100)|int }}%</p>
{% elif item.item_type == 'armor' %}
<p><strong>Defense:</strong> {{ item.defense }}</p>
<p><strong>Resistance:</strong> {{ item.resistance }}</p>
{% elif item.item_type == 'consumable' %}
<p><strong>HP Restore:</strong> {{ item.hp_restore }}</p>
<p><strong>MP Restore:</strong> {{ item.mp_restore }}</p>
{% endif %}
</div>
<p class="item-value">Value: {{ item.value }} gold</p>
</div>
<div class="modal-footer">
{% if item.item_type == 'weapon' %}
<button class="btn btn-primary"
hx-post="/inventory/{{ character_id }}/equip"
hx-vals='{"item_id": "{{ item.item_id }}", "slot": "weapon"}'
hx-target="#character-panel"
hx-swap="outerHTML">
Equip Weapon
</button>
{% elif item.item_type == 'consumable' %}
<button class="btn btn-primary"
hx-post="/inventory/{{ character_id }}/use"
hx-vals='{"item_id": "{{ item.item_id }}"}'
hx-target="#character-panel"
hx-swap="outerHTML">
Use Item
</button>
{% endif %}
<button class="btn btn-secondary" onclick="closeModal()">Cancel</button>
</div>
</div>
</div>
Acceptance Criteria:
- Inventory displays in character panel
- Click item shows modal with details
- Equip/unequip works via HTMX
- Use consumable works
- Equipment slots show equipped items
Task 3.4: Combat Testing & Polish (1 day / 8 hours)
Objective: Playtest combat and fix bugs
Testing Checklist:
- Start combat from story session
- Turn order correct
- Attack deals damage
- Critical hits work
- Spells consume mana
- Effects apply and tick correctly
- Items can be used in combat
- Defend action works
- Victory awards XP/gold/loot
- Defeat handling works
- Combat log readable
- HP/MP bars update
- Multiple enemies work
- Combat state persists (refresh page)
Bug Fixes & Polish:
- Fix any calculation errors
- Improve combat log messages
- Add visual feedback (animations, highlights)
- Improve mobile responsiveness
- Add loading states
Acceptance Criteria:
- Combat flows smoothly start to finish
- No critical bugs
- UX feels responsive and clear
- Ready for real gameplay
Phase 4B: Skill Trees & Leveling (Week 4)
See /PHASE4b.md
Phase 4C: NPC Shop (Days 15-18)
See /PHASE4c.md
Success Criteria - Phase 4 Complete
Combat System
- Turn-based combat works end-to-end
- Damage calculations correct (physical, magical, critical)
- Effects process correctly (DOT, HOT, buffs, debuffs, shields, stun)
- Combat UI functional and responsive
- Victory awards XP, gold, loot
- Combat state persists
Inventory System
- Inventory displays in UI
- Equip/unequip items works
- Consumables can be used
- Equipment affects character stats
- Item YAML data loaded correctly
Skill Trees
- Visual skill tree UI works
- Prerequisites enforced
- Unlock skills with skill points
- Respec functionality works
- Stat bonuses apply immediately
Leveling
- XP awarded after combat
- Level up triggers at threshold
- Skill points granted on level up
- Level up modal shown
- Character stats increase
NPC Shop
- Shop inventory displays
- Purchase validation works
- Items added to inventory
- Gold deducted correctly
- Transactions logged
Next Steps After Phase 4
Once Phase 4 is complete, you'll have a fully playable combat game with progression. The next logical phases are:
Phase 5: Story Progression & Quests (Original Phase 4 from roadmap)
- AI-driven story progression
- Action prompts (button-based gameplay)
- Quest system (YAML-driven, context-aware)
- Full gameplay loop: Explore → Combat → Quests → Level Up
Phase 6: Multiplayer Sessions
- Invite-based co-op
- Time-limited sessions
- AI-generated campaigns
Phase 7: Marketplace & Economy
- Player-to-player trading
- Auction system
- Economy balancing
Appendix: Testing Strategy
Manual Testing Checklist
Combat:
- Start combat from story
- Turn order correct
- Attack deals damage
- Spells work
- Items usable in combat
- Defend action
- Victory conditions
- Defeat handling
Inventory:
- Add items
- Remove items
- Equip weapons
- Equip armor
- Use consumables
- Inventory UI updates
Skills:
- View skill trees
- Unlock skills
- Prerequisites enforced
- Stat bonuses apply
- Respec works
Shop:
- Browse inventory
- Purchase items
- Insufficient gold handling
- Transaction logging
Document Maintenance
Update this document as you complete tasks:
- Mark tasks complete with ✅
- Add notes about implementation decisions
- Update time estimates based on actual progress
- Document any blockers or challenges
Good luck with Phase 4 implementation! 🚀