Skip to content

Commit fd9b9a9

Browse files
feat: implement Masterwork to Master rank trigger
Implements Epic 4.8 - When a character crafts a Masterwork item, they immediately reach Master rank (level 10) in all classes associated with that recipe's skill requirements. Changes: - Added MasterRankAchievedEvent to track Master rank achievements - Modified CraftingSystem to detect Masterwork outcomes and grant Master rank - Extracted MasterRankLevel constant to ClassProgressCollection - Consolidated class progress updates for cleaner code - Added comprehensive test coverage (4 tests) This completes Epic 4 (Crafting and Class Progression) - all 8 tasks done. Closes #39
1 parent d50f1c4 commit fd9b9a9

5 files changed

Lines changed: 313 additions & 27 deletions

File tree

docs/plans/rules-engine-design.md

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,29 @@ Build a headless .NET 10 simulation engine that validates game design by running
1414

1515
### Current Sprint Goal
1616

17-
Complete Epic 4 (Crafting and Class Progression) to enable full crafting mechanics and class unlocking.
17+
Epic 4 (Crafting and Class Progression) complete! Next: Epic 5 (Economy and Inventory) to enable trading and resource management.
1818

1919
### Epic Status Overview
2020

21-
| Epic | Title | Status | Progress | GitHub Issue |
22-
| ---- | -------------------------------- | -------------- | -------- | ------------ |
23-
| 1 | Project Foundation | ✅ Complete | 5/5 | #3 |
24-
| 2 | Time and Simulation Loop | ✅ Complete | 6/6 | #4 |
25-
| 3 | NPC Decision Making | ✅ Complete | 6/6 | #5 |
26-
| 4 | Crafting and Class Progression | 🔄 In Progress | 7/8 | #6 |
27-
| 5 | Economy and Inventory | ⏳ Planned | 0/6 | #7 |
28-
| 6 | Contracts and Commitments | 🔄 Partial | 2/6 | #8 |
29-
| 7 | Settlement and Guild | ⏳ Planned | 0/6 | #9 |
30-
| 8 | Scenario Testing Framework | ⏳ Planned | 0/7 | #10 |
31-
| 9 | Persistence | ⏳ Planned | 0/4 | #11 |
32-
| 10 | Apprenticeship and Role Stacking | ⏳ Planned | 0/4 | #12 |
33-
| 11 | Full Progression Scenario | ⏳ Planned | 0/5 | #13 |
34-
35-
**Total Progress**: 26/58 tasks complete (45%)
21+
| Epic | Title | Status | Progress | GitHub Issue |
22+
| ---- | -------------------------------- | ----------- | -------- | ------------ |
23+
| 1 | Project Foundation | ✅ Complete | 5/5 | #3 |
24+
| 2 | Time and Simulation Loop | ✅ Complete | 6/6 | #4 |
25+
| 3 | NPC Decision Making | ✅ Complete | 6/6 | #5 |
26+
| 4 | Crafting and Class Progression | ✅ Complete | 8/8 | #6 |
27+
| 5 | Economy and Inventory | ⏳ Planned | 0/6 | #7 |
28+
| 6 | Contracts and Commitments | 🔄 Partial | 2/6 | #8 |
29+
| 7 | Settlement and Guild | ⏳ Planned | 0/6 | #9 |
30+
| 8 | Scenario Testing Framework | ⏳ Planned | 0/7 | #10 |
31+
| 9 | Persistence | ⏳ Planned | 0/4 | #11 |
32+
| 10 | Apprenticeship and Role Stacking | ⏳ Planned | 0/4 | #12 |
33+
| 11 | Full Progression Scenario | ⏳ Planned | 0/5 | #13 |
34+
35+
**Total Progress**: 27/58 tasks complete (47%)
3636

3737
### Recent Completions
3838

