ModernUO/dev-docs/claude-skills/modernuo-era-expansion.md
Kamron Batman 1391c563fe
chore: Adds AI instructions and SKILLs for ModernUO codebase (#2347)
Summary

  - Adds CLAUDE.md at repo root with 14 terse code audit rules (always loaded, low token cost)
  - Adds pointer files for other AI tools: AGENTS.md (Codex), GEMINI.md, .github/COPILOT-INSTRUCTIONS.md (Copilot), .cursorrules (Cursor) — all redirect to CLAUDE.md as single source of truth
  - Gitignores /.claude so personal AI config isn't distributed
  - Moves Claude skills to dev-docs/claude-skills/ (opt-in, not auto-loaded)
  - Adds 14 dev-docs covering codebase conventions

  Code Audit Rules (in CLAUDE.md)

  1. LINQ tiered rules (Tier 1 free, Tier 2 warm, Tier 3 forbidden)
  2. No Console.WriteLine — use LogFactory.GetLogger()
  3. No concurrency primitives in game code
  4. No World.Mobiles/World.Items iteration
  5. Clean up refs in OnDelete()/OnAfterDelete()
  6. Cancel timers in OnDelete()/OnAfterDelete()
  7. STArrayPool<T>.Shared not ArrayPool<T>.Shared
  8. PooledRefList<T> not new List<T>() on hot paths
  9. Serialization: partial class, [Constructible], no serialized TimerExecutionToken
  10. No Task.Run/new Thread() in game code
  11. Never assume era — ask which expansion
  12. _camelCase fields, PascalCase properties/methods
  13. No empty gumps — use DisplayTo() pattern
  14. PropertyList string literals must be {} holes, cliloc-as-argument uses :#
2026-03-01 11:42:19 -08:00

134 lines
3.8 KiB
Markdown

---
name: modernuo-era-expansion
description: >
Trigger when writing era-conditional code, using Core.AOS/SE/ML etc., or when user hasn't specified target era. Always ask which expansion to target if not specified.
---
# ModernUO Era & Expansion Support
## When This Activates
- Writing code that depends on game era (damage formulas, skill caps, mechanics)
- Using `Core.AOS`, `Core.SE`, `Core.ML`, etc.
- User asks for a feature without specifying era
- Implementing mechanics that changed across expansions
## CRITICAL RULE
**Never assume era.** If the user hasn't specified which expansion to target, ASK before writing era-dependent code.
## Expansion Enum
```csharp
public enum Expansion
{
None, // 0 - Pre-T2A
T2A, // 1 - The Second Age
UOR, // 2 - Renaissance
UOTD, // 3 - Third Dawn
LBR, // 4 - Blackthorn's Revenge
AOS, // 5 - Age of Shadows (major overhaul)
SE, // 6 - Samurai Empire
ML, // 7 - Mondain's Legacy
SA, // 8 - Stygian Abyss
HS, // 9 - High Seas
TOL, // 10 - Time of Legends
EJ // 11 - Endless Journey
}
```
## Era Check Properties
Each returns `true` when `Core.Expansion >= that era`:
```csharp
Core.T2A // >= The Second Age
Core.UOR // >= Renaissance
Core.UOTD // >= Third Dawn
Core.LBR // >= Blackthorn's Revenge
Core.AOS // >= Age of Shadows
Core.SE // >= Samurai Empire
Core.ML // >= Mondain's Legacy
Core.SA // >= Stygian Abyss
Core.HS // >= High Seas
Core.TOL // >= Time of Legends
Core.EJ // >= Endless Journey
```
For exact expansion: `Core.Expansion == Expansion.AOS`
## Key Era Boundaries
### AOS (Age of Shadows) -- Most Significant Change
- Complete combat overhaul: resistance-based damage system
- Property-based item system (magic properties)
- New damage formula: `GetNewAosDamage()` vs flat `Utility.Random()`
- Luck system for loot
- Insurance system
### SE (Samurai Empire)
- Bushido / Ninjitsu skills
- Samurai/Ninja classes
- Loot pack adjustments
### ML (Mondain's Legacy)
- Spellweaving
- Adjusted skill gain chances
- Container weight display changes
## Pattern: Era-Conditional Code
```csharp
// Ternary for simple values
var delay = Core.SE ? 250 : Core.AOS ? 500 : 1000;
// If/else for logic branches
if (Core.AOS)
{
damage = GetNewAosDamage(10, 1, 4, target);
}
else
{
damage = Utility.Random(4, 4);
if (CheckResisted(target))
damage *= 0.75;
}
// Property display varies by era
if (Core.ML)
{
list.Add(1072241, $"{TotalItems}\t{MaxItems}\t{TotalWeight}\t{MaxWeight}");
}
else
{
list.Add(1050044, $"{TotalItems}\t{TotalWeight}");
}
```
## Pattern: Era-Dependent Loot
`LootPack` properties auto-select based on expansion:
```csharp
// These automatically pick the right era variant
LootPack.Poor // OldPoor / AosPoor / SePoor
LootPack.Average // OldAverage / AosAverage / SeAverage
LootPack.Rich // OldRich / AosRich / SeRich
LootPack.FilthyRich // OldFilthyRich / AosFilthyRich / SeFilthyRich
LootPack.UltraRich // OldUltraRich / AosUltraRich / SeUltraRich
```
## Anti-Patterns
- **Hardcoding mechanics for one era** without conditional checks
- **Assuming AOS** when the user might want pre-AOS
- **Using era-specific APIs** without checking (e.g., `GetNewAosDamage()` pre-AOS)
## Real Examples
- Era-conditional damage: `Projects/UOContent/Spells/First/MagicArrow.cs`
- Era-conditional properties: `Projects/Server/Items/Container.cs` (`GetProperties`)
- Era-conditional values: `Projects/UOContent/Items/Weapons/Ranged/BaseRanged.cs`
- Expansion enum: `Projects/Server/ExpansionInfo.cs`
- Stat config by era: `Projects/UOContent/Skills/SkillCheck.cs`
## See Also
- `dev-docs/era-expansion.md` - Complete expansion documentation
- `dev-docs/claude-skills/modernuo-content-patterns.md` - Content templates
- `dev-docs/claude-skills/modernuo-configuration.md` - Configuration system