view --main adding-support-mod-parsers-skill-preobrazovaniya-strok-v-obekty.md
adding-support-mod-parsers: Скилл преобразования строк в объекты
SKILL.md
description_ar.txt
description_cn.txt
description_de.txt
description_en.txt
description_es.txt
description_fr.txt
description_it.txt
description_ja.txt
description_ko.txt
description_ru.txt
description_tw.txt
stats.json
readonly
--- lines
---
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()`).
Инициализация мануала...
//
$ ls -R related_skills/
2026-03-30
⭐ 115
pcbway — Автоматизированная разработка и изготовление печатных плат
2026-03-28
⭐ 28101
hosted-agents-v2-py — Автоматизированное развертывание агентов Azure AI
2026-04-01
⭐ 511
Touch-controls: скилл для виртуальных джойстиков
2026-03-29
⭐ 3949
lmms-eval-guide — Комплексная оценка производительности LLM и мультимодальных моделей
package.json
$ install --global
skills.sh
npx skills add https://github.com/NeverSight/learn-skills.dev/tree/main/data/skills-md/aclinia/torchlight-of-building/adding-support-mod-parsers
$ download --local
man
[HINT] Скачивает всю директорию скилла с GitHub: SKILL.md и все связанные файлы