┌─ nix/apps/simulation/mkMassSim.nix ────────────────────────────────────────┐
│ diff --git a/nix/apps/simulation/mkMassSim.nix b/nix/apps/simulation/mkMassSim.nix │
│ index 9eb3905..b1e8a1c 100644 │
│ --- a/nix/apps/simulation/mkMassSim.nix │
│ +++ b/nix/apps/simulation/mkMassSim.nix │
│ @@ -10,6 +10,7 @@ │
│ }: let │
│ # TODO: this is cursed, consolidate the functions. │
│ inherit (lib.sim.simulation) mkSim; │
│ + inherit (lib.sim) itemDatabase; │
│ │
│ # extract playable races from class definitions │
│ getPlayableRaces = className: │
│ @@ -101,6 +102,11 @@ │
│ simDerivations = lib.listToAttrs (map (raceConfig: { │
│ name = "${raceConfig.className}-${raceConfig.specName}-${raceConfig.raceN │
│ ame}"; │
│ value = let │
│ + # Pre-enrich the equipment, consumables, and glyphs for this race confi │
│ g │
│ + enrichedEquipment = itemDatabase.enrichEquipment raceConfig.config.equi │
│ pment; │
│ + enrichedConsumables = itemDatabase.enrichConsumables raceConfig.config. │
│ consumables; │
│ + enrichedGlyphs = itemDatabase.enrichGlyphs raceConfig.config.glyphs; │
│ + │
│ simInput = mkSim { │
│ inherit iterations; │
│ player = raceConfig.config; │
│ @@ -113,9 +119,22 @@ │
│ buildInputs = [pkgs.jq]; │
│ nativeBuildInputs = [inputs.wowsims.packages.${pkgs.system}.wowsimcli │
│ ]; │
│ } '' │
│ - cat > input.json << 'EOF' │
│ - ${simInput} │
│ - EOF │
│ + # Write enriched JSON data to files using cat with EOF to handle any │
│ quotes safely │
│ + cat > enriched_equipment.json << 'EQUIPMENT_EOF' │
│ +${builtins.toJSON enrichedEquipment} │
│ +EQUIPMENT_EOF │
│ + │
│ + cat > consumables.json << 'CONSUMABLES_EOF' │
│ +${builtins.toJSON enrichedConsumables} │
│ +CONSUMABLES_EOF │
│ + │
│ + cat > glyphs.json << 'GLYPHS_EOF' │
│ +${builtins.toJSON enrichedGlyphs} │
│ +GLYPHS_EOF │
│ + │
│ + cat > input.json << 'INPUT_EOF' │
│ +${simInput} │
│ +INPUT_EOF │
│ │
│ echo "Running ${raceConfig.className}/${raceConfig.specName}/${raceCo │
│ nfig.raceName} simulation..." │
│ if wowsimcli sim --infile input.json --outfile output.json; then │
│ @@ -125,33 +144,37 @@ │
│ minDps=$(jq -r '.raidMetrics.dps.min // 0' output.json) │
│ stdevDps=$(jq -r '.raidMetrics.dps.stdev // 0' output.json) │
│ │
│ - # construct loadout info │
│ - loadout=$(echo '${builtins.toJSON raceConfig.config}' | jq '{ │
│ - consumables, │
│ - talentsString, │
│ - glyphs, │
│ - equipment, │
│ - race, │
│ - class, │
│ - profession1, │
│ - profession2 │
│ - }') │
│ - │
│ - # create final result │
│ + # create final result with enriched data │
│ jq -n \ │
│ --arg raceName "${raceConfig.raceName}" \ │
│ --arg avgDps "$avgDps" \ │
│ --arg maxDps "$maxDps" \ │
│ --arg minDps "$minDps" \ │
│ --arg stdevDps "$stdevDps" \ │
│ - --argjson loadout "$loadout" \ │
│ + --slurpfile equipment enriched_equipment.json \ │
│ + --slurpfile consumables consumables.json \ │
│ + --arg talentsString "${raceConfig.config.talentsString}" \ │
│ + --slurpfile glyphs glyphs.json \ │
│ + --arg race "${raceConfig.config.race}" \ │
│ + --arg class "${raceConfig.config.class}" \ │
│ + --arg profession1 "${raceConfig.config.profession1}" \ │
│ + --arg profession2 "${raceConfig.config.profession2}" \ │
│ '{ │
│ race: $raceName, │
│ dps: ($avgDps | tonumber), │
│ max: ($maxDps | tonumber), │
│ min: ($minDps | tonumber), │
│ stdev: ($stdevDps | tonumber), │
│ - loadout: $loadout │
│ + loadout: { │
│ + consumables: $consumables[0], │
│ + talentsString: $talentsString, │
│ + glyphs: $glyphs[0], │
│ + equipment: $equipment[0], │
│ + race: $race, │
│ + class: $class, │
│ + profession1: $profession1, │
│ + profession2: $profession2 │
│ + } │
│ }' > $out │
│ else │
│ echo "Simulation failed for ${raceConfig.className}/${raceConfig.sp │
│ ecName}/${raceConfig.raceName}" │
│ @@ -301,6 +324,11 @@ │
│ simDerivations = lib.listToAttrs (map (spec: { │
│ name = "${spec.className}-${spec.specName}"; │
│ value = let │
│ + # Pre-enrich the equipment, consumables, and glyphs for this spec │
│ + enrichedEquipment = itemDatabase.enrichEquipment spec.config.equipment; │
│ + enrichedConsumables = itemDatabase.enrichConsumables spec.config.consum │
│ ables; │
│ + enrichedGlyphs = itemDatabase.enrichGlyphs spec.config.glyphs; │
│ + │
│ simInput = mkSim { │
│ inherit iterations; │
│ player = spec.config; │
│ @@ -313,10 +341,22 @@ │
│ buildInputs = [pkgs.jq]; │
│ nativeBuildInputs = [inputs.wowsims.packages.${pkgs.system}.wowsimcli │
│ ]; │
│ } '' │
│ - # Generate input JSON file using HERE document approach (same as test │
│ -composition) │
│ - cat > input.json << 'EOF' │
│ - ${simInput} │
│ - EOF │
│ + # Write enriched JSON data to files using cat with EOF to handle any │
│ quotes safely │
│ + cat > enriched_equipment.json << 'EQUIPMENT_EOF' │
│ +${builtins.toJSON enrichedEquipment} │
│ +EQUIPMENT_EOF │
│ + │
│ + cat > consumables.json << 'CONSUMABLES_EOF' │
│ +${builtins.toJSON enrichedConsumables} │
│ +CONSUMABLES_EOF │
│ + │
│ + cat > glyphs.json << 'GLYPHS_EOF' │
│ +${builtins.toJSON enrichedGlyphs} │
│ +GLYPHS_EOF │
│ + │
│ + cat > input.json << 'INPUT_EOF' │
│ +${simInput} │
│ +INPUT_EOF │
│ │
│ echo "Running ${spec.className}/${spec.specName} simulation..." │
│ if wowsimcli sim --infile input.json --outfile output.json; then │
│ @@ -326,20 +366,7 @@ │
│ minDps=$(jq -r '.raidMetrics.dps.min // 0' output.json) │
│ stdevDps=$(jq -r '.raidMetrics.dps.stdev // 0' output.json) │
│ │
│ - # create loadout without rotation (only keep consumables, talents, │
│ glyphs, gear) │
│ - # TODO: should we output the apl? │
│ - loadout=$(echo '${builtins.toJSON spec.config}' | jq '{ │
│ - consumables, │
│ - talentsString, │
│ - glyphs, │
│ - equipment, │
│ - race, │
│ - class, │
│ - profession1, │
│ - profession2 │
│ - }') │
│ - │
│ - # create final result with all DPS statistics │
│ + # create final result with all DPS statistics and enriched data │
│ jq -n \ │
│ --arg className "${spec.className}" \ │
│ --arg specName "${spec.specName}" \ │
│ @@ -347,7 +374,14 @@ │
│ --arg maxDps "$maxDps" \ │
│ --arg minDps "$minDps" \ │
│ --arg stdevDps "$stdevDps" \ │
│ - --argjson loadout "$loadout" \ │
│ + --slurpfile equipment enriched_equipment.json \ │
│ + --slurpfile consumables consumables.json \ │
│ + --arg talentsString "${spec.config.talentsString}" \ │
│ + --slurpfile glyphs glyphs.json \ │
│ + --arg race "${spec.config.race}" \ │
│ + --arg class "${spec.config.class}" \ │
│ + --arg profession1 "${spec.config.profession1}" \ │
│ + --arg profession2 "${spec.config.profession2}" \ │
│ '{ │
│ className: $className, │
│ specName: $specName, │
│ @@ -355,7 +389,16 @@ │
│ max: ($maxDps | tonumber), │
│ min: ($minDps | tonumber), │
│ stdev: ($stdevDps | tonumber), │
│ - loadout: $loadout │
│ + loadout: { │
│ + consumables: $consumables[0], │
│ + talentsString: $talentsString, │
│ + glyphs: $glyphs[0], │
│ + equipment: $equipment[0], │
│ + race: $race, │
│ + class: $class, │
│ + profession1: $profession1, │
│ + profession2: $profession2 │
│ + } │
│ }' > $out │
│ else │
│ echo "Simulation failed for ${spec.className}/${spec.specName}" │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...apps/simulation/mkMassSim.nix ───┐
│ diff --git a/nix/apps/simulation/mkMassSim.n │
│ ix b/nix/apps/simulation/mkMassSim.nix │
│ index 9eb3905..b1e8a1c 100644 │
│ --- a/nix/apps/simulation/mkMassSim.nix │
│ +++ b/nix/apps/simulation/mkMassSim.nix │
│ @@ -10,6 +10,7 @@ │
│ }: let │
│ # TODO: this is cursed, consolidate the f │
│ unctions. │
│ inherit (lib.sim.simulation) mkSim; │
│ + inherit (lib.sim) itemDatabase; │
│ │
│ # extract playable races from class defin │
│ itions │
│ getPlayableRaces = className: │
│ @@ -101,6 +102,11 @@ │
│ simDerivations = lib.listToAttrs (map ( │
│ raceConfig: { │
│ name = "${raceConfig.className}-${r │
│ aceConfig.specName}-${raceConfig.raceName}"; │
│ value = let │
│ + # Pre-enrich the equipment, consu │
│ mables, and glyphs for this race config │
│ + enrichedEquipment = itemDatabase. │
│ enrichEquipment raceConfig.config.equipment; │
│ + enrichedConsumables = itemDatabas │
│ e.enrichConsumables raceConfig.config.consum │
│ ables; │
│ + enrichedGlyphs = itemDatabase.enr │
│ ichGlyphs raceConfig.config.glyphs; │
│ + │
│ simInput = mkSim { │
│ inherit iterations; │
│ player = raceConfig.config; │
│ @@ -113,9 +119,22 @@ │
│ buildInputs = [pkgs.jq]; │
│ nativeBuildInputs = [inputs.wow │
│ sims.packages.${pkgs.system}.wowsimcli]; │
│ } '' │
│ - cat > input.json << 'EOF' │
│ - ${simInput} │
│ - EOF │
│ + # Write enriched JSON data to f │
│ iles using cat with EOF to handle any quotes │
│ safely │
│ + cat > enriched_equipment.json < │
│ < 'EQUIPMENT_EOF' │
│ +${builtins.toJSON enrichedEquipment} │
│ +EQUIPMENT_EOF │
│ + │
│ + cat > consumables.json << 'CONS │
│ UMABLES_EOF' │
│ +${builtins.toJSON enrichedConsumables} │
│ +CONSUMABLES_EOF │
│ + │
│ + cat > glyphs.json << 'GLYPHS_EO │
│ F' │
│ +${builtins.toJSON enrichedGlyphs} │
│ +GLYPHS_EOF │
│ + │
│ + cat > input.json << 'INPUT_EOF' │
│ +${simInput} │
│ +INPUT_EOF │
│ │
│ echo "Running ${raceConfig.clas │
│ sName}/${raceConfig.specName}/${raceConfig.r │
│ aceName} simulation..." │
│ if wowsimcli sim --infile input │
│ .json --outfile output.json; then │
│ @@ -125,33 +144,37 @@ │
│ minDps=$(jq -r '.raidMetrics. │
│ dps.min // 0' output.json) │
│ stdevDps=$(jq -r '.raidMetric │
│ s.dps.stdev // 0' output.json) │
│ │
│ - # construct loadout info │
│ - loadout=$(echo '${builtins.to │
│ JSON raceConfig.config}' | jq '{ │
│ - consumables, │
│ - talentsString, │
│ - glyphs, │
│ - equipment, │
│ - race, │
│ - class, │
│ - profession1, │
│ - profession2 │
│ - }') │
│ - │
│ - # create final result │
│ + # create final result with en │
│ riched data │
│ jq -n \ │
│ --arg raceName "${raceConfi │
│ g.raceName}" \ │
│ --arg avgDps "$avgDps" \ │
│ --arg maxDps "$maxDps" \ │
│ --arg minDps "$minDps" \ │
│ --arg stdevDps "$stdevDps" │
│ \ │
│ - --argjson loadout "$loadout │
│ " \ │
│ + --slurpfile equipment enric │
│ hed_equipment.json \ │
│ + --slurpfile consumables con │
│ sumables.json \ │
│ + --arg talentsString "${race │
│ Config.config.talentsString}" \ │
│ + --slurpfile glyphs glyphs.j │
│ son \ │
│ + --arg race "${raceConfig.co │
│ nfig.race}" \ │
│ + --arg class "${raceConfig.c │
│ onfig.class}" \ │
│ + --arg profession1 "${raceCo │
│ nfig.config.profession1}" \ │
│ + --arg profession2 "${raceCo │
│ nfig.config.profession2}" \ │
│ '{ │
│ race: $raceName, │
│ dps: ($avgDps | tonumber) │
│ , │
│ max: ($maxDps | tonumber) │
│ , │
│ min: ($minDps | tonumber) │
│ , │
│ stdev: ($stdevDps | tonum │
│ ber), │
│ - loadout: $loadout │
│ + loadout: { │
│ + consumables: $consumabl │
│ es[0], │
│ + talentsString: $talents │
│ String, │
│ + glyphs: $glyphs[0], │
│ + equipment: $equipment[0 │
│ ], │
│ + race: $race, │
│ + class: $class, │
│ + profession1: $professio │
│ n1, │
│ + profession2: $professio │
│ n2 │
│ + } │
│ }' > $out │
│ else │
│ echo "Simulation failed for $ │
│ {raceConfig.className}/${raceConfig.specName │
│ }/${raceConfig.raceName}" │
│ @@ -301,6 +324,11 @@ │
│ simDerivations = lib.listToAttrs (map ( │
│ spec: { │
│ name = "${spec.className}-${spec.sp │
│ ecName}"; │
│ value = let │
│ + # Pre-enrich the equipment, consu │
│ mables, and glyphs for this spec │
│ + enrichedEquipment = itemDatabase. │
│ enrichEquipment spec.config.equipment; │
│ + enrichedConsumables = itemDatabas │
│ e.enrichConsumables spec.config.consumables; │
│ + enrichedGlyphs = itemDatabase.enr │
│ ichGlyphs spec.config.glyphs; │
│ + │
│ simInput = mkSim { │
│ inherit iterations; │
│ player = spec.config; │
│ @@ -313,10 +341,22 @@ │
│ buildInputs = [pkgs.jq]; │
│ nativeBuildInputs = [inputs.wow │
│ sims.packages.${pkgs.system}.wowsimcli]; │
│ } '' │
│ - # Generate input JSON file usin │
│ g HERE document approach (same as test-compo │
│ sition) │
│ - cat > input.json << 'EOF' │
│ - ${simInput} │
│ - EOF │
│ + # Write enriched JSON data to f │
│ iles using cat with EOF to handle any quotes │
│ safely │
│ + cat > enriched_equipment.json < │
│ < 'EQUIPMENT_EOF' │
│ +${builtins.toJSON enrichedEquipment} │
│ +EQUIPMENT_EOF │
│ + │
│ + cat > consumables.json << 'CONS │
│ UMABLES_EOF' │
│ +${builtins.toJSON enrichedConsumables} │
│ +CONSUMABLES_EOF │
│ + │
│ + cat > glyphs.json << 'GLYPHS_EO │
│ F' │
│ +${builtins.toJSON enrichedGlyphs} │
│ +GLYPHS_EOF │
│ + │
│ + cat > input.json << 'INPUT_EOF' │
│ +${simInput} │
│ +INPUT_EOF │
│ │
│ echo "Running ${spec.className} │
│ /${spec.specName} simulation..." │
│ if wowsimcli sim --infile input │
│ .json --outfile output.json; then │
│ @@ -326,20 +366,7 @@ │
│ minDps=$(jq -r '.raidMetrics. │
│ dps.min // 0' output.json) │
│ stdevDps=$(jq -r '.raidMetric │
│ s.dps.stdev // 0' output.json) │
│ │
│ - # create loadout without rota │
│ tion (only keep consumables, talents, glyphs │
│ , gear) │
│ - # TODO: should we output the │
│ apl? │
│ - loadout=$(echo '${builtins.to │
│ JSON spec.config}' | jq '{ │
│ - consumables, │
│ - talentsString, │
│ - glyphs, │
│ - equipment, │
│ - race, │
│ - class, │
│ - profession1, │
│ - profession2 │
│ - }') │
│ - │
│ - # create final result with al │
│ l DPS statistics │
│ + # create final result with al │
│ l DPS statistics and enriched data │
│ jq -n \ │
│ --arg className "${spec.cla │
│ ssName}" \ │
│ --arg specName "${spec.spec │
│ Name}" \ │
│ @@ -347,7 +374,14 @@ │
│ --arg maxDps "$maxDps" \ │
│ --arg minDps "$minDps" \ │
│ --arg stdevDps "$stdevDps" │
│ \ │
│ - --argjson loadout "$loadout │
│ " \ │
│ + --slurpfile equipment enric │
│ hed_equipment.json \ │
│ + --slurpfile consumables con │
│ sumables.json \ │
│ + --arg talentsString "${spec │
│ .config.talentsString}" \ │
│ + --slurpfile glyphs glyphs.j │
│ son \ │
│ + --arg race "${spec.config.r │
│ ace}" \ │
│ + --arg class "${spec.config. │
│ class}" \ │
│ + --arg profession1 "${spec.c │
│ onfig.profession1}" \ │
│ + --arg profession2 "${spec.c │
│ onfig.profession2}" \ │
│ '{ │
│ className: $className, │
│ specName: $specName, │
│ @@ -355,7 +389,16 @@ │
│ max: ($maxDps | tonumber) │
│ , │
│ min: ($minDps | tonumber) │
│ , │
│ stdev: ($stdevDps | tonum │
│ ber), │
│ - loadout: $loadout │
│ + loadout: { │
│ + consumables: $consumabl │
│ es[0], │
│ + talentsString: $talents │
│ String, │
│ + glyphs: $glyphs[0], │
│ + equipment: $equipment[0 │
│ ], │
│ + race: $race, │
│ + class: $class, │
│ + profession1: $professio │
│ n1, │
│ + profession2: $professio │
│ n2 │
│ + } │
│ }' > $out │
│ else │
│ echo "Simulation failed for $ │
│ {spec.className}/${spec.specName}" │
└──────────────────────────────────────────────┘
┌─ nix/lib/itemDatabase.nix ─────────────────────────────────────────────────┐
│ diff --git a/nix/lib/itemDatabase.nix b/nix/lib/itemDatabase.nix │
│ new file mode 100644 │
│ index 0000000..b0b9b4f │
│ --- /dev/null │
│ +++ b/nix/lib/itemDatabase.nix │
│ @@ -0,0 +1,486 @@ │
│ +{ │
│ + inputs, │
│ + lib, │
│ + ... │
│ +}: let │
│ + wowsimsDb = lib.importJSON "${inputs.wowsims}/assets/database/db.json"; │
│ + │
│ + itemsById = lib.listToAttrs (map (item: { │
│ + name = toString item.id; │
│ + value = item; │
│ + }) │
│ + wowsimsDb.items); │
│ + │
│ + gemsById = lib.listToAttrs (map (gem: { │
│ + name = toString gem.id; │
│ + value = gem; │
│ + }) │
│ + wowsimsDb.gems); │
│ + │
│ + glyphsById = lib.listToAttrs (lib.imap0 (index: glyph: { │
│ + name = toString index; │
│ + value = glyph; │
│ + }) │
│ + wowsimsDb.glyphIds); │
│ + │
│ + enchantsByEffectId = lib.listToAttrs (map (enchant: { │
│ + name = toString enchant.effectId; │
│ + value = enchant; │
│ + }) │
│ + wowsimsDb.enchants); │
│ + │
│ + reforgeData = { │
│ + "113" = { │
│ + "i1" = 6; │
│ + "s1" = "spi"; │
│ + "i2" = 13; │
│ + "s2" = "dodgertng"; │
│ + }; │
│ + "138" = { │
│ + "i1" = 31; │
│ + "s1" = "hitrtng"; │
│ + "i2" = 36; │
│ + "s2" = "hastertng"; │
│ + }; │
│ + "158" = { │
│ + "i1" = 37; │
│ + "s1" = "exprtng"; │
│ + "i2" = 31; │
│ + "s2" = "hitrtng"; │
│ + }; │
│ + "159" = { │
│ + "i1" = 37; │
│ + "s1" = "exprtng"; │
│ + "i2" = 32; │
│ + "s2" = "critstrkrtng"; │
│ + }; │
│ + "166" = { │
│ + "i1" = 49; │
│ + "s1" = "mastrtng"; │
│ + "i2" = 32; │
│ + "s2" = "critstrkrtng"; │
│ + }; │
│ + "167" = { │
│ + "i1" = 49; │
│ + "s1" = "mastrtng"; │
│ + "i2" = 36; │
│ + "s2" = "hastertng"; │
│ + }; │
│ + "168" = { │
│ + "i1" = 49; │
│ + "s1" = "mastrtng"; │
│ + "i2" = 37; │
│ + "s2" = "exprtng"; │
│ + }; │
│ + }; │
│ + │
│ + # stat name mappings (for reforging) │
│ + statNames = { │
│ + "spi" = "Spirit"; │
│ + "dodgertng" = "Dodge"; │
│ + "parryrtng" = "Parry"; │
│ + "hitrtng" = "Hit"; │
│ + "critstrkrtng" = "Crit"; │
│ + "hastertng" = "Haste"; │
│ + "exprtng" = "Expertise"; │
│ + "mastrtng" = "Mastery"; │
│ + }; │
│ + │
│ + # stat array index mappings (for gems/items) │
│ + statIndexNames = { │
│ + "0" = "Strength"; │
│ + "1" = "Agility"; │
│ + "2" = "Stamina"; │
│ + "3" = "Intellect"; │
│ + "4" = "Spirit"; │
│ + "5" = "Hit"; │
│ + "6" = "Crit"; │
│ + "7" = "Haste"; │
│ + "8" = "Expertise"; │
│ + "9" = "Dodge"; │
│ + "10" = "Parry"; │
│ + "11" = "Mastery"; │
│ + }; │
│ + # Helper functions for item database operations │
│ + helpers = { │
│ + # get item data by ID │
│ + getItem = id: let │
│ + idStr = toString id; │
│ + in │
│ + if itemsById ? ${idStr} │
│ + then itemsById.${idStr} │
│ + else null; │
│ + │
│ + # get gem data by ID │
│ + getGem = id: let │
│ + idStr = toString id; │
│ + in │
│ + if gemsById ? ${idStr} │
│ + then gemsById.${idStr} │
│ + else null; │
│ + │
│ + # get enchant data by effect ID │
│ + getEnchant = effectId: let │
│ + idStr = toString effectId; │
│ + in │
│ + if enchantsByEffectId ? ${idStr} │
│ + then enchantsByEffectId.${idStr} │
│ + else null; │
│ + │
│ + # get reforge data by ID │
│ + getReforge = reforgeId: let │
│ + idStr = toString reforgeId; │
│ + in │
│ + if reforgeData ? ${idStr} │
│ + then reforgeData.${idStr} │
│ + else null; │
│ + │
│ + # get glyph data by ID │
│ + getGlyph = glyphId: let │
│ + idStr = toString glyphId; │
│ + in │
│ + if glyphsById ? ${idStr} │
│ + then glyphsById.${idStr} │
│ + else null; │
│ + │
│ + # parse gem stats into readable format │
│ + parseGemStats = gem: │
│ + if gem ? stats │
│ + then let │
│ + # find non-zero stats and convert to readable format │
│ + indexedStats = lib.imap0 (index: value: {inherit index value;}) gem.stats │
│ ; │
│ + activeStats = lib.filter (stat: stat.value > 0) indexedStats; │
│ + statDescriptions = │
│ + map ( │
│ + stat: let │
│ + statName = statIndexNames.${toString stat.index} or "Unknown Stat"; │
│ + in "+${toString stat.value} ${statName}" │
│ + ) │
│ + activeStats; │
│ + in │
│ + statDescriptions │
│ + else []; │
│ + │
│ + # enrich a single gem with stats │
│ + enrichGem = gemId: │
│ + if gemId != null && gemId != 0 │
│ + then let │
│ + gem = helpers.getGem gemId; │
│ + in │
│ + if gem != null │
│ + then let │
│ + stats = helpers.parseGemStats gem; │
│ + in { │
│ + inherit (gem) id name; │
│ + icon = if gem ? icon then gem.icon else null; │
│ + color = if gem ? color then gem.color else null; │
│ + quality = if gem ? quality then gem.quality else null; │
│ + stats = stats; │
│ + description = │
│ + if stats != [] │
│ + then "${gem.name} -- ${lib.concatStringsSep ", " stats}" │
│ + else gem.name; │
│ + } │
│ + else null │
│ + else null; │
│ + │
│ + # enrich enchant info │
│ + enrichEnchant = enchantId: │
│ + if enchantId != null && enchantId != 0 │
│ + then { │
│ + id = enchantId; │
│ + name = let │
│ + enchant = helpers.getEnchant enchantId; │
│ + in │
│ + if enchant != null │
│ + then enchant.name │
│ + else "Unknown Enchant ${toString enchantId}"; │
│ + } │
│ + else null; │
│ + │
│ + # enrich reforge info │
│ + enrichReforge = reforgeId: │
│ + if reforgeId != null && reforgeId != 0 │
│ + then { │
│ + id = reforgeId; │
│ + description = let │
│ + reforge = helpers.getReforge reforgeId; │
│ + in │
│ + if reforge != null │
│ + then let │
│ + fromStat = statNames.${reforge.s1} or reforge.s1; │
│ + toStat = statNames.${reforge.s2} or reforge.s2; │
│ + in "${fromStat} -> ${toStat}" │
│ + else "Unknown Reforge ${toString reforgeId}"; │
│ + } │
│ + else null; │
│ + │
│ + # enrich a single equipment item │
│ + enrichItem = item: │
│ + if !(item ? id) then item │
│ + else let │
│ + itemData = helpers.getItem item.id; │
│ + enrichedGems = if item ? gems │
│ + then lib.filter (gem: gem != null) (map helpers.enrichGem item.gems) │
│ + else []; │
│ + enrichedEnchant = if item ? enchant │
│ + then helpers.enrichEnchant item.enchant │
│ + else null; │
│ + enrichedReforge = if item ? reforging │
│ + then helpers.enrichReforge item.reforging │
│ + else null; │
│ + │
│ + # Extract item stats from scalingOptions if available │
│ + itemStats = if itemData != null && itemData ? scalingOptions && itemData. │
│ scalingOptions ? "0" │
│ + then itemData.scalingOptions."0" │
│ + else null; │
│ + in │
│ + item // { │
│ + name = if itemData != null then itemData.name else "Item ${toString ite │
│ m.id}"; │
│ + icon = if itemData != null && itemData ? icon then itemData.icon else n │
│ ull; │
│ + quality = if itemData != null && itemData ? quality then itemData.quali │
│ ty else null; │
│ + type = if itemData != null && itemData ? type then itemData.type else n │
│ ull; │
│ + } // lib.optionalAttrs (itemStats != null) { │
│ + stats = itemStats; │
│ + } // lib.optionalAttrs (enrichedGems != []) { │
│ + gems = enrichedGems; │
│ + } // lib.optionalAttrs (enrichedEnchant != null) { │
│ + enchant = enrichedEnchant; │
│ + } // lib.optionalAttrs (enrichedReforge != null) { │
│ + reforging = enrichedReforge; │
│ + }; │
│ + }; │
│ + │
│ +in { │
│ + # get item data by ID │
│ + getItem = helpers.getItem; │
│ + │
│ + # get just the item name by ID │
│ + getItemName = id: let │
│ + item = helpers.getItem id; │
│ + in │
│ + if item != null │
│ + then item.name │
│ + else "Unknown Item ${toString id}"; │
│ + │
│ + # get gem data by ID │
│ + getGem = helpers.getGem; │
│ + │
│ + # get gem name by ID │
│ + getGemName = id: let │
│ + gem = helpers.getGem id; │
│ + in │
│ + if gem != null │
│ + then gem.name │
│ + else "Unknown Gem ${toString id}"; │
│ + │
│ + # parse gem stats into readable format │
│ + parseGemStats = helpers.parseGemStats; │
│ + │
│ + # get enriched gem data with stats (delegates to helpers) │
│ + getEnrichedGem = helpers.enrichGem; │
│ + │
│ + # get enchant data by effect ID │
│ + getEnchant = helpers.getEnchant; │
│ + │
│ + # get enchant name by effect ID │
│ + getEnchantName = effectId: let │
│ + enchant = helpers.getEnchant effectId; │
│ + in │
│ + if enchant != null │
│ + then enchant.name │
│ + else "Unknown Enchant ${toString effectId}"; │
│ + │
│ + # get reforge data by ID │
│ + getReforge = helpers.getReforge; │
│ + │
│ + # get reforge description by ID │
│ + getReforgeDescription = reforgeId: let │
│ + reforge = helpers.getReforge reforgeId; │
│ + in │
│ + if reforge != null │
│ + then let │
│ + fromStat = statNames.${reforge.s1} or reforge.s1; │
│ + toStat = statNames.${reforge.s2} or reforge.s2; │
│ + in "${fromStat} -> ${toStat}" │
│ + else "Unknown Reforge ${toString reforgeId}"; │
│ + │
│ + # get glyph data by ID │
│ + getGlyph = helpers.getGlyph; │
│ + │
│ + # get glyph name by ID │
│ + getGlyphName = glyphId: let │
│ + glyph = helpers.getGlyph glyphId; │
│ + item = if glyph != null then helpers.getItem glyph.itemId else null; │
│ + in │
│ + if item != null then item.name else "Unknown Glyph ${toString glyphId}"; │
│ + │
│ + # enrich equipment item with name and other data │
│ + enrichEquipmentItem = helpers.enrichItem; │
│ + │
│ + # enrich an entire equipment array │
│ + enrichEquipment = equipment: │
│ + if equipment ? items │
│ + then │
│ + equipment │
│ + // { │
│ + items = map helpers.enrichItem equipment.items; │
│ + } │
│ + else equipment; │
│ + │
│ + # enrich consumables with item names and icons │
│ + enrichConsumables = consumables: │
│ + if consumables == null then null │
│ + else let │
│ + getItemDataSafe = id: let item = helpers.getItem id; in │
│ + if item != null then { │
│ + name = item.name; │
│ + icon = if item ? icon then item.icon else null; │
│ + quality = if item ? quality then item.quality else null; │
│ + } else { │
│ + name = "Item ${toString id}"; │
│ + icon = null; │
│ + quality = null; │
│ + }; │
│ + in │
│ + consumables // lib.optionalAttrs (consumables ? flaskId && consumables.flas │
│ kId != 0) ( │
│ + let flaskData = getItemDataSafe consumables.flaskId; in { │
│ + flaskName = flaskData.name; │
│ + flaskIcon = flaskData.icon; │
│ + flaskQuality = flaskData.quality; │
│ + } │
│ + ) // lib.optionalAttrs (consumables ? foodId && consumables.foodId != 0) ( │
│ + let foodData = getItemDataSafe consumables.foodId; in { │
│ + foodName = foodData.name; │
│ + foodIcon = foodData.icon; │
│ + foodQuality = foodData.quality; │
│ + } │
│ + ) // lib.optionalAttrs (consumables ? potId && consumables.potId != 0) ( │
│ + let potData = getItemDataSafe consumables.potId; in { │
│ + potName = potData.name; │
│ + potIcon = potData.icon; │
│ + potQuality = potData.quality; │
│ + } │
│ + ) // lib.optionalAttrs (consumables ? prepotId && consumables.prepotId != 0 │
│ ) ( │
│ + let prepotData = getItemDataSafe consumables.prepotId; in { │
│ + prepotName = prepotData.name; │
│ + prepotIcon = prepotData.icon; │
│ + prepotQuality = prepotData.quality; │
│ + } │
│ + ); │
│ + │
│ + # enrich glyphs with item names and icons │
│ + enrichGlyphs = glyphs: │
│ + if glyphs == null then null │
│ + else let │
│ + getGlyphDataSafe = glyphId: let │
│ + glyph = helpers.getGlyph glyphId; │
│ + item = if glyph != null then helpers.getItem glyph.itemId else null; │
│ + in │
│ + if item != null then { │
│ + name = item.name; │
│ + icon = if item ? icon then item.icon else null; │
│ + quality = if item ? quality then item.quality else null; │
│ + spellId = if glyph != null then glyph.spellId else null; │
│ + } else { │
│ + name = "Glyph ${toString glyphId}"; │
│ + icon = null; │
│ + quality = null; │
│ + spellId = null; │
│ + }; │
│ + in │
│ + glyphs // lib.optionalAttrs (glyphs ? major1 && glyphs.major1 != 0) ( │
│ + let glyphData = getGlyphDataSafe glyphs.major1; in { │
│ + major1Name = glyphData.name; │
│ + major1Icon = glyphData.icon; │
│ + major1Quality = glyphData.quality; │
│ + major1SpellId = glyphData.spellId; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? major2 && glyphs.major2 != 0) ( │
│ + let glyphData = getGlyphDataSafe glyphs.major2; in { │
│ + major2Name = glyphData.name; │
│ + major2Icon = glyphData.icon; │
│ + major2Quality = glyphData.quality; │
│ + major2SpellId = glyphData.spellId; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? major3 && glyphs.major3 != 0) ( │
│ + let glyphData = getGlyphDataSafe glyphs.major3; in { │
│ + major3Name = glyphData.name; │
│ + major3Icon = glyphData.icon; │
│ + major3Quality = glyphData.quality; │
│ + major3SpellId = glyphData.spellId; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? minor1 && glyphs.minor1 != 0) ( │
│ + let glyphData = getGlyphDataSafe glyphs.minor1; in { │
│ + minor1Name = glyphData.name; │
│ + minor1Icon = glyphData.icon; │
│ + minor1Quality = glyphData.quality; │
│ + minor1SpellId = glyphData.spellId; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? minor2 && glyphs.minor2 != 0) ( │
│ + let glyphData = getGlyphDataSafe glyphs.minor2; in { │
│ + minor2Name = glyphData.name; │
│ + minor2Icon = glyphData.icon; │
│ + minor2Quality = glyphData.quality; │
│ + minor2SpellId = glyphData.spellId; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? minor3 && glyphs.minor3 != 0) ( │
│ + let glyphData = getGlyphDataSafe glyphs.minor3; in { │
│ + minor3Name = glyphData.name; │
│ + minor3Icon = glyphData.icon; │
│ + minor3Quality = glyphData.quality; │
│ + minor3SpellId = glyphData.spellId; │
│ + } │
│ + ); │
│ + │
│ + # enrich a full loadout │
│ + enrichLoadout = loadout: │
│ + if loadout ? equipment │
│ + then │
│ + loadout │
│ + // { │
│ + equipment = │
│ + if loadout.equipment ? items │
│ + then │
│ + loadout.equipment │
│ + // { │
│ + items = map helpers.enrichItem loadout.equipment.items; │
│ + } │
│ + else loadout.equipment; │
│ + # TODO: Add consumables, gems, enchants enrichment │
│ + } │
│ + else loadout; │
│ + │
│ + # enrich simulation results (main function) │
│ + enrichSimulationData = simData: │
│ + if simData ? results │
│ + then │
│ + simData │
│ + // { │
│ + results = │
│ + lib.mapAttrs ( │
│ + name: result: │
│ + if result ? loadout │
│ + then │
│ + result │
│ + // { │
│ + loadout = │
│ + if result.loadout ? equipment && result.loadout.equipment ? i │
│ tems │
│ + then │
│ + result.loadout │
│ + // { │
│ + equipment = result.loadout.equipment │
│ + // { │
│ + items = map helpers.enrichItem result.loadout.equipment │
│ .items; │
│ + }; │
│ + # TODO: Add consumables, gems, enchants enrichment │
│ + } │
│ + else result.loadout; │
│ + } │
│ + else result │
│ + ) │
│ + simData.results; │
│ + } │
│ + else simData; │
│ +} │
│ + │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ nix/lib/itemDatabase.nix ───────────┐
│ diff --git a/nix/lib/itemDatabase.nix b/nix/ │
│ lib/itemDatabase.nix │
│ new file mode 100644 │
│ index 0000000..b0b9b4f │
│ --- /dev/null │
│ +++ b/nix/lib/itemDatabase.nix │
│ @@ -0,0 +1,486 @@ │
│ +{ │
│ + inputs, │
│ + lib, │
│ + ... │
│ +}: let │
│ + wowsimsDb = lib.importJSON "${inputs.wows │
│ ims}/assets/database/db.json"; │
│ + │
│ + itemsById = lib.listToAttrs (map (item: { │
│ + name = toString item.id; │
│ + value = item; │
│ + }) │
│ + wowsimsDb.items); │
│ + │
│ + gemsById = lib.listToAttrs (map (gem: { │
│ + name = toString gem.id; │
│ + value = gem; │
│ + }) │
│ + wowsimsDb.gems); │
│ + │
│ + glyphsById = lib.listToAttrs (lib.imap0 ( │
│ index: glyph: { │
│ + name = toString index; │
│ + value = glyph; │
│ + }) │
│ + wowsimsDb.glyphIds); │
│ + │
│ + enchantsByEffectId = lib.listToAttrs (map │
│ (enchant: { │
│ + name = toString enchant.effectId; │
│ + value = enchant; │
│ + }) │
│ + wowsimsDb.enchants); │
│ + │
│ + reforgeData = { │
│ + "113" = { │
│ + "i1" = 6; │
│ + "s1" = "spi"; │
│ + "i2" = 13; │
│ + "s2" = "dodgertng"; │
│ + }; │
│ + "138" = { │
│ + "i1" = 31; │
│ + "s1" = "hitrtng"; │
│ + "i2" = 36; │
│ + "s2" = "hastertng"; │
│ + }; │
│ + "158" = { │
│ + "i1" = 37; │
│ + "s1" = "exprtng"; │
│ + "i2" = 31; │
│ + "s2" = "hitrtng"; │
│ + }; │
│ + "159" = { │
│ + "i1" = 37; │
│ + "s1" = "exprtng"; │
│ + "i2" = 32; │
│ + "s2" = "critstrkrtng"; │
│ + }; │
│ + "166" = { │
│ + "i1" = 49; │
│ + "s1" = "mastrtng"; │
│ + "i2" = 32; │
│ + "s2" = "critstrkrtng"; │
│ + }; │
│ + "167" = { │
│ + "i1" = 49; │
│ + "s1" = "mastrtng"; │
│ + "i2" = 36; │
│ + "s2" = "hastertng"; │
│ + }; │
│ + "168" = { │
│ + "i1" = 49; │
│ + "s1" = "mastrtng"; │
│ + "i2" = 37; │
│ + "s2" = "exprtng"; │
│ + }; │
│ + }; │
│ + │
│ + # stat name mappings (for reforging) │
│ + statNames = { │
│ + "spi" = "Spirit"; │
│ + "dodgertng" = "Dodge"; │
│ + "parryrtng" = "Parry"; │
│ + "hitrtng" = "Hit"; │
│ + "critstrkrtng" = "Crit"; │
│ + "hastertng" = "Haste"; │
│ + "exprtng" = "Expertise"; │
│ + "mastrtng" = "Mastery"; │
│ + }; │
│ + │
│ + # stat array index mappings (for gems/ite │
│ ms) │
│ + statIndexNames = { │
│ + "0" = "Strength"; │
│ + "1" = "Agility"; │
│ + "2" = "Stamina"; │
│ + "3" = "Intellect"; │
│ + "4" = "Spirit"; │
│ + "5" = "Hit"; │
│ + "6" = "Crit"; │
│ + "7" = "Haste"; │
│ + "8" = "Expertise"; │
│ + "9" = "Dodge"; │
│ + "10" = "Parry"; │
│ + "11" = "Mastery"; │
│ + }; │
│ + # Helper functions for item database oper │
│ ations │
│ + helpers = { │
│ + # get item data by ID │
│ + getItem = id: let │
│ + idStr = toString id; │
│ + in │
│ + if itemsById ? ${idStr} │
│ + then itemsById.${idStr} │
│ + else null; │
│ + │
│ + # get gem data by ID │
│ + getGem = id: let │
│ + idStr = toString id; │
│ + in │
│ + if gemsById ? ${idStr} │
│ + then gemsById.${idStr} │
│ + else null; │
│ + │
│ + # get enchant data by effect ID │
│ + getEnchant = effectId: let │
│ + idStr = toString effectId; │
│ + in │
│ + if enchantsByEffectId ? ${idStr} │
│ + then enchantsByEffectId.${idStr} │
│ + else null; │
│ + │
│ + # get reforge data by ID │
│ + getReforge = reforgeId: let │
│ + idStr = toString reforgeId; │
│ + in │
│ + if reforgeData ? ${idStr} │
│ + then reforgeData.${idStr} │
│ + else null; │
│ + │
│ + # get glyph data by ID │
│ + getGlyph = glyphId: let │
│ + idStr = toString glyphId; │
│ + in │
│ + if glyphsById ? ${idStr} │
│ + then glyphsById.${idStr} │
│ + else null; │
│ + │
│ + # parse gem stats into readable format │
│ + parseGemStats = gem: │
│ + if gem ? stats │
│ + then let │
│ + # find non-zero stats and convert t │
│ o readable format │
│ + indexedStats = lib.imap0 (index: va │
│ lue: {inherit index value;}) gem.stats; │
│ + activeStats = lib.filter (stat: sta │
│ t.value > 0) indexedStats; │
│ + statDescriptions = │
│ + map ( │
│ + stat: let │
│ + statName = statIndexNames.${t │
│ oString stat.index} or "Unknown Stat"; │
│ + in "+${toString stat.value} ${s │
│ tatName}" │
│ + ) │
│ + activeStats; │
│ + in │
│ + statDescriptions │
│ + else []; │
│ + │
│ + # enrich a single gem with stats │
│ + enrichGem = gemId: │
│ + if gemId != null && gemId != 0 │
│ + then let │
│ + gem = helpers.getGem gemId; │
│ + in │
│ + if gem != null │
│ + then let │
│ + stats = helpers.parseGemStats gem │
│ ; │
│ + in { │
│ + inherit (gem) id name; │
│ + icon = if gem ? icon then gem.ico │
│ n else null; │
│ + color = if gem ? color then gem.c │
│ olor else null; │
│ + quality = if gem ? quality then g │
│ em.quality else null; │
│ + stats = stats; │
│ + description = │
│ + if stats != [] │
│ + then "${gem.name} -- ${lib.conc │
│ atStringsSep ", " stats}" │
│ + else gem.name; │
│ + } │
│ + else null │
│ + else null; │
│ + │
│ + # enrich enchant info │
│ + enrichEnchant = enchantId: │
│ + if enchantId != null && enchantId != │
│ 0 │
│ + then { │
│ + id = enchantId; │
│ + name = let │
│ + enchant = helpers.getEnchant ench │
│ antId; │
│ + in │
│ + if enchant != null │
│ + then enchant.name │
│ + else "Unknown Enchant ${toString │
│ enchantId}"; │
│ + } │
│ + else null; │
│ + │
│ + # enrich reforge info │
│ + enrichReforge = reforgeId: │
│ + if reforgeId != null && reforgeId != │
│ 0 │
│ + then { │
│ + id = reforgeId; │
│ + description = let │
│ + reforge = helpers.getReforge refo │
│ rgeId; │
│ + in │
│ + if reforge != null │
│ + then let │
│ + fromStat = statNames.${reforge. │
│ s1} or reforge.s1; │
│ + toStat = statNames.${reforge.s2 │
│ } or reforge.s2; │
│ + in "${fromStat} -> ${toStat}" │
│ + else "Unknown Reforge ${toString │
│ reforgeId}"; │
│ + } │
│ + else null; │
│ + │
│ + # enrich a single equipment item │
│ + enrichItem = item: │
│ + if !(item ? id) then item │
│ + else let │
│ + itemData = helpers.getItem item.id; │
│ + enrichedGems = if item ? gems │
│ + then lib.filter (gem: gem != null │
│ ) (map helpers.enrichGem item.gems) │
│ + else []; │
│ + enrichedEnchant = if item ? enchant │
│ │
│ + then helpers.enrichEnchant item.e │
│ nchant │
│ + else null; │
│ + enrichedReforge = if item ? reforgi │
│ ng │
│ + then helpers.enrichReforge item.r │
│ eforging │
│ + else null; │
│ + │
│ + # Extract item stats from scalingOp │
│ tions if available │
│ + itemStats = if itemData != null && │
│ itemData ? scalingOptions && itemData.scalin │
│ gOptions ? "0" │
│ + then itemData.scalingOptions."0" │
│ + else null; │
│ + in │
│ + item // { │
│ + name = if itemData != null then i │
│ temData.name else "Item ${toString item.id}" │
│ ; │
│ + icon = if itemData != null && ite │
│ mData ? icon then itemData.icon else null; │
│ + quality = if itemData != null && │
│ itemData ? quality then itemData.quality els │
│ e null; │
│ + type = if itemData != null && ite │
│ mData ? type then itemData.type else null; │
│ + } // lib.optionalAttrs (itemStats ! │
│ = null) { │
│ + stats = itemStats; │
│ + } // lib.optionalAttrs (enrichedGem │
│ s != []) { │
│ + gems = enrichedGems; │
│ + } // lib.optionalAttrs (enrichedEnc │
│ hant != null) { │
│ + enchant = enrichedEnchant; │
│ + } // lib.optionalAttrs (enrichedRef │
│ orge != null) { │
│ + reforging = enrichedReforge; │
│ + }; │
│ + }; │
│ + │
│ +in { │
│ + # get item data by ID │
│ + getItem = helpers.getItem; │
│ + │
│ + # get just the item name by ID │
│ + getItemName = id: let │
│ + item = helpers.getItem id; │
│ + in │
│ + if item != null │
│ + then item.name │
│ + else "Unknown Item ${toString id}"; │
│ + │
│ + # get gem data by ID │
│ + getGem = helpers.getGem; │
│ + │
│ + # get gem name by ID │
│ + getGemName = id: let │
│ + gem = helpers.getGem id; │
│ + in │
│ + if gem != null │
│ + then gem.name │
│ + else "Unknown Gem ${toString id}"; │
│ + │
│ + # parse gem stats into readable format │
│ + parseGemStats = helpers.parseGemStats; │
│ + │
│ + # get enriched gem data with stats (deleg │
│ ates to helpers) │
│ + getEnrichedGem = helpers.enrichGem; │
│ + │
│ + # get enchant data by effect ID │
│ + getEnchant = helpers.getEnchant; │
│ + │
│ + # get enchant name by effect ID │
│ + getEnchantName = effectId: let │
│ + enchant = helpers.getEnchant effectId; │
│ + in │
│ + if enchant != null │
│ + then enchant.name │
│ + else "Unknown Enchant ${toString effect │
│ Id}"; │
│ + │
│ + # get reforge data by ID │
│ + getReforge = helpers.getReforge; │
│ + │
│ + # get reforge description by ID │
│ + getReforgeDescription = reforgeId: let │
│ + reforge = helpers.getReforge reforgeId; │
│ + in │
│ + if reforge != null │
│ + then let │
│ + fromStat = statNames.${reforge.s1} or │
│ reforge.s1; │
│ + toStat = statNames.${reforge.s2} or r │
│ eforge.s2; │
│ + in "${fromStat} -> ${toStat}" │
│ + else "Unknown Reforge ${toString reforg │
│ eId}"; │
│ + │
│ + # get glyph data by ID │
│ + getGlyph = helpers.getGlyph; │
│ + │
│ + # get glyph name by ID │
│ + getGlyphName = glyphId: let │
│ + glyph = helpers.getGlyph glyphId; │
│ + item = if glyph != null then helpers.ge │
│ tItem glyph.itemId else null; │
│ + in │
│ + if item != null then item.name else "Un │
│ known Glyph ${toString glyphId}"; │
│ + │
│ + # enrich equipment item with name and oth │
│ er data │
│ + enrichEquipmentItem = helpers.enrichItem; │
│ + │
│ + # enrich an entire equipment array │
│ + enrichEquipment = equipment: │
│ + if equipment ? items │
│ + then │
│ + equipment │
│ + // { │
│ + items = map helpers.enrichItem equi │
│ pment.items; │
│ + } │
│ + else equipment; │
│ + │
│ + # enrich consumables with item names and │
│ icons │
│ + enrichConsumables = consumables: │
│ + if consumables == null then null │
│ + else let │
│ + getItemDataSafe = id: let item = help │
│ ers.getItem id; in │
│ + if item != null then { │
│ + name = item.name; │
│ + icon = if item ? icon then item.i │
│ con else null; │
│ + quality = if item ? quality then │
│ item.quality else null; │
│ + } else { │
│ + name = "Item ${toString id}"; │
│ + icon = null; │
│ + quality = null; │
│ + }; │
│ + in │
│ + consumables // lib.optionalAttrs (con │
│ sumables ? flaskId && consumables.flaskId != │
│ 0) ( │
│ + let flaskData = getItemDataSafe con │
│ sumables.flaskId; in { │
│ + flaskName = flaskData.name; │
│ + flaskIcon = flaskData.icon; │
│ + flaskQuality = flaskData.quality; │
│ + } │
│ + ) // lib.optionalAttrs (consumables ? │
│ foodId && consumables.foodId != 0) ( │
│ + let foodData = getItemDataSafe cons │
│ umables.foodId; in { │
│ + foodName = foodData.name; │
│ + foodIcon = foodData.icon; │
│ + foodQuality = foodData.quality; │
│ + } │
│ + ) // lib.optionalAttrs (consumables ? │
│ potId && consumables.potId != 0) ( │
│ + let potData = getItemDataSafe consu │
│ mables.potId; in { │
│ + potName = potData.name; │
│ + potIcon = potData.icon; │
│ + potQuality = potData.quality; │
│ + } │
│ + ) // lib.optionalAttrs (consumables ? │
│ prepotId && consumables.prepotId != 0) ( │
│ + let prepotData = getItemDataSafe co │
│ nsumables.prepotId; in { │
│ + prepotName = prepotData.name; │
│ + prepotIcon = prepotData.icon; │
│ + prepotQuality = prepotData.qualit │
│ y; │
│ + } │
│ + ); │
│ + │
│ + # enrich glyphs with item names and icons │
│ + enrichGlyphs = glyphs: │
│ + if glyphs == null then null │
│ + else let │
│ + getGlyphDataSafe = glyphId: let │
│ + glyph = helpers.getGlyph glyphId; │
│ + item = if glyph != null then helper │
│ s.getItem glyph.itemId else null; │
│ + in │
│ + if item != null then { │
│ + name = item.name; │
│ + icon = if item ? icon then item.i │
│ con else null; │
│ + quality = if item ? quality then │
│ item.quality else null; │
│ + spellId = if glyph != null then g │
│ lyph.spellId else null; │
│ + } else { │
│ + name = "Glyph ${toString glyphId} │
│ "; │
│ + icon = null; │
│ + quality = null; │
│ + spellId = null; │
│ + }; │
│ + in │
│ + glyphs // lib.optionalAttrs (glyphs ? │
│ major1 && glyphs.major1 != 0) ( │
│ + let glyphData = getGlyphDataSafe gl │
│ yphs.major1; in { │
│ + major1Name = glyphData.name; │
│ + major1Icon = glyphData.icon; │
│ + major1Quality = glyphData.quality │
│ ; │
│ + major1SpellId = glyphData.spellId │
│ ; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? majo │
│ r2 && glyphs.major2 != 0) ( │
│ + let glyphData = getGlyphDataSafe gl │
│ yphs.major2; in { │
│ + major2Name = glyphData.name; │
│ + major2Icon = glyphData.icon; │
│ + major2Quality = glyphData.quality │
│ ; │
│ + major2SpellId = glyphData.spellId │
│ ; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? majo │
│ r3 && glyphs.major3 != 0) ( │
│ + let glyphData = getGlyphDataSafe gl │
│ yphs.major3; in { │
│ + major3Name = glyphData.name; │
│ + major3Icon = glyphData.icon; │
│ + major3Quality = glyphData.quality │
│ ; │
│ + major3SpellId = glyphData.spellId │
│ ; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? mino │
│ r1 && glyphs.minor1 != 0) ( │
│ + let glyphData = getGlyphDataSafe gl │
│ yphs.minor1; in { │
│ + minor1Name = glyphData.name; │
│ + minor1Icon = glyphData.icon; │
│ + minor1Quality = glyphData.quality │
│ ; │
│ + minor1SpellId = glyphData.spellId │
│ ; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? mino │
│ r2 && glyphs.minor2 != 0) ( │
│ + let glyphData = getGlyphDataSafe gl │
│ yphs.minor2; in { │
│ + minor2Name = glyphData.name; │
│ + minor2Icon = glyphData.icon; │
│ + minor2Quality = glyphData.quality │
│ ; │
│ + minor2SpellId = glyphData.spellId │
│ ; │
│ + } │
│ + ) // lib.optionalAttrs (glyphs ? mino │
│ r3 && glyphs.minor3 != 0) ( │
│ + let glyphData = getGlyphDataSafe gl │
│ yphs.minor3; in { │
│ + minor3Name = glyphData.name; │
│ + minor3Icon = glyphData.icon; │
│ + minor3Quality = glyphData.quality │
│ ; │
│ + minor3SpellId = glyphData.spellId │
│ ; │
│ + } │
│ + ); │
│ + │
│ + # enrich a full loadout │
│ + enrichLoadout = loadout: │
│ + if loadout ? equipment │
│ + then │
│ + loadout │
│ + // { │
│ + equipment = │
│ + if loadout.equipment ? items │
│ + then │
│ + loadout.equipment │
│ + // { │
│ + items = map helpers.enrichIte │
│ m loadout.equipment.items; │
│ + } │
│ + else loadout.equipment; │
│ + # TODO: Add consumables, gems, ench │
│ ants enrichment │
│ + } │
│ + else loadout; │
│ + │
│ + # enrich simulation results (main functio │
│ n) │
│ + enrichSimulationData = simData: │
│ + if simData ? results │
│ + then │
│ + simData │
│ + // { │
│ + results = │
│ + lib.mapAttrs ( │
│ + name: result: │
│ + if result ? loadout │
│ + then │
│ + result │
│ + // { │
│ + loadout = │
│ + if result.loadout ? equ │
│ ipment && result.loadout.equipment ? items │
│ + then │
│ + result.loadout │
│ + // { │
│ + equipment = result. │
│ loadout.equipment │
│ + // { │
│ + items = map helpe │
│ rs.enrichItem result.loadout.equipment.items │
│ ; │
│ + }; │
│ + # TODO: Add consuma │
│ bles, gems, enchants enrichment │
│ + } │
│ + else result.loadout; │
│ + } │
│ + else result │
│ + ) │
│ + simData.results; │
│ + } │
│ + else simData; │
│ +} │
│ + │
└──────────────────────────────────────────────┘