IM

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.zip

Installs 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)
208 chars✓ has a “when” trigger
Advanced

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

You give it
HTML skill data from `.garbage/tlidb/skill/{category}/{Skill_Name}.html`
You get back
TypeScript file with generated skill data (`levelValues` arrays)

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:

  1. Parser extracts numeric values from HTML/data sources with named keys
  2. Factory defines how to build Mod objects using those named values
  3. Generation script combines parsed values into levelValues output

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

PurposeFile Path
Active factoriessrc/tli/skills/active-factories.ts
Passive factoriessrc/tli/skills/passive-factories.ts
Factory types & helperssrc/tli/skills/types.ts
Active parserssrc/scripts/skills/active-parsers.ts
Passive parserssrc/scripts/skills/passive-parsers.ts
Parser registrysrc/scripts/skills/index.ts
Generation scriptsrc/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: dmgPctPerProjectile not just dmgPct

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

MistakeFix
Using array for offenseoffense is a SkillOffense object, NOT an array. Use offense: { weaponAtkDmgPct: { value: ... } }
Using modType in DmgPct modsUse dmgModType instead of modType
Using HTML regex on clean textInput is already .text().trim() - no HTML tags
Parser key doesn't match factory keyKeys must match exactly: vals.dmgPct needs parser to return { dmgPct: ... }
Forgetting parser registrationAdd to SKILL_PARSERS array in index.ts
Missing factoryMust 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-40Many 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.

SkillInstallsUpdatedSafetyDifficulty
implementing-game-skill-parsers (this skill)07moReviewAdvanced
json-render-core32moNo flagsAdvanced
drizzle2382moNo flagsIntermediate
zustand1132moNo flagsIntermediate

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.

323

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.

238873

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.

113434

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

58202

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.

61191

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.

37194

Search skills

Search the agent skills registry