39+
-**2026-01-19**: Epic 4.8 - Masterwork → Master rank trigger (#39, PR #136)
3940
-**2026-01-19**: Epic 4.7 - CraftingSystem (#38, PR #135)
4041
-**2026-01-19**: Epic 4.6 - CraftingOutcomeRule (#37, PR #134)
4142
-**2026-01-19**: Epic 4.5 - Define RecipeDefinition (#36, PR #127, commit 8148f26)
@@ -48,7 +49,8 @@ Complete Epic 4 (Crafting and Class Progression) to enable full crafting mechani
4849

4950
Priority order for remaining work:
5051

51-
1. **Epic 4.8** - #39: Implement Masterwork → Master rank trigger
52+
1. **Epic 5** - #7: Economy and Inventory (0/6 tasks)
53+
2. **Epic 6** - #8: Complete Contracts and Commitments (4/6 remaining)
5254

5355
## Key Design Decisions
5456

@@ -250,11 +252,13 @@ _For implementation patterns, examine referenced code paths._
250252
- ✅ Event emission with outcome details
251253
- **Verified**: 8 tests pass, crafting produces items and awards XP, all 511 tests pass
252254

253-
**4.8 Implement Masterwork → Master rank trigger** _Issue #39_
255+
**4.8 Implement Masterwork → Master rank trigger** _Issue #39, PR #136_
254256

255-
- Creating Masterwork immediately grants Master rank
256-
- Emit event
257-
- **Verify**: Masterwork creation triggers Master rank
257+
- ✅ MasterRankAchievedEvent for tracking Master rank achievements
258+
- ✅ Masterwork outcome immediately grants Master rank (level 10)
259+
- ✅ Event emission with trigger reason
260+
- ✅ Handles already-Master characters (no duplicate events)
261+
- **Verified**: 4 tests pass, Masterwork triggers Master rank, all 515 tests pass
258262

259263
---
260264

src/FantasyRpgWorld.Core/Domain/Components/ClassProgressCollection.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ namespace FantasyRpgWorld.Core.Domain.Components;
88
/// </summary>
99
public readonly record struct ClassProgressCollection
1010
{
11+
/// <summary>
12+
/// Master rank level threshold. Achieved when creating a Masterwork item.
13+
/// </summary>
14+
public const int MasterRankLevel = 10;
15+
1116
private readonly IReadOnlyDictionary<ClassId, ClassProgress> _progressByClass;
1217

1318
public ClassProgressCollection()
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using FantasyRpgWorld.Core.Domain.ValueObjects;
2+
3+
namespace FantasyRpgWorld.Core.Events;
4+
5+
/// <summary>
6+
/// Event emitted when a character achieves Master rank (level 10) in a class.
7+
/// Triggered by creating a Masterwork item.
8+
/// </summary>
9+
public sealed record MasterRankAchievedEvent : IGameEvent
10+
{
11+
public GameTime OccurredAt { get; init; }
12+
public CharacterId CharacterId { get; init; }
13+
public ClassId ClassId { get; init; }
14+
public string TriggerReason { get; init; }
15+
16+
public MasterRankAchievedEvent(
17+
GameTime occurredAt,
18+
CharacterId characterId,
19+
ClassId classId,
20+
string triggerReason)
21+
{
22+
OccurredAt = occurredAt;
23+
CharacterId = characterId;
24+
ClassId = classId;
25+
TriggerReason = triggerReason ?? string.Empty;
26+
}
27+
}

src/FantasyRpgWorld.Simulation/Systems/CraftingSystem.cs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using FantasyRpgWorld.Core.Definitions;
2+
using FantasyRpgWorld.Core.Domain.Components;
23
using FantasyRpgWorld.Core.Domain.ValueObjects;
34
using FantasyRpgWorld.Core.Domain.World;
45
using FantasyRpgWorld.Core.Events;
@@ -62,10 +63,11 @@ public SystemUpdateResult Update(WorldState worldState, GameTime currentTime)
6263
activeCraft.WorkspaceModifier,
6364
_random);
6465

65-
// Award XP if item was produced
66+
// Consolidate all class progress updates
6667
int xpAwarded = 0;
67-
var updatedCharacter = character;
68+
var classProgress = character.ClassProgress;
6869

70+
// Award XP if item was produced
6971
if (outcomeResult.ItemProduced)
7072
{
7173
// Calculate base XP (simple formula: quality / 2)
@@ -76,20 +78,50 @@ public SystemUpdateResult Update(WorldState worldState, GameTime currentTime)
7678
xpAwarded = XpCalculator.CalculateXp(baseXp, totalLevels);
7779

7880
// Award XP to all applicable classes (classes listed in skill requirements)
79-
var classProgress = character.ClassProgress;
8081
foreach (var skillReq in recipe.SkillRequirements)
8182
{
8283
if (classProgress.HasClass(skillReq.ClassId))
8384
{
8485
classProgress = classProgress.AddXp(skillReq.ClassId, xpAwarded, out _);
8586
}
8687
}
88+
}
8789

88-
updatedCharacter = updatedCharacter.WithClassProgress(classProgress);
90+
// Handle Masterwork → Master rank trigger
91+
if (outcomeResult.Outcome == CraftingOutcome.Masterwork)
92+
{
93+
foreach (var skillReq in recipe.SkillRequirements)
94+
{
95+
if (classProgress.HasClass(skillReq.ClassId))
96+
{
97+
int currentLevel = classProgress.GetLevel(skillReq.ClassId);
98+
if (currentLevel < ClassProgressCollection.MasterRankLevel)
99+
{
100+
// Set character to Master rank (level 10)
101+
// Calculate XP needed to reach Master rank
102+
int xpToAdd = 0;
103+
for (int level = currentLevel; level < ClassProgressCollection.MasterRankLevel; level++)
104+
{
105+
xpToAdd += 100 * level; // XP formula from ClassProgressCollection
106+
}
107+
108+
classProgress = classProgress.AddXp(skillReq.ClassId, xpToAdd, out _);
109+
110+
// Emit Master rank achieved event
111+
events.Add(new MasterRankAchievedEvent(
112+
currentTime,
113+
character.Id,
114+
skillReq.ClassId,
115+
"Masterwork"));
116+
}
117+
}
118+
}
89119
}
90120

91-
// Clear the active craft
92-
updatedCharacter = updatedCharacter.WithActiveCraft(null);
121+
// Apply all class progress changes at once
122+
var updatedCharacter = character
123+
.WithClassProgress(classProgress)
124+
.WithActiveCraft(null);
93125

94126
// Update world state
95127
updatedWorld = updatedWorld.UpdateCharacter(updatedCharacter);

0 commit comments

Comments
 (0)