jpskill.com
📦 その他 コミュニティ

add-mod-resolver

resolveModsForOffenseSkill内で、絡み合いや弱体効果、条件付きダメージボーナスといったModに基づいた戦闘メカニズムを処理するために、新しいpush*リゾルバー関数を追加するSkill。

📜 元の英語説明(参考)

Use when adding new push* resolver functions inside resolveModsForOffenseSkill to handle mod-based combat mechanics like tangles, debuffs, or conditional damage bonuses (project)

🇯🇵 日本人クリエイター向け解説

一言でいうと

resolveModsForOffenseSkill内で、絡み合いや弱体効果、条件付きダメージボーナスといったModに基づいた戦闘メカニズムを処理するために、新しいpush*リゾルバー関数を追加するSkill。

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o add-mod-resolver.zip https://jpskill.com/download/9218.zip && unzip -o add-mod-resolver.zip && rm add-mod-resolver.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/9218.zip -OutFile "$d\add-mod-resolver.zip"; Expand-Archive "$d\add-mod-resolver.zip" -DestinationPath $d -Force; ri "$d\add-mod-resolver.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して add-mod-resolver.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → add-mod-resolver フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-18
取得日時
2026-05-18
同梱ファイル
1

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

Modリゾルバーの追加

概要

Modリゾルバーは、src/tli/calcs/offense.tsresolveModsForOffenseSkill内で定義されたpush*関数です。これらは、mods配列(およびオプションでconfigprenormModsresourcePooldefenses)から読み取り、ゲームのメカニズムに基づいて新しい派生Modをプッシュします。各リゾルバーは、1つのメカニズム(例:frostbite、numbed、tangles、infiltrations)を処理します。

使用するタイミング

  • 既存のModまたは構成からModを派生させる新しい戦闘メカニズムを追加する場合
  • フラグMod(例:IsTangleWindStalker)の存在に基づいて条件付きのダメージボーナスを追加する場合
  • 敵のデバフ計算(例:numbedスタック、frostbite、frail)を追加する場合
  • 効果乗数を使用したバフ/オーラ効果の計算を追加する場合

プロジェクトファイルの場所

目的 ファイルパス
リゾルバー関数の場所 src/tli/calcs/offense.tsresolveModsForOffenseSkill内)
Modの型定義 src/tli/mod.tsModDefinitionsインターフェース)
構成インターフェースとデフォルト src/tli/core.tsConfigurationDEFAULT_CONFIGURATION
Modヘルパーユーティリティ src/tli/calcs/mod-utils.ts
テスト src/tli/calcs/offense.test.ts

アーキテクチャ

applyModFiltersは、すべてのModを次の4つのグループに前処理します。

  • modscondThresholdまたはresolvedCondのない非per Mod(すぐに使用可能)
  • prenormModsresolvedCondのないすべてのMod(スタック可能な正規化のソース)
  • condThresholdModscondThresholdのある非per Mod(しきい値が満たされたときにnormalize()によってプッシュバックされる)
  • resolvedCondModsresolvedCondのあるMod(条件が満たされたときに個々のpush*リゾルバーによってプッシュバックされる)

重要:perフィールドを持つModは、prenormModsにのみフィルタリングされます。modsには表示されません。 perフィールド(ModBaseから)は、normalize()による自動スタック可能正規化をトリガーします。Modにカスタムリゾルバーロジックが必要な場合(例:スタックでスケーリングする前に効果乗数を適用する)、Modタイプでperを使用しないでください。代わりに、スケーリング情報をカスタムフィールド(例:perFervorAmt: number)に格納して、Modがmodsに残り、リゾルバーがfilterMods()を介してそれを見つけられるようにします。

