Use when adding support mod parsers to convert support skill affix strings to typed SupportMod objects - guides the template-based parsing pattern (project)
Scanned 9/4/2026
Install to Claude Code
npx -y skills add NeverSight/skills_feed --skill adding-support-mod-parsers --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Adding Support Mod Parsers?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/neversight-adding-support-mod-parsers)More formats (shields.io, HTML) on the badges page.
---
name: adding-support-mod-parsers
description: Use when adding support mod parsers to convert support skill affix strings to typed SupportMod objects - guides the template-based parsing pattern (project)
---
# Adding Support Mod Parsers
## Overview
Support mod parsers convert raw support skill affix strings (e.g., `"+15% additional damage for the supported skill"`) into typed `SupportMod` objects at runtime. Unlike active/passive skills which use level-scaling factories, support skills parse their affixes directly using templates.
## When to Use
- Adding support for new support skill affix patterns
- Extending support mod parsing to handle new variants
## Project File Locations
| Purpose | File Path |
|---------|-----------|
| Support mod templates | `src/tli/skills/support-mod-templates.ts` |
| Mod type definitions | `src/tli/mod.ts` |
| SupportMod type | `src/tli/core.ts` |
| Template/spec helpers | `src/tli/mod-parser/` |
| Calculation handlers | `src/tli/calcs/offense.ts` |
## Implementation Checklist
### 1. Check if Mod Type Exists
Look in `src/tli/mod.ts` under `ModDefinitions`. If the mod type doesn't exist, add it first (see `adding-mod-parsers` skill).
### 2. Add Template in `support-mod-templates.ts`
Templates use the same DSL as the main mod parser:
```typescript
// In allSupportParsers array
t("{value:dec%} additional damage for the supported skill").output(
"DmgPct",
(c) => ({
value: c.value,
dmgModType: "global" as const,
addn: true,
}),
),
```
**Template capture types:**
| Type | Matches | Example Input → Output |
|------|---------|------------------------|
| `{name:int}` | Unsigned integer | `"5"` → `5` |
| `{name:dec}` | Unsigned decimal | `"21.5"` → `21.5` |
| `{name:int%}` | Unsigned integer percent | `"30%"` → `30` |
| `{name:dec%}` | Unsigned decimal percent | `"96%"` → `96` |
| `{name:+int}` | **Signed** integer (requires `+` or `-`) | `"+5"` → `5`, `"-3"` → `-3` |
| `{name:+dec}` | **Signed** decimal (requires `+` or `-`) | `"+21.5"` → `21.5` |
| `{name:+int%}` | **Signed** integer percent | `"+30%"` → `30`, `"-15%"` → `-15` |
| `{name:+dec%}` | **Signed** decimal percent | `"+96%"` → `96` |
**Signed vs Unsigned Types:**
- Use **unsigned** (`dec%`, `int`) when input does NOT start with `+` or `-` (e.g., `"0.8% additional damage"`)
- Use **signed** (`+dec%`, `+int`) when input STARTS with `+` or `-` (e.g., `"+19.8% additional damage"`)
- Signed types will NOT match unsigned inputs, and vice versa
- **IMPORTANT:** Some support skills have signed inputs, others have unsigned - you may need BOTH templates (see examples below)
**Optional syntax:**
- `[additional]` - Optional literal, sets `c.additional?: true`
- `(effect|damage)` - Alternation (regex-style)
- `\\(` and `\\)` - Escaped parentheses for literal matching
### 3. SupportMod Structure
Each parsed mod is wrapped in `SupportMod`:
```typescript
interface SupportMod {
mod: Mod;
}
```
The `parseSupportAffix` function handles this wrapping:
```typescript
return mods.map((mod) => ({ mod }));
```
### 4. Multi-Output Parsers
For affixes that produce multiple mods:
```typescript
t("{value:dec%} additional attack and cast speed for the supported skill")
.outputMany([
spec("AspdPct", (c) => ({ value: c.value, addn: true })),
spec("CspdPct", (c) => ({ value: c.value, addn: true })),
]),
```
### 5. Add a Test
Add a test case to `src/tli/skills/support-mod-templates.test.ts` using the example input string given to you:
```typescript
test("parse <skill name> <description of what it parses>", () => {
const result = parseSupportAffixes([
"<exact input string from the skill data>",
]);
expect(result).toEqual([
[
{
mod: {
type: "<ModType>",
// ... expected mod properties
},
},
],
]);
});
```
### 6. Verify
Run tests to ensure parsing works:
```bash
pnpm test
pnpm typecheck
pnpm check
```
## Examples
### Damage Mod with BOTH Signed and Unsigned Variants
Some support skills use `+{value}%` templates (e.g., Increased Area) while others use `{value}%` (e.g., Haunt). You need BOTH templates:
**Inputs:**
- `"+19.8% additional damage for the supported skill"` (Increased Area - signed)
- `"0.8% additional damage for the supported skill"` (Haunt - unsigned)
```typescript
// Signed version (e.g., "+19.8% additional damage...")
t("{value:+dec%} additional damage for the supported skill").output(
"DmgPct",
(c) => ({
value: c.value,
dmgModType: "global" as const,
addn: true,
}),
),
// Unsigned version (e.g., "0.8% additional damage...")
t("{value:dec%} additional damage for the supported skill").output(
"DmgPct",
(c) => ({
value: c.value,
dmgModType: "global" as const,
addn: true,
}),
),
```
### Typed Damage Mod (Signed)
**Input:** `"+20% additional melee damage for the supported skill"`
```typescript
t("{value:+dec%} additional melee damage for the supported skill").output(
"DmgPct",
(c) => ({
value: c.value,
dmgModType: "melee" as const,
addn: true,
}),
),
```
### Attack Speed (Signed - can be negative)
**Input:** `"-15% Attack Speed for the supported skill"` (Steamroll)
```typescript
t("{value:+dec%} attack speed for the supported skill").output(
"AspdPct",
(c) => ({
value: c.value,
addn: false,
}),
),
```
Note: `+dec%` matches both `+` and `-` signs.
### Conditional Mod
**Input:** `"The supported skill deals +30% additional damage to cursed enemies"`
```typescript
t("the supported skill deals {value:dec%} additional damage to cursed enemies")
.output("DmgPct", (c) => ({
value: c.value,
dmgModType: "global" as const,
addn: true,
cond: "enemy_is_cursed" as const,
})),
```
### Per-Stackable Mod
**Input:** `"+5% additional damage for the supported skill for every stack of buffs while standing still"`
```typescript
t("{value:dec%} additional damage for the supported skill for every stack of buffs while standing still")
.output("DmgPct", (c) => ({
value: c.value,
dmgModType: "global" as const,
addn: false,
per: { stackable: "willpower" as const },
})),
```
### Mod with No Value
**Input:** `"The supported skill cannot inflict wilt"`
```typescript
t("the supported skill cannot inflict wilt").output("CannotInflictWilt"),
```
### Escaped Parentheses
**Input:** `"Stacks up to 5 time(s)"`
```typescript
t("stacks up to {value:int} time(s)").output("MaxWillpowerStacks", (c) => ({
value: c.value,
})),
```
Note: Literal `(` and `)` don't need escaping when they don't contain alternations.
### Complex Pattern with Ignored Values
**Input:** `"When the supported skill deals damage over time, it inflicts 10 affliction on the enemy. Effect cooldown: 3 s"`
```typescript
t("when the supported skill deals damage over time, it inflicts {value:int} affliction on the enemy. effect cooldown: {_:int} s")
.output("AfflictionInflictedPerSec", (c) => ({
value: c.value,
})),
```
Use `{_:type}` to capture but ignore values.
### Shadow Quantity (Signed Flat Integer)
**Input:** `"+2 Shadow Quantity for the supported skill"`
```typescript
t("{value:+int} shadow quantity for the supported skill").output(
"ShadowQuant",
(c) => ({
value: c.value,
}),
),
```
## Template Ordering
**IMPORTANT:** More specific patterns must come before generic ones in `allSupportParsers` array.
```typescript
// Good: specific before generic
t("{value:dec%} additional melee damage for the supported skill").output(...),
t("{value:dec%} additional damage for the supported skill").output(...),
// Bad: generic would match first
t("{value:dec%} additional damage for the supported skill").output(...),
t("{value:dec%} additional melee damage for the supported skill").output(...), // never matches
```
## Common Mistakes
| Mistake | Fix |
|---------|-----|
| Using `dec%` for input with `+` prefix | Use `+dec%` for inputs like `"+25% damage"` |
| Using `+dec%` for input without sign | Use `dec%` for inputs like `"0.8% damage"` |
| Only one template when inputs vary | Add BOTH signed and unsigned templates (see examples) |
| Generic template before specific | Move specific templates earlier in array |
| Missing `as const` on string literals | Add `as const` for type narrowing |
| Handler doesn't account for new mod type | Update `offense.ts` to handle new mod types |
| Forgot the wrapper structure | `parseSupportAffix` already wraps in `{ mod }` |
## Data Flow
```
Support skill affix: "+15% additional damage for the supported skill"
↓ parseSupportAffixes()
↓ normalize (lowercase, trim)
"15% additional damage for the supported skill"
↓ template matching (allSupportParsers)
[{ mod: { type: "DmgPct", value: 15, dmgModType: "global", addn: true } }]
↓ resolveSelectedSkillSupportMods() in offense.ts
Applied to skill calculations
```
## Difference from Main Mod Parser
| Aspect | Main Mod Parser | Support Mod Parser |
|--------|-----------------|-------------------|
| File | `src/tli/mod-parser/templates.ts` | `src/tli/skills/support-mod-templates.ts` |
| Source | Gear affixes, talents, etc. | Support skill affixes only |
| Output | `Mod[]` | `SupportMod[]` (wrapped in `{ mod }`) |
| Usage | `parseMod()` | `parseSupportAffixes()` |
Both use the same template DSL (`t()`, `spec()`, `outputMany()`).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!