implementing-game-skill-parsers
ゲーム攻略サイトなどのHTMLから、ゲームのスキル情報を自動で抽出して、レベルごとの効果を解析し、ゲームの育成計画ツールに活用できるデータを作成するSkill。
📜 元の英語説明(参考)
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)
🇯🇵 日本人クリエイター向け解説
ゲーム攻略サイトなどのHTMLから、ゲームのスキル情報を自動で抽出して、レベルごとの効果を解析し、ゲームの育成計画ツールに活用できるデータを作成するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o implementing-game-skill-parsers.zip https://jpskill.com/download/9221.zip && unzip -o implementing-game-skill-parsers.zip && rm implementing-game-skill-parsers.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9221.zip -OutFile "$d\implementing-game-skill-parsers.zip"; Expand-Archive "$d\implementing-game-skill-parsers.zip" -DestinationPath $d -Force; ri "$d\implementing-game-skill-parsers.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
implementing-game-skill-parsers.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
implementing-game-skill-parsersフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
ゲームスキルパーサーの実装
概要
スキルデータ生成は、parser-factory-generation パターンに従います。
- Parser は、名前付きキー を使用して HTML/データソースから数値を抽出します。
- Factory は、それらの名前付きの値を使用して Mod オブジェクトを構築する方法を定義します。
- Generation script は、解析された値を
levelValues出力に結合します。
重要: パーサーキーは、ファクトリーキーの使用法と正確に一致する必要があります。
注: このスキルは、アクティブスキルとパッシブスキルのみを対象としています。サポートスキルについては、adding-support-mod-parsers スキルを参照してください。
使用するタイミング
- レベルスケーリングプロパティを持つ新しいアクティブスキルまたはパッシブスキルを追加する場合
- ゲームデータ HTML ページから値を抽出する場合
プロジェクトファイルの場所
| 目的 | ファイルパス |
|---|---|
| アクティブファクトリー | src/tli/skills/active-factories.ts |
| パッシブファクトリー | src/tli/skills/passive-factories.ts |
| ファクトリーの型とヘルパー | src/tli/skills/types.ts |
| アクティブパーサー | src/scripts/skills/active-parsers.ts |
| パッシブパーサー | src/scripts/skills/passive-parsers.ts |
| パーサーレジストリ | src/scripts/skills/index.ts |
| 生成スクリプト | src/scripts/generate-skill-data.ts |
| HTML データソース | .garbage/tlidb/skill/{category}/{Skill_Name}.html |
カテゴリ: active、passive、activation_medium
実装チェックリスト
1. データソースの特定
.garbage/tlidb/skill/{category}/{Skill_Name}.htmlにある HTML ファイル- Progression /40 テーブルを見つける - 列は次のとおりです: level, col0, col1, col2 (Descript)
- 列のインデックス:
values[0]= レベルの後の最初の列,values[2]= Descript - 入力はクリーンなテキストです (HTML は
buildProgressionTableInputによって既に削除されています)
2. ファクトリーの定義 (構造 + キー名)
// active-factories.ts または passive-factories.ts 内
import { v } from "./types";
"Ice Bond": (l, vals) => ({
buffMods: [
{
type: "DmgPct",
value: v(vals.coldDmgPctVsFrostbitten, l), // ここでキー名を定義します
addn: true,
dmgModType: "cold",
cond: "enemy_frostbitten",
},
],
}),
ファクトリーの戻り値の型:
- アクティブスキル:
{ offense?: SkillOffense; mods?: Mod[]; buffMods?: Mod[] } - パッシブスキル:
{ mods?: Mod[]; buffMods?: Mod[] }
SkillOffense は構造化されたインターフェースであり、配列ではありません:
interface SkillOffense {
weaponAtkDmgPct?: { value: number };
addedDmgEffPct?: { value: number };
persistentDmg?: { value: number; dmgType: DmgChunkType; duration: number };
spellDmg?: { value: DmgRange; dmgType: DmgChunkType; castTime: number };
// マルチフェーズ攻撃スキル (例: Berserking Blade)
sweepWeaponAtkDmgPct?: { value: number };
sweepAddedDmgEffPct?: { value: number };
steepWeaponAtkDmgPct?: { value: number };
steepAddedDmgEffPct?: { value: number };
}
v(arr, level) ヘルパーは、境界チェックを使用して arr[level - 1] に安全にアクセスします。
キーの命名規則:
- 記述的な camelCase 名を使用します
- コンテキストを含めます:
dmgPctPerProjectileであり、単にdmgPctではありません
3. パーサーの作成 (それらのキーの値を抽出)
// active-parsers.ts または 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;
// ヘッダーで列を検索します (部分文字列一致を使用)
const descriptCol = findColumn(progressionTable, "descript", skillName);
const coldDmgPctVsFrostbitten: Record<number, number> = {};
// 列の行を反復処理します (level → text)
for (const [levelStr, text] of Object.entries(descriptCol.rows)) {
const level = Number(levelStr);
// パターンマッチングには template() を使用します - 正規表現よりもクリーンです
const match = template("{value:dec%} additional cold damage").match(
text,
skillName,
);
coldDmgPctVsFrostbitten[level] = match.value;
}
validateAllLevels(coldDmgPctVsFrostbitten, skillName);
// ファクトリーの期待値に一致する名前付きキーを返します
return { coldDmgPctVsFrostbitten };
};
値抽出のためのテンプレート構文:
{name:int}- 整数 (例: "5" → 5){name:dec}- 10進数 (例: "21.5" → 21.5){name:dec%}- 10進数としてのパーセンテージ (例: "96%" → 96, 0.96 ではありません){name:int%}- 整数としてのパーセンテージ (例: "-30%" → -30)
定数値の場合 (すべてのレベルで同じ): createConstantLevels(value) を使用します
4. パーサーの登録
// index.ts 内
{ skillName: "Ice Bond", categories: ["active"], parser: iceBondParser }
5. 再生成と検証
pnpm exec tsx src/scripts/generate_skill_data.ts
pnpm test
生成された出力のレベル 1、20、40 をソース HTML と比較して確認します。
例: 複雑なスキル (Frost Spike)
Parser は複数の名前付きの値を抽出します。
export const frostSpikeParser: SupportLevelParser = (input) => {
const weaponAtkDmgPct: Record<number, number> = {};
const addedDmgEffPct: Record<number, number> = {};
// ... 列から抽出 ...
return {
weaponAtkDmgPct,
addedDmgEffPct,
convertPhysicalToColdPct: createConstantLevels(convertValue),
maxProjectile: createConstantLevels(maxProjValue),
projectilePerFrostbiteRating: createConstantLevels(projPerRating),
baseProjectile: createConstantLevels(baseProj),
dmgPctPerProjectile: createConstantLevels(dmgPerProj),
};
};
Factory はこれらのキーを使用します。
"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: " 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
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
HTML Source → buildProgressionTableInput (strips HTML)
→ Parser (extracts values with named keys)
→ Generation Script (converts to levelValues arrays)
→ Output TypeScript file
↓
Runtime: Factory + levelValues → Mod objects
Benefits of Named Keys
- Self-documenting:
vals.projectilePerFrostbiteRatingis clearer thanvals[4] - Order-independent: Parser and factory don't need to agree on array order
- Extensible: Adding new values doesn't shift existing indices
- Type-safe: TypeScript can catch typos in key names