次に、resolveModsForOffenseSkillは、pm()を介して新しい派生Modをmodsにプッシュする一連のpush*リゾルバー関数を実行します。各push*関数は、以下をキャプチャするクロージャです。

  • mods: Mod[] — 解決されたModの共有可変配列
  • prenormMods — スタック可能な正規化が必要なMod
  • condThresholdMods — スタック可能なしきい値がnormalize()によって評価されるまで保留されるMod
  • resolvedCondMods — 解決された条件がpush*リゾルバーによって評価されるまで保留されるMod
  • config: Configuration — ユーザー構成の戦闘パラメータ
  • resourcePool — ステータス、マナ、祝福など
  • defenses — アーマー、回避、耐性など
  • loadout: Loadout — 完全に解析されたロードアウト
  • ヘルパー関数:pm()(Modのプッシュ)、normalize()(スタック可能の正規化)、step()(依存関係の追跡)

実行シーケンスは、normalizeFromConfig()から始まります。これは、値が純粋にconfigフィールド(例:levelnum_enemies_nearbyenemy_numbed_stacks)から来るすべてのスタック可能を正規化します。これは、pushStatNorms()および他のすべてのリゾルバーの前に実行されます。値がMod、ステータス、防御、またはresourcePoolに依存する正規化呼び出しは、それぞれのpush*関数内または実行シーケンスのインラインに残ります。

利用可能なヘルパー

クロージャから(resolveModsForOffenseSkillで定義):

  • pm(...ms: Mod[])mods.push(...ms)の省略形
  • normalize(stackable, value)prenormModsからスタック可能なModを正規化し、満たされたcondThresholdModsをプッシュします
  • normalizeFromConfig() — 値が純粋にconfigフィールドから来るすべてのスタック可能に対してnormalize()を呼び出します。pushStatNorms()の前の実行シーケンスの開始時に一度呼び出されます
  • step(stepName) — 依存関係の追跡のためにステップを登録します(他のステップがこれに依存する場合にのみ必要です)
  • resolvedCondMods: Mod[]resolvedCondを持つMod。applyModFiltersによって分離されます。条件が満たされたときにpm()を介して一致するものをmodsにプッシュします
  • condThresholdMods: Mod[]condThresholdを持つ非per Mod。applyModFiltersによって分離されます。スタック可能なしきい値が満たされたときにnormalize()によって自動的にプッシュバックされます

src/tli/calcs/mod-utils.tsから:

  • modExists(mods, "ModType")booleanを返し、そのタイプのModが存在するかどうかを確認します
  • findMod(mods, "ModType") — タイプの最初のModまたはundefinedを返します
  • filterMods(mods, "ModType") — タイプのすべてのModをModT<T>[]として返します
  • sumByValue(mods) — 配列内のすべてのModの.valueを合計します
  • calcEffMult(mods, "ModType")(1 + sum_of_inc) * product_of_addn乗数を計算します
  • multModValue(mod, multiplier).valueが乗算された新しいModを返します

実装チェックリスト

1. トリガーModタイプが存在することを確認する

ModDefinitionssrc/tli/mod.tsを確認してください。フラグMod(例:IsTangle)が存在しない場合は、次のように追加します。

// In ModDefinitions
IsTangle: object;  // flag mod, no fields

データを持つModの場合:

NumbedEffPct: { value: number };

2. 構成フィールドが存在することを確認する(必要な場合)

リゾルバーがユーザー構成可能な値を必要とする場合は、src/tli/core.tsにそれらが存在することを確認してください。存在しない場合は、/add-configurationスキルを使用してください。

3. push*関数を作成する

関連するリゾルバーの近くのresolveModsForOffenseSkill内で関数を定義します。次のパターンに従ってください。

単純なフラグベースのリゾルバー(ダメージを構成値で乗算します):

const pushTangle = (): void => {
  if (!modExists(mods, "IsTangle") || config.numActiveTangles <= 1) return;
  mods.push({
    type: "DmgPct",
    dmgModType: "global",
    addn: true,
    value: (config.numActiveTangles - 1) * 100,
    src: "Tangle",
  });
};

効果乗数を持つデバフ:

