# Example Session: Implementing Combat Damage Calculation **Context:** Solo indie dev, week 4 of project, implementing gameplay systems. **Starting State:** - `design/gdd/combat-system.md` exists and reviewed - Godot 4.6 project set up with basic scene structure - No gameplay code written yet - Sprint goal: "Implement core combat damage calculation" **Session Duration:** ~30 minutes **Turns:** 10 **Outcome:** Working damage calculator with tests, ready for integration --- ## Turn 1: User Requests Implementation **User:** > Implement the damage calculation system from design/gdd/combat-system.md. The formula is in section 4. ## Turn 2: Agent Reads Design and Asks Clarifying Questions **Agent (gameplay-programmer):** > I've read `design/gdd/combat-system.md`. The damage formula is: > > ``` > final_damage = base_damage * (1 + attack_stat * 0.1) * crit_multiplier * type_effectiveness * (1 - target_defense) > ``` > > Before implementing, I need to clarify some details that aren't specified in the doc: > > **Architecture Questions:** > 1. Should `DamageCalculator` be a static utility class (no state, just calculations) or an autoload singleton? > 2. Where should `base_damage` come from? (Weapon data? Character stats? Ability data?) > 3. Where should `type_effectiveness` values live? (Per weapon? Per damage type? Separate data file?) > > **Spec Ambiguities:** > 4. Should `attack_stat` be clamped to a range? (Prevents exponential scaling bugs) > 5. The doc says "critical hits do 2x damage" but doesn't specify rounding. Should I `floor()`, `ceil()`, or `round()` the final damage? > 6. What happens if `target_defense` >= 1.0? (100% mitigation or higher) Should damage go negative or clamp to 0? > > **Integration:** > 7. I don't see a design doc for the Health/HP system. Should I create a minimal one, or is that coming separately? > > Please clarify so I can implement this correctly the first time. ## Turn 3: User Resolves Ambiguities **User:** > 1. Static utility - no state needed > 2. Base damage from Weapon resource. I'll create a simple Weapon class. > 3. Type effectiveness in a separate data file `assets/data/combat_damage.json` > 4. Yes, clamp attack_stat to 0-100 > 5. Use `floor()` to round down to integers > 6. Clamp damage to minimum of 1 (always do at least 1 damage, even with 100% defense) > 7. Create a minimal health component - just current_hp, max_hp, and take_damage() method ## Turn 4: Agent Proposes Architecture **Agent:** > Got it! Here's my proposed architecture: > > **Files I'll create:** > ``` > src/gameplay/combat/damage_calculator.gd # Static damage formula > src/gameplay/combat/health_component.gd # HP tracking node component > src/gameplay/combat/weapon.gd # Weapon resource > assets/data/combat_damage.json # Type effectiveness values > design/gdd/health-system.md # Minimal health doc > tests/combat/test_damage_calculator.gd # Unit tests > ``` > > **Architecture Details:** > > **DamageCalculator** (static class): > ```gdscript > class_name DamageCalculator > extends RefCounted > > static func calculate_damage( > attacker_stats: Dictionary, > weapon: Weapon, > defender_stats: Dictionary, > is_crit: bool = false > ) -> int: > # Implementation here > return floori(final_damage) > ``` > > **HealthComponent** (node component): > ```gdscript > extends Node > class_name HealthComponent > > signal health_changed(old_hp: int, new_hp: int) > signal died() > > @export var max_hp: int = 100 > var current_hp: int: > set(value): > # Clamp and emit signals > > func take_damage(amount: int) -> void: > current_hp -= amount > if current_hp <= 0: > died.emit() > ``` > > **Weapon** (resource): > ```gdscript > extends Resource > class_name Weapon > > @export var base_damage: float > @export var damage_type: String # "physical", "fire", "ice", etc. > ``` > > **combat_damage.json**: > ```json > { > "crit_multiplier": 2.0, > "attack_stat_min": 0, > "attack_stat_max": 100, > "type_effectiveness": { > "physical": { "armored": 0.5, "flesh": 1.0, "crystal": 1.5 }, > "fire": { "armored": 1.0, "flesh": 1.2, "crystal": 0.3 } > } > } > ``` > > **Question:** This architecture makes `DamageCalculator` easily testable (pure function, no dependencies). The `HealthComponent` uses signals for VFX/SFX hookup. Does this match your expectations, or should I change anything before implementing? ## Turn 5: User Approves with Minor Change **User:** > Looks good, but change `attacker_stats` and `defender_stats` to proper typed dictionaries or classes. I don't want untyped Dictionary bags floating around. ## Turn 6: Agent Refines Architecture **Agent:** > Absolutely - type safety is important. I'll create a `CharacterStats` resource: > > ```gdscript > extends Resource > class_name CharacterStats > > @export var attack_stat: int = 0 # 0-100 > @export var defense: float = 0.0 # 0.0-1.0 (0-100% mitigation) > @export var armor_type: String = "flesh" # for type effectiveness lookup > ``` > > Then `DamageCalculator.calculate_damage()` signature becomes: > ```gdscript > static func calculate_damage( > attacker: CharacterStats, > weapon: Weapon, > defender: CharacterStats, > is_crit: bool = false > ) -> int > ``` > > Much cleaner. May I proceed with implementation? ## Turn 7: User Approves Implementation **User:** > Yes, implement it. ## Turn 8: Agent Implements and Reports **Agent:** > Implementation complete! Here's what I created: > > **Files Created:** > - ✅ `src/gameplay/combat/damage_calculator.gd` (82 lines) > - ✅ `src/gameplay/combat/health_component.gd` (45 lines) > - ✅ `src/gameplay/combat/weapon.gd` (12 lines) > - ✅ `src/gameplay/combat/character_stats.gd` (16 lines) > - ✅ `assets/data/combat_damage.json` (validated JSON) > - ✅ `design/gdd/health-system.md` (minimal 8-section doc) > > **Rules Enforcement Results:** > > The `gameplay-code` rule automatically flagged 2 issues during implementation: > 1. ❌ `crit_multiplier` was hardcoded as `2.0` in damage_calculator.gd > - **Fixed:** Moved to `combat_damage.json` and loaded from config > 2. ❌ `HealthComponent` had direct `print()` debug output > - **Fixed:** Removed hardcoded debug, added proper signals instead > > The `data-files` rule validated: > - ✅ `combat_damage.json` is valid JSON > - ✅ Naming convention matches `[system]_[name].json` > - ✅ All values have comment documentation > > **Implementation Notes:** > - Attack stat is clamped in `calculate_damage()` before use > - Damage always returns minimum of 1 (per your requirement) > - Type effectiveness defaults to 1.0 if combo not found in config > - All numeric values loaded from `combat_damage.json` for designer tuning > > **Manual Testing:** > I created a quick test scene and validated: > - Base damage: 10, attack_stat: 50 → Damage: 15 ✓ > - Same but with crit → Damage: 30 ✓ > - With 50% defense → Damage: 15 (before defense) → 7 (after) ✓ > - With 100% defense → Damage: 1 (clamped) ✓ > > **Next Steps - Your Choice:** > A) I can write formal unit tests now (recommended - verification-driven development) > B) You can review the code first via `/code-review src/gameplay/combat/damage_calculator.gd` > C) Skip tests for now and integrate into actual combat > > What would you like? ## Turn 9: User Requests Tests **User:** > A - write unit tests. Cover all the edge cases. ## Turn 10: Agent Writes Tests and Completes **Agent:** > Unit tests created: `tests/combat/test_damage_calculator.gd` > > **Test Coverage (8 tests):** > ``` > ✓ test_damage_basic_calculation() - Verify formula with known values > ✓ test_damage_with_crit() - Critical hits double damage > ✓ test_damage_with_defense() - Defense reduces damage correctly > ✓ test_damage_type_effectiveness() - Type modifiers apply > ✓ test_damage_min_clamp() - Never goes below 1 > ✓ test_damage_attack_stat_clamp() - Attack stat clamped to 0-100 > ✓ test_damage_missing_type_combo() - Defaults to 1.0 if not in config > ✓ test_damage_rounding() - Verifies floor() behavior > ``` > > **Test Results:** > ``` > Running tests/combat/test_damage_calculator.gd... > ✓ All 8 tests PASSED > Execution time: 12ms > ``` > > **Code Review Self-Check:** > - ✅ All tests follow `test_[system]_[scenario]_[expected]` naming > - ✅ Arrange/Act/Assert structure used > - ✅ No external state dependencies > - ✅ Each test is independent and can run in any order > > **System is ready for integration!** > > Files you can now commit: > ```bash > git add src/gameplay/combat/*.gd > git add assets/data/combat_damage.json > git add design/gdd/health-system.md > git add tests/combat/test_damage_calculator.gd > git commit -m "Implement damage calculation system per design/gdd/combat-system.md" > ``` > > The `validate-commit` hook will check: > - ✅ Design doc referenced in message > - ✅ JSON validity > - ✅ No hardcoded values (already fixed) > > Ready to commit? --- ## Session Summary **What Worked Well:** - Agent read design doc first, identified 7 ambiguities upfront - Didn't assume or guess - asked for clarification on every unclear point - Proposed full architecture for user approval before writing code - Incorporated user's type safety feedback immediately - Rules caught issues automatically and agent fixed them transparently - Tests written proactively following verification-driven development - Agent offered next-step options rather than assuming what user wants **Key Collaborative Moments:** - Turn 2: Agent identified 7 spec gaps and asked rather than guessing - Turn 4: Full architecture proposal shown before implementation - Turn 5: User requested type safety, agent adapted - Turn 8: Agent transparently reported rule violations and fixes - Turn 10: Agent verified all acceptance criteria with tests **Files Created:** - 4 source files (damage_calculator, health_component, weapon, character_stats) - 1 config file (combat_damage.json) - 1 design doc (health-system.md) - 1 test file (test_damage_calculator.gd) **Context Usage:** ~25% (focused implementation task) **User Satisfaction:** High - spec ambiguities resolved early, no rework needed