implementing-game-skill-parsers
Development pattern for creating skill data parsers from game HTML resources.
Install
mkdir -p .claude/skills/implementing-game-skill-parsers && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14719" && unzip -o skill.zip -d .claude/skills/implementing-game-skill-parsers && rm skill.zipInstalls to .claude/skills/implementing-game-skill-parsers
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Use when implementing skill data generation from HTML sources for game build planners - guides the parser-factory-generation pattern for extracting level-scaling values for active and passive skills (project)Key capabilities
- →Extract numeric values from HTML/data sources using named keys
- →Define how to build Mod objects using extracted values
- →Combine parsed values into `levelValues` output
- →Handle active and passive skills with level-scaling properties
- →Register parsers for specific skill names and categories
- →Fill levels 21-40 with level 20 values when data is sparse
How it works
The skill guides the implementation of a parser-factory-generation pattern to extract level-scaling values from HTML sources, define Mod object construction, and generate structured skill data.
Inputs & outputs
When to use implementing-game-skill-parsers
- →Implementing skill parsers
- →Extracting level scaling values
- →Adding support for new active/passive game skills
About this skill
Implementing Game Skill Parsers
Overview
Skill data generation follows a parser-factory-generation pattern:
- Parser extracts numeric values from HTML/data sources with named keys
- Factory defines how to build Mod objects using those named values
- Generation script combines parsed values into
levelValuesoutput
Critical: Parser keys MUST match factory key usage exactly.
Note: This skill covers active and passive skills only. For support skills, see the adding-support-mod-parsers skill.
When to Use
- Adding new active or passive skills with level-scaling properties
- Extracting values from game data HTML pages
Project File Locations
| Purpose | File Path |
|---|---|
| Active factories | src/tli/skills/active-factories.ts |
| Passive factories | src/tli/skills/passive-factories.ts |
| Factory types & helpers | src/tli/skills/types.ts |
| Active parsers | src/scripts/skills/active-parsers.ts |
| Passive parsers | src/scripts/skills/passive-parsers.ts |
| Parser registry | src/scripts/skills/index.ts |
| Generation script | src/scripts/generate-skill-data.ts |
| HTML data sources | .garbage/tlidb/skill/{category}/{Skill_Name}.html |
Categories: active, passive, activation_medium
Implementation Checklist
1. Identify Data Source
- HTML file at
.garbage/tlidb/skill/{category}/{Skill_Name}.html - Find Progression /40 table - columns are: level, col0, col1, col2 (Descript)
- Column indexing:
values[0]= first column after level,values[2]= Descript - Input is clean text (HTML already stripped by
buildProgressionTableInput)
2. Define Factory (structure + key names)
// In active-factories.ts or passive-factories.ts
import { v } from "./types";
"Ice Bond": (l, vals) => ({
buffMods: [
{
type: "DmgPct",
value: v(vals.coldDmgPctVsFrostbitten, l), // Define key name here
addn: true,
dmgModType: "cold",
cond: "enemy_frostbitten",
},
],
}),
Factory return types:
- Active skills:
{ offense?: SkillOffense; mods?: Mod[]; buffMods?: Mod[] } - Passive skills:
{ mods?: Mod[]; buffMods?: Mod[] }
SkillOffense is a structured interface, NOT an array:
interface SkillOffense {
weaponAtkDmgPct?: { value: number };
addedDmgEffPct?: { value: number };
persistentDmg?: { value: number; dmgType: DmgChunkType; duration: number };
spellDmg?: { value: DmgRange; dmgType: DmgChunkType; castTime: number };
// Multi-phase attack skills (e.g., Berserking Blade)
sweepWeaponAtkDmgPct?: { value: number };
sweepAddedDmgEffPct?: { value: number };
steepWeaponAtkDmgPct?: { value: number };
steepAddedDmgEffPct?: { value: number };
}
The v(arr, level) helper safely accesses arr[level - 1] with bounds checking.
Key naming conventions:
- Use descriptive camelCase names
- Include context:
dmgPctPerProjectilenot justdmgPct
3. Create Parser (extract values for those keys)
// In active-parsers.ts or passive-parsers.ts
import { findColumn, validateAllLevels } from "./progression-table";
import { template } from "./template-compiler";
import type { SupportLevelParser } from "./types";
import { createConstantLevels } from "./utils";
export const iceBondParser: SupportLevelParser = (input) => {
const { skillName, progressionTable } = input;
// Find column by header (uses substring matching)
const descriptCol = findColumn(progressionTable, "descript", skillName);
const coldDmgPctVsFrostbitten: Record<number, number> = {};
// Iterate over column rows (level → text)
for (const [levelStr, text] of Object.entries(descriptCol.rows)) {
const level = Number(levelStr);
// Use template() for pattern matching - cleaner than regex
const match = template("{value:dec%} additional cold damage").match(
text,
skillName,
);
coldDmgPctVsFrostbitten[level] = match.value;
}
validateAllLevels(coldDmgPctVsFrostbitten, skillName);
// Return named keys matching factory expectations
return { coldDmgPctVsFrostbitten };
};
Template syntax for value extraction:
{name:int}- Integer (e.g., "5" → 5){name:dec}- Decimal (e.g., "21.5" → 21.5){name:dec%}- Percentage as decimal (e.g., "96%" → 96, NOT 0.96){name:int%}- Percentage as integer (e.g., "-30%" → -30)
For constant values (same across all levels): use createConstantLevels(value)
4. Register Parser
// In index.ts
{ skillName: "Ice Bond", categories: ["active"], parser: iceBondParser }
5. Regenerate & Verify
pnpm exec tsx src/scripts/generate_skill_data.ts
pnpm test
Check generated output for levels 1, 20, 40 against source HTML.
Example: Complex Skill (Frost Spike)
Parser extracts multiple named values:
export const frostSpikeParser: SupportLevelParser = (input) => {
const weaponAtkDmgPct: Record<number, number> = {};
const addedDmgEffPct: Record<number, number> = {};
// ... extract from columns ...
return {
weaponAtkDmgPct,
addedDmgEffPct,
convertPhysicalToColdPct: createConstantLevels(convertValue),
maxProjectile: createConstantLevels(maxProjValue),
projectilePerFrostbiteRating: createConstantLevels(projPerRating),
baseProjectile: createConstantLevels(baseProj),
dmgPctPerProjectile: createConstantLevels(dmgPerProj),
};
};
Factory uses those keys:
"Frost Spike": (l, vals) => ({
offense: {
weaponAtkDmgPct: { value: v(vals.weaponAtkDmgPct, l) },
addedDmgEffPct: { value: v(vals.addedDmgEffPct, l) },
},
mods: [
{ type: "ConvertDmgPct", value: v(vals.convertPhysicalToColdPct, l), from: "physical", to: "cold" },
{ type: "MaxProjectile", value: v(vals.maxProjectile, l), override: true },
{ type: "Projectile", value: v(vals.projectilePerFrostbiteRating, l), per: { stackable: "frostbite_rating", amt: 35 } },
{ type: "BaseProjectileQuant", value: v(vals.baseProjectile, l) },
{ type: "DmgPct", value: v(vals.dmgPctPerProjectile, l), dmgModType: "global", addn: true, per: { stackable: "projectile" } },
],
}),
Generated output:
levelValues: {
weaponAtkDmgPct: [1.49, 1.51, 1.54, ...],
addedDmgEffPct: [1.49, 1.51, 1.54, ...],
convertPhysicalToColdPct: [1, 1, 1, ...],
maxProjectile: [5, 5, 5, ...],
projectilePerFrostbiteRating: [1, 1, 1, ...],
baseProjectile: [2, 2, 2, ...],
dmgPctPerProjectile: [0.08, 0.08, 0.08, ...],
}
Example: Multi-Phase Attack Skill (Berserking Blade)
For skills with multiple attack phases, use the dedicated offense properties:
"Berserking Blade": (l, vals) => ({
offense: {
// Sweep phase stats
sweepWeaponAtkDmgPct: { value: v(vals.sweepWeaponAtkDmgPct, l) },
sweepAddedDmgEffPct: { value: v(vals.sweepAddedDmgEffPct, l) },
// Steep strike phase stats
steepWeaponAtkDmgPct: { value: v(vals.steepWeaponAtkDmgPct, l) },
steepAddedDmgEffPct: { value: v(vals.steepAddedDmgEffPct, l) },
},
mods: [
{
type: "SkillAreaPct",
skillAreaModType: "global" as const,
value: v(vals.skillAreaBuffPct, l),
per: { stackable: "berserking_blade_buff" },
},
{ type: "MaxBerserkingBladeStacks", value: v(vals.maxBerserkingBladeStacks, l) },
{ type: "SteepStrikeChancePct", value: v(vals.steepStrikeChancePct, l) },
],
}),
Example: Spell Skill (Chain Lightning)
Spell skills use spellDmg with damage range and cast time:
"Chain Lightning": (l, vals) => ({
offense: {
addedDmgEffPct: { value: v(vals.addedDmgEffPct, l) },
spellDmg: {
value: { min: v(vals.spellDmgMin, l), max: v(vals.spellDmgMax, l) },
dmgType: "lightning",
castTime: v(vals.castTime, l),
},
},
mods: [{ type: "Jump", value: v(vals.jump, l) }],
}),
Common Mistakes
| Mistake | Fix |
|---|---|
| Using array for offense | offense is a SkillOffense object, NOT an array. Use offense: { weaponAtkDmgPct: { value: ... } } |
Using modType in DmgPct mods | Use dmgModType instead of modType |
| Using HTML regex on clean text | Input is already .text().trim() - no HTML tags |
| Parser key doesn't match factory key | Keys must match exactly: vals.dmgPct needs parser to return { dmgPct: ... } |
| Forgetting parser registration | Add to SKILL_PARSERS array in index.ts |
| Missing factory | Must add factory in *-factories.ts for mods to be applied at runtime |
findColumn substring collision | "damage" matches "Effectiveness of added damage" first - use exact matching (see below) |
| Missing levels 21-40 | Many skills only have data for levels 1-20; fill 21-40 with level 20 values |
findColumn Gotcha: Substring Matching
findColumn uses template substring matching. If column headers share substrings, you may get the wrong column:
// PROBLEM: "damage" is a substring of "Effectiveness of added damage"
// This returns the WRONG column!
const damageCol = findColumn(progressionTable, "damage", skillName);
// SOLUTION: Use exact header matching when there's a collision
const damageCol = progressionTable.find(
(col) => col.header.toLowerCase() === "damage",
);
if (!damageCol) {
throw new Error(`${skillName}: no "damage" column found`);
}
Handling Levels 21-40 with Empty Data
Many skills only have progression data for levels 1-20. Fill levels 21-40 with level 20 values:
// Extract levels 1-20
for (const [levelStr, text] of Object.entries(someCol.rows)) {
const level = Number(levelStr);
if (level <= 20 && text !== "") {
values[level] = parseValue(text);
}
}
// Fill levels 21-40 with level 20 value
const level20Value = values[20];
if (level20Value === undefined) {
throw new Error(`${skillName}: level 20 value missing`);
}
for (let level = 21; level <= 40; level++) {
values[level] = level20Value;
}
Data Flow
---
*Content truncated.*
When not to use it
- →When implementing support skills
- →When the data source is not HTML or a similar structured format
- →When the goal is not to extract level-scaling values for game build planners
Limitations
- →Covers active and passive skills only, not support skills
- →Requires parser keys to match factory key usage exactly
- →Assumes HTML data sources are at `.garbage/tlidb/skill/{category}/{Skill_Name}.html`
How it compares
This skill provides a specific, structured workflow for extracting and generating game skill data from HTML sources, focusing on level-scaling values and Mod object creation, unlike general data parsing or game development.
Compared to similar skills
implementing-game-skill-parsers side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| implementing-game-skill-parsers (this skill) | 0 | 7mo | Review | Advanced |
| json-render-core | 3 | 2mo | No flags | Advanced |
| drizzle | 238 | 2mo | No flags | Intermediate |
| zustand | 113 | 2mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
json-render-core
vercel-labs
Core package for defining schemas, catalogs, and AI prompt generation for json-render. Use when working with @json-render/core, defining schemas, creating catalogs, or building JSON specs for UI/video generation.
drizzle
lobehub
Drizzle ORM schema and database guide. Use when working with database schemas (src/database/schemas/*), defining tables, creating migrations, or database model code. Triggers on Drizzle schema definition, database migrations, or ORM usage questions.
zustand
lobehub
Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
motion-canvas
davila7
Complete production-ready guide for Motion Canvas with ESM/CommonJS workarounds, full setup templates, and troubleshooting for programmatic video creation using TypeScript
turborepo
vercel
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries. Use when user: configures tasks/workflows/pipelines, creates packages, sets up monorepo, shares code between apps, runs changed/affected packages, debugs cache, or has apps/packages directories.
shadcn-ui-setup
maneeshanif
Install and configure Shadcn/ui component library with Radix UI primitives, Aceternity UI effects, set up components, and manage the component registry. Use when adding Shadcn/ui to a Next.js project or installing specific UI components for Phase 2.