const pushNumbed = (): void => {
  if (!config.enemyNumbed) return;
  const numbedStacks = config.enemyNumbedStac
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

Adding Mod Resolvers

Overview

Mod resolvers are push* functions defined inside resolveModsForOffenseSkill in src/tli/calcs/offense.ts. They read from the mods array (and optionally config, prenormMods, resourcePool, defenses) and push new derived mods based on game mechanics. Each resolver handles one mechanic (e.g., frostbite, numbed, tangles, infiltrations).

When to Use

  • Adding a new combat mechanic that derives mods from existing mods or configuration
  • Adding conditional damage bonuses based on presence of a flag mod (e.g., IsTangle, WindStalker)
  • Adding enemy debuff calculations (e.g., numbed stacks, frostbite, frail)
  • Adding buff/aura effect calculations with effect multipliers

Project File Locations

Purpose File Path
Resolver function location src/tli/calcs/offense.ts (inside resolveModsForOffenseSkill)
Mod type definitions src/tli/mod.ts (ModDefinitions interface)
Configuration interface & defaults src/tli/core.ts (Configuration, DEFAULT_CONFIGURATION)
Mod helper utilities src/tli/calcs/mod-utils.ts
Tests src/tli/calcs/offense.test.ts

Architecture

applyModFilters preprocesses all mods into four groups:

  • mods — non-per mods without condThreshold or resolvedCond (ready to use immediately)
  • prenormMods — all mods without resolvedCond (source for per-stackable normalization)
  • condThresholdMods — non-per mods with condThreshold (pushed back by normalize() when threshold is met)
  • resolvedCondMods — mods with resolvedCond (pushed back by individual push* resolvers when condition is met)

IMPORTANT: Mods with a per field are filtered into prenormMods only — they do NOT appear in mods. The per field (from ModBase) triggers automatic per-stackable normalization via normalize(). If a mod needs custom resolver logic (e.g., applying an effect multiplier before scaling by stacks), do NOT use per on the mod type. Instead, store the scaling info in a custom field (e.g., perFervorAmt: number) so the mod stays in mods and the resolver can find it via filterMods().

resolveModsForOffenseSkill then runs a sequence of push* resolver functions that push new derived mods into mods via pm(). Each push* function is a closure that captures:

  • mods: Mod[] — the shared mutable array of resolved mods
  • prenormMods — mods needing per-stackable normalization
  • condThresholdMods — mods held back until their stackable threshold is evaluated by normalize()
  • resolvedCondMods — mods held back until their resolved condition is evaluated by a push* resolver
  • config: Configuration — user-configured combat parameters
  • resourcePool — stats, mana, blessings, etc.
  • defenses — armor, evasion, resistances, etc.
  • loadout: Loadout — full parsed loadout
  • Helper functions: pm() (push mods), normalize() (normalize stackables), step() (dependency tracking)

The execution sequence begins with normalizeFromConfig(), which normalizes all stackables whose values come purely from config fields (e.g., level, num_enemies_nearby, enemy_numbed_stacks). This runs before pushStatNorms() and all other resolvers. Normalize calls whose values depend on mods, stats, defenses, or resourcePool remain in their respective push* functions or inline in the execution sequence.

Available Helpers

From closure (defined in resolveModsForOffenseSkill):

  • pm(...ms: Mod[]) — shorthand for mods.push(...ms)
  • normalize(stackable, value) — normalizes per-stackable mods from prenormMods and pushes satisfied condThresholdMods for that stackable
  • normalizeFromConfig() — calls normalize() for all stackables whose values come purely from config fields; called once at the start of the execution sequence before pushStatNorms()
  • step(stepName) — registers a step for dependency tracking (only needed if other steps depend on this one)
  • resolvedCondMods: Mod[] — mods with resolvedCond, separated out by applyModFilters; push matching ones into mods via pm() when the condition is met
  • condThresholdMods: Mod[] — non-per mods with condThreshold, separated out by applyModFilters; pushed back automatically by normalize() when their stackable threshold is met

From src/tli/calcs/mod-utils.ts:

  • modExists(mods, "ModType") — returns boolean, checks if any mod of that type exists
  • findMod(mods, "ModType") — returns first mod of type or undefined
  • filterMods(mods, "ModType") — returns all mods of type as ModT<T>[]
  • sumByValue(mods) — sums .value of all mods in array
  • calcEffMult(mods, "ModType") — calculates (1 + sum_of_inc) * product_of_addn multiplier
  • multModValue(mod, multiplier) — returns new mod with .value multiplied

Implementation Checklist

1. Ensure the Trigger Mod Type Exists

Check src/tli/mod.ts under ModDefinitions. If the flag mod (e.g., IsTangle) doesn't exist, add it:

// In ModDefinitions
IsTangle: object;  // flag mod, no fields

For mods with data:

NumbedEffPct: { value: number };

2. Ensure Configuration Fields Exist (if needed)

If the resolver needs user-configurable values, ensure they exist in src/tli/core.ts. Use the /add-configuration skill if they don't.

3. Write the push* Function

Define the function inside resolveModsForOffenseSkill, near related resolvers. Follow these patterns:

Simple flag-based resolver (multiplies damage by a config value):

const pushTangle = (): void => {
  if (!modExists(mods, "IsTangle") || config.numActiveTangles <= 1) return;
  mods.push({
    type: "DmgPct",
    dmgModType: "global",
    addn: true,
    value: (config.numActiveTangles - 1) * 100,
    src: "Tangle",
  });
};

Debuff with effect multiplier:

const pushNumbed = (): void => {
  if (!config.enemyNumbed) return;
  const numbedStacks = config.enemyNumbedStacks ?? 10;
  const numbedEffMult = calcEffMult(mods, "NumbedEffPct");
  const baseValPerStack = 5;
  const numbedVal = baseValPerStack * numbedEffMult * numbedStacks;
  mods.push({
    type: "DmgPct",
    value: numbedVal,
    dmgModType: "lightning",
    addn: true,
    isEnemyDebuff: true,
    src: "Numbed",
  });
};

Buff with effect multiplier (e.g., aggression, mark):

const pushMark = (): void => {
  if (!config.targetEnemyMarked) return;
  const markEffMult = calcEffMult(mods, "MarkEffPct");
  const baseValue = 20;
  mods.push({
    type: "CritDmgPct",
    value: baseValue * markEffMult,
    addn: true,
    modType: "global",
    isEnemyDebuff: true,
    src: "Mark",
  });
};

Resolver with per-stackable normalization:

const pushPactspirits = () => {
  const addedMaxStacks = sumByValue(filterMods(mods, "MaxPureHeartStacks"));
  const maxStacks = 5 + addedMaxStacks;
  const stacks = config.pureHeartStacks ?? maxStacks;
  normalize("pure_heart", stacks);
};

Config-only normalization (add to normalizeFromConfig):

If the stackable value comes purely from config fields (no dependency on mods, stats, defenses, or resourcePool), add the normalize() call inside normalizeFromConfig() instead of creating a separate push* function or placing it inline in the execution sequence:

const normalizeFromConfig = (): void => {
  // ... existing config-based normalizes ...
  normalize("new_stackable", config.newStackableValue ?? defaultValue);
};

Only use a separate push* function or inline normalize() when the value depends on computed data (mods, stats, etc.), or when the value has a config override with a mod-computed fallback (e.g., config.stacks ?? maxStacksFromMods).

Resolved condition resolver (conditions that depend on calculated values):

Some mod conditions can't be evaluated statically from configuration — they depend on values calculated earlier in resolveModsForOffenseSkill (e.g., sealed mana/life percentages come from resourcePool.sealedResources, not config). These use resolvedCond on the mod (see ResolvedCondition in mod.ts) instead of cond (which is for static Configuration-based conditions evaluated in filterModsByCond).

Mods with resolvedCond are separated out by applyModFilters into resolvedCondMods. The push* resolver filters for its condition and pushes matching mods into mods via pm() when the condition is met.

const pushHasSealedLifeAndManaCond = (): void => {
  const { sealedManaPct, sealedLifePct } = resourcePool.sealedResources;
  if (sealedManaPct <= 0 || sealedLifePct <= 0) return;
  pm(
    ...resolvedCondMods.filter(
      (m) => m.resolvedCond === "have_both_sealed_mana_and_life",
    ),
  );
};

To add a new resolved condition:

  1. Add the condition string to ResolvedConditions in src/tli/mod.ts
  2. In the mod parser template (src/tli/mod-parser/templates.ts), use resolvedCond: "condition_name" instead of cond: "condition_name"
  3. Write a push* resolver that filters resolvedCondMods and pushes matching mods via pm(), and call it at the appropriate point in the execution sequence

Resolver with step dependencies (when one resolver produces mods consumed by another):

Use step() and stepDeps whenever a resolver pushes mods that another resolver later reads. For example, pushFervor generates SkillAreaPct mods, so pushSkillArea depends on it. The dependency graph is validated at test time — if pushSkillArea runs before pushFervor, an error is recorded.

  1. Register both steps and their dependency in stepDeps (above resolveModsForOffenseSkill):

    const stepDeps = createSelfReferential({
    // ... existing steps ...
    fervor: [],
    skillArea: ["fervor"],  // skillArea must run after fervor
    });
  2. Call step() at the top of each resolver:

    
    const pushFervor = () => {
    step("fervor");
    if (resourcePool.hasFervor) {
     mods.push(calculateFervorCritRateMod(mods, resourcePool));
     mods.push(...calculateFervorBaseEffSkillArea(mods, resourcePool));
     normalize("fervor", resourcePool.fervorPts);
    }
    };

const pushSkillArea = (): void => { step("skillArea"); const skillAreaPct = sumByValue(filterMods(mods, "SkillAreaPct")); normalize("skill_area", skillAreaPct); };


3. Ensure the call order in the execution sequence matches the dependency graph (dependent runs after dependency):
```typescript
pushFervor();    // must come first
pushSkillArea(); // depends on fervor

step() always goes at the top of the resolver, before any early returns, so the step is registered even if the resolver short-circuits.

4. Call the Function

Add the call in the execution sequence inside resolveModsForOffenseSkill. Place it near related mechanics.

5. Verify

pnpm test
pnpm typecheck
pnpm check

Common Patterns

Pattern When to Use Key Helper
Check flag mod exists Mechanic only applies when a specific support/skill mod is present modExists(mods, "FlagMod")
Check config boolean Mechanic depends on user toggle if (!config.someToggle) return
Effect multiplier Buff/debuff has mods that scale its effectiveness calcEffMult(mods, "SomeEffPct")
Config stacks with default User can override stack count, defaults to max config.someStacks ?? maxStacks
Normalize stackable Mechanic involves per-stackable scaling with computed value normalize("stackable_name", value)
Config-only normalize Stackable value comes purely from config Add to normalizeFromConfig()
Filter by resolved condition Condition depends on calculated values, not static config pm(...resolvedCondMods.filter(...))
addn: true on DmgPct More multiplier (multiplicative with other addn: true mods)
addn: false on DmgPct Increased multiplier (additive with other addn: false mods)
isEnemyDebuff: true Damage increase from enemy debuff (for display grouping)
src: "Name" Label for debug/display panel

DmgPct addn Field

The addn (additional) field on DmgPct controls how the damage bonus stacks:

  • addn: falseIncreased damage. All addn: false mods sum together into one multiplier: (1 + sum).
  • addn: trueMore damage. Each addn: true mod is its own separate multiplier: (1 + value1) * (1 + value2) * ...

Most resolvers use addn: true because their effects are multiplicative with other damage sources.

Common Mistakes

Mistake Fix
Forgetting early return when flag/config is absent Always guard with if (!condition) return
Using addn: false when the mechanic should be multiplicative Use addn: true for separate "more" multipliers
Pushing mods without src Always include src for debug panel visibility
Forgetting to add config field Use /add-configuration skill first
Missing step() when a resolver produces mods consumed by another Add both steps to stepDeps with the dependency, and call step() at the top of each resolver
Not matching execution order to stepDeps The call order must satisfy the dependency graph — dependent resolvers run after their dependencies
Not handling undefined config with ?? Optional config values need fallback: config.stacks ?? defaultMax
Placing config-only normalize inline in execution sequence Add to normalizeFromConfig() instead; only use inline/push* for computed values
Using per on a mod that needs custom resolver logic Mods with per go to prenormMods, not mods, so filterMods(mods, ...) won't find them. Use a custom field (e.g., perFervorAmt: number) instead so the mod stays in mods for the resolver to read