OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      eeda0bdb58ed
DATE      2025-07-27
SUBJECT   nix: enrich talents and glyphs
FILES     5 CHANGED
HASH      eeda0bdb58ed
DATE      2025-07-27
SUBJECT   nix: enrich talents and glyphs
FILES     5 CHANGED
 

diff --git a/flake.lock b/flake.lock
index 313dcab..47a8c2c 100644
--- a/flake.lock
+++ b/flake.lock
@@ -85,11 +85,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1753422684,
-        "narHash": "sha256-mUTXnS3d5IMH3GZ744dLylXdJYfxek9MRiWBCmtrtBQ=",
+        "lastModified": 1753526276,
+        "narHash": "sha256-XIqrgUb8ml9t2oEE6tuCMnVX6TtzSYa9aujtAe6r+jg=",
         "owner": "ooks-io",
         "repo": "mop",
-        "rev": "77b72ea264ed075871108b78fd9ff83f459b4780",
+        "rev": "49e2efe1a9b8f68e049f874142a25ce7f2e076a1",
         "type": "github"
       },
       "original": {

diff --git a/flake.lock b/flake.lock
index 313dcab..47a8c2c 100644
--- a/flake.lock
+++ b/flake.lock
@@ -85,11 +85,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1753422684,
-        "narHash": "sha256-mUTXnS3d5IMH3GZ7
44dLylXdJYfxek9MRiWBCmtrtBQ=",
+        "lastModified": 1753526276,
+        "narHash": "sha256-XIqrgUb8ml9t2oEE
6tuCMnVX6TtzSYa9aujtAe6r+jg=",
         "owner": "ooks-io",
         "repo": "mop",
-        "rev": "77b72ea264ed075871108b78fd9
ff83f459b4780",
+        "rev": "49e2efe1a9b8f68e049f874142a
25ce7f2e076a1",
         "type": "github"
       },
       "original": {
 

diff --git a/nix/apps/simulation/mkMassSim.nix b/nix/apps/simulation/mkMassSim.nix
index b1e8a1c..3b3f5aa 100644
--- a/nix/apps/simulation/mkMassSim.nix
+++ b/nix/apps/simulation/mkMassSim.nix
@@ -102,10 +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
+          # Pre-enrich the equipment, consumables, glyphs, and talents for this r
ace config
           enrichedEquipment = itemDatabase.enrichEquipment raceConfig.config.equi
pment;
           enrichedConsumables = itemDatabase.enrichConsumables raceConfig.config.
consumables;
-          enrichedGlyphs = itemDatabase.enrichGlyphs raceConfig.config.glyphs;
+          enrichedGlyphs = itemDatabase.enrichGlyphs raceConfig.config.class race
Config.config.glyphs;
+          enrichedTalents = itemDatabase.enrichTalents raceConfig.config.class ra
ceConfig.config.talentsString;
 
           simInput = mkSim {
             inherit iterations;
@@ -132,6 +133,10 @@ CONSUMABLES_EOF
 ${builtins.toJSON enrichedGlyphs}
 GLYPHS_EOF
             
+            cat > talents.json << 'TALENTS_EOF'
+${builtins.toJSON enrichedTalents}
+TALENTS_EOF
+            
             cat > input.json << 'INPUT_EOF'
 ${simInput}
 INPUT_EOF
@@ -154,6 +159,7 @@ INPUT_EOF
                 --slurpfile equipment enriched_equipment.json \
                 --slurpfile consumables consumables.json \
                 --arg talentsString "${raceConfig.config.talentsString}" \
+                --slurpfile talents talents.json \
                 --slurpfile glyphs glyphs.json \
                 --arg race "${raceConfig.config.race}" \
                 --arg class "${raceConfig.config.class}" \
@@ -168,6 +174,7 @@ INPUT_EOF
                   loadout: {
                     consumables: $consumables[0],
                     talentsString: $talentsString,
+                    talents: $talents[0],
                     glyphs: $glyphs[0],
                     equipment: $equipment[0],
                     race: $race,
@@ -324,10 +331,11 @@ INPUT_EOF
     simDerivations = lib.listToAttrs (map (spec: {
         name = "${spec.className}-${spec.specName}";
         value = let
-          # Pre-enrich the equipment, consumables, and glyphs for this spec
+          # Pre-enrich the equipment, consumables, glyphs, and talents for this s
pec
           enrichedEquipment = itemDatabase.enrichEquipment spec.config.equipment;
           enrichedConsumables = itemDatabase.enrichConsumables spec.config.consum
ables;
-          enrichedGlyphs = itemDatabase.enrichGlyphs spec.config.glyphs;
+          enrichedGlyphs = itemDatabase.enrichGlyphs spec.config.class spec.confi
g.glyphs;
+          enrichedTalents = itemDatabase.enrichTalents spec.config.class spec.con
fig.talentsString;
           
           simInput = mkSim {
             inherit iterations;
@@ -354,6 +362,10 @@ CONSUMABLES_EOF
 ${builtins.toJSON enrichedGlyphs}
 GLYPHS_EOF
             
+            cat > talents.json << 'TALENTS_EOF'
+${builtins.toJSON enrichedTalents}
+TALENTS_EOF
+            
             cat > input.json << 'INPUT_EOF'
 ${simInput}
 INPUT_EOF
@@ -377,6 +389,7 @@ INPUT_EOF
                 --slurpfile equipment enriched_equipment.json \
                 --slurpfile consumables consumables.json \
                 --arg talentsString "${spec.config.talentsString}" \
+                --slurpfile talents talents.json \
                 --slurpfile glyphs glyphs.json \
                 --arg race "${spec.config.race}" \
                 --arg class "${spec.config.class}" \
@@ -392,6 +405,7 @@ INPUT_EOF
                   loadout: {
                     consumables: $consumables[0],
                     talentsString: $talentsString,
+                    talents: $talents[0],
                     glyphs: $glyphs[0],
                     equipment: $equipment[0],
                     race: $race,

diff --git a/nix/apps/simulation/mkMassSim.n
ix b/nix/apps/simulation/mkMassSim.nix
index b1e8a1c..3b3f5aa 100644
--- a/nix/apps/simulation/mkMassSim.nix
+++ b/nix/apps/simulation/mkMassSim.nix
@@ -102,10 +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
+          # Pre-enrich the equipment, consu
mables, glyphs, and talents for this race co
nfig
           enrichedEquipment = itemDatabase.
enrichEquipment raceConfig.config.equipment;
           enrichedConsumables = itemDatabas
e.enrichConsumables raceConfig.config.consum
ables;
-          enrichedGlyphs = itemDatabase.enr
ichGlyphs raceConfig.config.glyphs;
+          enrichedGlyphs = itemDatabase.enr
ichGlyphs raceConfig.config.class raceConfig
.config.glyphs;
+          enrichedTalents = itemDatabase.en
richTalents raceConfig.config.class raceConf
ig.config.talentsString;
 
           simInput = mkSim {
             inherit iterations;
@@ -132,6 +133,10 @@ CONSUMABLES_EOF
 ${builtins.toJSON enrichedGlyphs}
 GLYPHS_EOF
             
+            cat > talents.json << 'TALENTS_
EOF'
+${builtins.toJSON enrichedTalents}
+TALENTS_EOF
+            
             cat > input.json << 'INPUT_EOF'
 ${simInput}
 INPUT_EOF
@@ -154,6 +159,7 @@ INPUT_EOF
                 --slurpfile equipment enric
hed_equipment.json \
                 --slurpfile consumables con
sumables.json \
                 --arg talentsString "${race
Config.config.talentsString}" \
+                --slurpfile talents talents
.json \
                 --slurpfile glyphs glyphs.j
son \
                 --arg race "${raceConfig.co
nfig.race}" \
                 --arg class "${raceConfig.c
onfig.class}" \
@@ -168,6 +174,7 @@ INPUT_EOF
                   loadout: {
                     consumables: $consumabl
es[0],
                     talentsString: $talents
String,
+                    talents: $talents[0],
                     glyphs: $glyphs[0],
                     equipment: $equipment[0
],
                     race: $race,
@@ -324,10 +331,11 @@ INPUT_EOF
     simDerivations = lib.listToAttrs (map (
spec: {
         name = "${spec.className}-${spec.sp
ecName}";
         value = let
-          # Pre-enrich the equipment, consu
mables, and glyphs for this spec
+          # Pre-enrich the equipment, consu
mables, glyphs, and talents for this spec
           enrichedEquipment = itemDatabase.
enrichEquipment spec.config.equipment;
           enrichedConsumables = itemDatabas
e.enrichConsumables spec.config.consumables;
-          enrichedGlyphs = itemDatabase.enr
ichGlyphs spec.config.glyphs;
+          enrichedGlyphs = itemDatabase.enr
ichGlyphs spec.config.class spec.config.glyp
hs;
+          enrichedTalents = itemDatabase.en
richTalents spec.config.class spec.config.ta
lentsString;
           
           simInput = mkSim {
             inherit iterations;
@@ -354,6 +362,10 @@ CONSUMABLES_EOF
 ${builtins.toJSON enrichedGlyphs}
 GLYPHS_EOF
             
+            cat > talents.json << 'TALENTS_
EOF'
+${builtins.toJSON enrichedTalents}
+TALENTS_EOF
+            
             cat > input.json << 'INPUT_EOF'
 ${simInput}
 INPUT_EOF
@@ -377,6 +389,7 @@ INPUT_EOF
                 --slurpfile equipment enric
hed_equipment.json \
                 --slurpfile consumables con
sumables.json \
                 --arg talentsString "${spec
.config.talentsString}" \
+                --slurpfile talents talents
.json \
                 --slurpfile glyphs glyphs.j
son \
                 --arg race "${spec.config.r
ace}" \
                 --arg class "${spec.config.
class}" \
@@ -392,6 +405,7 @@ INPUT_EOF
                   loadout: {
                     consumables: $consumabl
es[0],
                     talentsString: $talents
String,
+                    talents: $talents[0],
                     glyphs: $glyphs[0],
                     equipment: $equipment[0
],
                     race: $race,
 

diff --git a/nix/lib/database/glyphs.nix b/nix/lib/database/glyphs.nix
new file mode 100644
index 0000000..3d24853
--- /dev/null
+++ b/nix/lib/database/glyphs.nix
@@ -0,0 +1,91 @@
+let
+  mkGlyph = name: spellId: icon: {
+    inherit spellId icon;
+    name = "Glyph of ${name}";
+  };
+in {
+  # TODO: complete glyph list, currently only supporting those that exist in the 
current profiles
+  ClassDeathKnight = {
+    "43533" = mkGlyph "Anti-Magic Shell" 58623 "spell_shadow_antimagicshell";
+    "43537" = mkGlyph "Chains of Ice" 58620 "spell_frost_chainsofice";
+    "45800" = mkGlyph "Simulacrum" 63331 "spell_holy_consumemagic";
+    "104048" = mkGlyph "Regenerative Magic" 146648 "spell_shadow_antimagicshell";
+    "104047" = mkGlyph "Loud Horn" 146646 "inv_misc_horn_04";
+    "43548" = mkGlyph "Pestilence" 58657 "spell_shadow_plaguecloud";
+    "43554" = mkGlyph "Vampiric Blood" 58657 "spell_shadow_lifedrain";
+    "43550" = mkGlyph "Army of the Dead" 58669 "spell_deathknight_armyofthedead";
+    "45806" = mkGlyph "Tranquil Grip" 63335 "ability_rogue_envelopingshadows";
+    "43673" = mkGlyph "Death Gate" 60200 "spell_arcane_teleportundercity";
+    "43539" = mkGlyph "Death's Embrace" 58677 "spell_shadow_deathcoil";
+  };
+  ClassDruid = {
+    "40914" = mkGlyph "Healing Touch" 54825 "spell_nature_healingtouch";
+    "40906" = mkGlyph "Stampede" 114300 "spell_druid_stampedingroar_cat";
+    "40909" = mkGlyph "Rebirth" 54733 "spell_nature_reincarnation";
+  };
+  ClassHunter = {
+    "42909" = mkGlyph "Animal Bond" 20895 "classic_ability_druid_demoralizingroar
";
+    "42903" = mkGlyph "Deterrence" 56850 "ability_whirlwind";
+    "42911" = mkGlyph "Pathfinding" 19560 "ability_hunter_pathfinding2";
+    "42914" = mkGlyph "Aimed Shot" 126095 "inv_spear_07";
+    "42899" = mkGlyph "Liberation" 132106 "achievement_bg_returnxflags_def_wsg";
+  };
+  ClassMage = {
+    # major
+    "44955" = mkGlyph "Arcane Power" 62210 "spell_nature_lightning";
+    "42748" = mkGlyph "Rapid Displacement" 146659 "spell_arcane_blink";
+    "42746" = mkGlyph "Cone of Cold" 115705 "spell_frost_glacier";
+    "42739" = mkGlyph "Combustion" 56368 "spell_fire_sealoffire";
+    "63539" = mkGlyph "Inferno Blast" 89926 "spell_mage_infernoblast";
+    "42745" = mkGlyph "Splitting Ice" 56377 "spell_frost_frostblast";
+    "45736" = mkGlyph "Water Elemental" 63090 "spell_frost_summonwaterelemental_2
";
+    # minor
+    "42743" = mkGlyph "Momentum" 56384 "spell_arcane_blink";
+    "63416" = mkGlyph "Rapid Teleportation" 46989 "spell_arcane_portaldalaran";
+    "42735" = mkGlyph "Loose Mana" 56363 "inv_misc_gem_sapphire_02";
+    "104104" = mkGlyph "Unbound Elemental" 146976 "spell_frost_summonwaterelement
al";
+    "45739" = mkGlyph "Mirror Image" 45739 "spell_magic_lesserinvisibilty";
+  };
+  ClassMonk = {
+    # major
+    "85697" = mkGlyph "Spinning Crane Kick" 120479 "ability_monk_cranekick_new";
+    "87900" = mkGlyph "Touch of Karma" 125678 "ability_monk_touchofkarma";
+    "85695" = mkGlyph "Zen Meditation" 120477 "ability_monk_zenmeditation";
+    # minor
+    "90715" = mkGlyph "Blackout Kick" 132005 "ability_monk_blackoutkick";
+  };
+  ClassPaladin = {
+    # major
+    "41097" = mkGlyph "Templar's Verdict" 54926 "spell_paladin_templarsverdict";
+    "41092" = mkGlyph "Double Jeopardy" 54922 "spell_holy_righteousfury";
+    "83107" = mkGlyph "Mass Exorcism" 122028 "spell_holy_righteousfury";
+  };
+  ClassPriest = {};
+  ClassRogue = {
+    # major
+    "45761" = mkGlyph "Vendetta" 63249 "ability_rogue_deadliness";
+    "42970" = mkGlyph "Hemorrhaging Veins" 146631 "spell_holy_sealofsacrifice";
+  };
+  ClassShaman = {
+    # major
+    "41539" = mkGlyph "Spiritwalker's Grace" 55446 "spell_shaman_spiritwalkersgra
ce";
+    "71155" = mkGlyph "Lightning Shield" 101052 "spell_nature_lightningshield";
+    "41529" = mkGlyph "Fire Elemental Totem" 55455 "spell_fire_elemental_totem";
+    "41530" = mkGlyph "Fire Nova" 55450 "spell_shaman_firenova";
+  };
+  ClassWarlock = {
+    # major
+    "42472" = mkGlyph "Unstable Affliction" 56233 "spell_shadow_unstableafflictio
n_3";
+    "42470" = mkGlyph "Soulstone" 56231 "inv_misc_orb_04";
+    "45785" = mkGlyph "Life Tap" 63320 "spell_shadow_burningspirit";
+    "42465" = mkGlyph "Imp Swarm" 104316 "ability_warlock_impoweredimp";
+    # minor
+    "43389" = mkGlyph "Unending Breath" 58079 "spell_shadow_demonbreath";
+  };
+  ClassWarrior = {
+    # major
+    "67482" = mkGlyph "Bull Rush" 94372 "achievement_character_tauren_male";
+    "43399" = mkGlyph "Unending Rage" 58098 "ability_warrior_intensifyrage";
+    "45792" = mkGlyph "Death From Above" 63325 "ability_heroicleap";
+  };
+}

diff --git a/nix/lib/database/glyphs.nix b/n
ix/lib/database/glyphs.nix
new file mode 100644
index 0000000..3d24853
--- /dev/null
+++ b/nix/lib/database/glyphs.nix
@@ -0,0 +1,91 @@
+let
+  mkGlyph = name: spellId: icon: {
+    inherit spellId icon;
+    name = "Glyph of ${name}";
+  };
+in {
+  # TODO: complete glyph list, currently on
ly supporting those that exist in the curren
t profiles
+  ClassDeathKnight = {
+    "43533" = mkGlyph "Anti-Magic Shell" 58
623 "spell_shadow_antimagicshell";
+    "43537" = mkGlyph "Chains of Ice" 58620
 "spell_frost_chainsofice";
+    "45800" = mkGlyph "Simulacrum" 63331 "s
pell_holy_consumemagic";
+    "104048" = mkGlyph "Regenerative Magic"
 146648 "spell_shadow_antimagicshell";
+    "104047" = mkGlyph "Loud Horn" 146646 "
inv_misc_horn_04";
+    "43548" = mkGlyph "Pestilence" 58657 "s
pell_shadow_plaguecloud";
+    "43554" = mkGlyph "Vampiric Blood" 5865
7 "spell_shadow_lifedrain";
+    "43550" = mkGlyph "Army of the Dead" 58
669 "spell_deathknight_armyofthedead";
+    "45806" = mkGlyph "Tranquil Grip" 63335
 "ability_rogue_envelopingshadows";
+    "43673" = mkGlyph "Death Gate" 60200 "s
pell_arcane_teleportundercity";
+    "43539" = mkGlyph "Death's Embrace" 586
77 "spell_shadow_deathcoil";
+  };
+  ClassDruid = {
+    "40914" = mkGlyph "Healing Touch" 54825
 "spell_nature_healingtouch";
+    "40906" = mkGlyph "Stampede" 114300 "sp
ell_druid_stampedingroar_cat";
+    "40909" = mkGlyph "Rebirth" 54733 "spel
l_nature_reincarnation";
+  };
+  ClassHunter = {
+    "42909" = mkGlyph "Animal Bond" 20895 "
classic_ability_druid_demoralizingroar";
+    "42903" = mkGlyph "Deterrence" 56850 "a
bility_whirlwind";
+    "42911" = mkGlyph "Pathfinding" 19560 "
ability_hunter_pathfinding2";
+    "42914" = mkGlyph "Aimed Shot" 126095 "
inv_spear_07";
+    "42899" = mkGlyph "Liberation" 132106 "
achievement_bg_returnxflags_def_wsg";
+  };
+  ClassMage = {
+    # major
+    "44955" = mkGlyph "Arcane Power" 62210 
"spell_nature_lightning";
+    "42748" = mkGlyph "Rapid Displacement" 
146659 "spell_arcane_blink";
+    "42746" = mkGlyph "Cone of Cold" 115705
 "spell_frost_glacier";
+    "42739" = mkGlyph "Combustion" 56368 "s
pell_fire_sealoffire";
+    "63539" = mkGlyph "Inferno Blast" 89926
 "spell_mage_infernoblast";
+    "42745" = mkGlyph "Splitting Ice" 56377
 "spell_frost_frostblast";
+    "45736" = mkGlyph "Water Elemental" 630
90 "spell_frost_summonwaterelemental_2";
+    # minor
+    "42743" = mkGlyph "Momentum" 56384 "spe
ll_arcane_blink";
+    "63416" = mkGlyph "Rapid Teleportation"
 46989 "spell_arcane_portaldalaran";
+    "42735" = mkGlyph "Loose Mana" 56363 "i
nv_misc_gem_sapphire_02";
+    "104104" = mkGlyph "Unbound Elemental" 
146976 "spell_frost_summonwaterelemental";
+    "45739" = mkGlyph "Mirror Image" 45739 
"spell_magic_lesserinvisibilty";
+  };
+  ClassMonk = {
+    # major
+    "85697" = mkGlyph "Spinning Crane Kick"
 120479 "ability_monk_cranekick_new";
+    "87900" = mkGlyph "Touch of Karma" 1256
78 "ability_monk_touchofkarma";
+    "85695" = mkGlyph "Zen Meditation" 1204
77 "ability_monk_zenmeditation";
+    # minor
+    "90715" = mkGlyph "Blackout Kick" 13200
5 "ability_monk_blackoutkick";
+  };
+  ClassPaladin = {
+    # major
+    "41097" = mkGlyph "Templar's Verdict" 5
4926 "spell_paladin_templarsverdict";
+    "41092" = mkGlyph "Double Jeopardy" 549
22 "spell_holy_righteousfury";
+    "83107" = mkGlyph "Mass Exorcism" 12202
8 "spell_holy_righteousfury";
+  };
+  ClassPriest = {};
+  ClassRogue = {
+    # major
+    "45761" = mkGlyph "Vendetta" 63249 "abi
lity_rogue_deadliness";
+    "42970" = mkGlyph "Hemorrhaging Veins" 
146631 "spell_holy_sealofsacrifice";
+  };
+  ClassShaman = {
+    # major
+    "41539" = mkGlyph "Spiritwalker's Grace
" 55446 "spell_shaman_spiritwalkersgrace";
+    "71155" = mkGlyph "Lightning Shield" 10
1052 "spell_nature_lightningshield";
+    "41529" = mkGlyph "Fire Elemental Totem
" 55455 "spell_fire_elemental_totem";
+    "41530" = mkGlyph "Fire Nova" 55450 "sp
ell_shaman_firenova";
+  };
+  ClassWarlock = {
+    # major
+    "42472" = mkGlyph "Unstable Affliction"
 56233 "spell_shadow_unstableaffliction_3";
+    "42470" = mkGlyph "Soulstone" 56231 "in
v_misc_orb_04";
+    "45785" = mkGlyph "Life Tap" 63320 "spe
ll_shadow_burningspirit";
+    "42465" = mkGlyph "Imp Swarm" 104316 "a
bility_warlock_impoweredimp";
+    # minor
+    "43389" = mkGlyph "Unending Breath" 580
79 "spell_shadow_demonbreath";
+  };
+  ClassWarrior = {
+    # major
+    "67482" = mkGlyph "Bull Rush" 94372 "ac
hievement_character_tauren_male";
+    "43399" = mkGlyph "Unending Rage" 58098
 "ability_warrior_intensifyrage";
+    "45792" = mkGlyph "Death From Above" 63
325 "ability_heroicleap";
+  };
+}
 

diff --git a/nix/lib/database/talents.nix b/nix/lib/database/talents.nix
new file mode 100644
index 0000000..a3caff9
--- /dev/null
+++ b/nix/lib/database/talents.nix
@@ -0,0 +1,371 @@
+let
+  mkTalent = name: spellId: icon: {
+    inherit name spellId icon;
+  };
+in {
+  # talent string format: "123456" where each digit represents the choice (1-3) f
or each tier (1-6)
+
+  ClassDeathKnight = {
+    "1" = {
+      "1" = mkTalent "Roiling Blood" 108170 "ability_deathknight_roilingblood";
+      "2" = mkTalent "Plague Leech" 123693 "ability_creature_disease_02";
+      "3" = mkTalent "Unholy Blight" 115989 "spell_shadow_contagion";
+    };
+    "2" = {
+      "1" = mkTalent "Lichborne" 49039 "spell_shadow_raisedead";
+      "2" = mkTalent "Anti-Magic Zone" 51052 "spell_deathknight_antimagiczone";
+      "3" = mkTalent "Purgatory" 114556 "inv_misc_shadowegg";
+    };
+    "3" = {
+      "1" = mkTalent "Death's Advance" 96268 "spell_shadow_demonicempathy";
+      "2" = mkTalent "Chilblains" 50041 "spell_frost_wisp";
+      "3" = mkTalent "Asphyxiate" 108194 "ability_deathknight_asphixiate";
+    };
+    "4" = {
+      "1" = mkTalent "Death Pact" 48743 "spell_shadow_deathpact";
+      "2" = mkTalent "Death Siphon" 108196 "ability_deathknight_deathsiphon";
+      "3" = mkTalent "Conversion" 119975 "ability_deathknight_deathsiphon2";
+    };
+    "5" = {
+      "1" = mkTalent "Blood Tap" 45529 "spell_deathknight_bloodtap";
+      "2" = mkTalent "Runic Empowerment" 81229 "inv_misc_rune_10";
+      "3" = mkTalent "Runic Corruption" 51462 "spell_shadow_rune";
+    };
+    "6" = {
+      "1" = mkTalent "Gorefiend's Grasp" 108199 "ability_deathknight_aoedeathgrip
";
+      "2" = mkTalent "Remorseless Winter" 108200 "ability_deathknight_remorseless
winters2";
+      "3" = mkTalent "Desecrated Ground" 108201 "ability_deathknight_desecratedgr
ound";
+    };
+  };
+
+  ClassDruid = {
+    "1" = {
+      "1" = mkTalent "Feline Swiftness" 131768 "spell_druid_tirelesspursuit";
+      "2" = mkTalent "Displacer Best" 102280 "spell_druid_displacement";
+      "3" = mkTalent "Wild Charge" 102401 "spell_druid_wildcharge";
+    };
+    "2" = {
+      "1" = mkTalent "Ysera's Gift" 145108 "inv_misc_head_dragon_green";
+      "2" = mkTalent "Renewal" 108238 "spell_nature_natureblessing";
+      "3" = mkTalent "Cenarion Ward" 102351 "ability_druid_naturalperfection";
+    };
+    "3" = {
+      "1" = mkTalent "Faerie Swarm" 106707 "spell_druid_swarm";
+      "2" = mkTalent "Mass Entanglement" 102359 "spell_druid_massentanglement";
+      "3" = mkTalent "Typhoon" 132469 "ability_druid_typhoon";
+    };
+    "4" = {
+      "1" = mkTalent "Soul of the Forest" 114107 "ability_druid_manatree";
+      "2" = mkTalent "Incarnation" 106731 "spell_druid_incarnation";
+      "3" = mkTalent "Force of Nature" 106737 "ability_druid_forceofnature";
+    };
+    "5" = {
+      "1" = mkTalent "Disorienting Roar" 99 "classic_ability_druid_demoralizingro
ar";
+      "2" = mkTalent "Ursol's Vortex" 102793 "spell_druid_ursolsvortex";
+      "3" = mkTalent "Mighty Bash" 5211 "ability_druid_bash";
+    };
+    "6" = {
+      "1" = mkTalent "Heart of the Wild" 108288 "spell_holy_blessingofagility";
+      "2" = mkTalent "Dream of Cenarius" 108373 "ability_druid_dreamstate";
+      "3" = mkTalent "Nature's Vigil" 124974 "achievement_zone_feralas";
+    };
+  };
+
+  ClassHunter = {
+    "1" = {
+      "1" = mkTalent "Posthaste" 109215 "ability_hunter_posthaste";
+      "2" = mkTalent "Narrow Escape" 109298 "inv_misc_web_01";
+      "3" = mkTalent "Crouching Tiger, Hidden Chimera" 118675 "ability_hunter_pet
_chimera";
+    };
+    "2" = {
+      "1" = mkTalent "Binding Shot" 109248 "spell_shaman_bindelemental";
+      "2" = mkTalent "Wyvern Sting" 19386 "inv_spear_02";
+      "3" = mkTalent "Intimidation" 19577 "ability_devour";
+    };
+    "3" = {
+      "1" = mkTalent "Exhilaration" 109304 "ability_hunter_onewithnature";
+      "2" = mkTalent "Aspect of the Iron Hawk" 109260 "spell_hunter_aspectoftheir
onhawk";
+      "3" = mkTalent "Spirit Bond" 109212 "classic_ability_druid_demoralizingroar
";
+    };
+    "4" = {
+      "1" = mkTalent "Fervor" 82726 "ability_hunter_aspectoftheviper";
+      "2" = mkTalent "Dire Beast" 120679 "ability_hunter_sickem";
+      "3" = mkTalent "Thrill of the Hunt" 109306 "ability_hunter_thrillofthehunt"
;
+    };
+    "5" = {
+      "1" = mkTalent "A Murder of Crows" 131894 "ability_hunter_murderofcrows";
+      "2" = mkTalent "Blink Strikes" 130392 "spell_arcane_arcane04";
+      "3" = mkTalent "Lynx Rush" 120697 "ability_hunter_catlikereflexes";
+    };
+    "6" = {
+      "1" = mkTalent "Glaive Toss" 117050 "ability_glaivetoss";
+      "2" = mkTalent "Powershot" 109259 "ability_hunter_resistanceisfutile";
+      "3" = mkTalent "Barrage" 120360 "ability_hunter_rapidregeneration";
+    };
+  };
+
+  ClassMage = {
+    "1" = {
+      "1" = mkTalent "Presence of Mind" 12043 "spell_nature_enchantarmor";
+      "2" = mkTalent "Blazing Speed" 108843 "spell_fire_burningspeed";
+      "3" = mkTalent "Ice Floes" 108839 "spell_mage_iceflows";
+    };
+    "2" = {
+      "1" = mkTalent "Temporal Shield" 115610 "spell_mage_temporalshield";
+      "2" = mkTalent "Flameglow" 140468 "inv_elemental_primal_fire";
+      "3" = mkTalent "Ice Barrier" 11426 "spell_ice_lament";
+    };
+    "3" = {
+      "1" = mkTalent "Ring of Frost" 113724 "spell_frost_ring-of-frost";
+      "2" = mkTalent "Ice Ward" 111264 "spell_frost_frostward";
+      "3" = mkTalent "Frostjaw" 102051 "ability_mage_frostjaw";
+    };
+    "4" = {
+      "1" = mkTalent "Greater Invisibility" 110959 "ability_mage_greaterinvisibil
ity";
+      "2" = mkTalent "Cauterize" 86949 "spell_fire_rune";
+      "3" = mkTalent "Cold Snap" 11958 "spell_frost_wizardmark";
+    };
+    "5" = {
+      "1" = mkTalent "Nether Tempest" 114923 "spell_mage_nethertempest";
+      "2" = mkTalent "Living Bomb" 44457 "ability_mage_livingbomb";
+      "3" = mkTalent "Frost Bomb" 112948 "spell_mage_frostbomb";
+    };
+    "6" = {
+      "1" = mkTalent "Invocation" 114003 "spell_arcane_arcane03";
+      "2" = mkTalent "Rune of Power" 116011 "spell_mage_runeofpower";
+      "3" = mkTalent "Incanter's Ward" 1463 "spell_shadow_detectlesserinvisibilit
y";
+    };
+  };
+
+  ClassMonk = {
+    "1" = {
+      "1" = mkTalent "Celerity" 115173 "ability_monk_quipunch";
+      "2" = mkTalent "Tiger's Lust" 116841 "ability_monk_tigerslust";
+      "3" = mkTalent "Momentum" 115174 "ability_monk_standingkick";
+    };
+    "2" = {
+      "1" = mkTalent "Chi Wave" 115098 "ability_monk_chiwave";
+      "2" = mkTalent "Zen Sphere" 124081 "ability_monk_forcesphere";
+      "3" = mkTalent "Chi Burst" 123986 "spell_arcane_arcanetorrent";
+    };
+    "3" = {
+      "1" = mkTalent "Power Strikes" 121817 "ability_monk_powerstrikes";
+      "2" = mkTalent "Ascension" 115396 "ability_monk_ascension";
+      "3" = mkTalent "Chi Brew" 115399 "ability_monk_chibrew";
+    };
+    "4" = {
+      "1" = mkTalent "Ring of Peace" 116844 "spell_monk_ringofpeace";
+      "2" = mkTalent "Charging Ox Wave" 119392 "ability_monk_chargingoxwave";
+      "3" = mkTalent "Leg Sweep" 119381 "ability_monk_legsweep";
+    };
+    "5" = {
+      "1" = mkTalent "Healing Elixirs" 122280 "ability_monk_jasmineforcetea";
+      "2" = mkTalent "Dampen Harm" 122278 "ability_monk_dampenharm";
+      "3" = mkTalent "Diffuse Magic" 122783 "spell_monk_diffusemagic";
+    };
+    "6" = {
+      "1" = mkTalent "Rushing Jade Wind" 116847 "ability_monk_rushingjadewind";
+      "2" = mkTalent "Invoke Xuen, the White Tiger" 123904 "ability_monk_summonti
gerstatue";
+      "3" = mkTalent "Chi Torpedo" 115008 "ability_monk_quitornado";
+    };
+  };
+
+  ClassPaladin = {
+    "1" = {
+      "1" = mkTalent "Speed of Light" 85499 "ability_paladin_speedoflight";
+      "2" = mkTalent "Long Arm of the Law" 87172 "ability_paladin_longarmofthelaw
";
+      "3" = mkTalent "Pursuit of Justice" 26023 "ability_paladin_veneration";
+    };
+    "2" = {
+      "1" = mkTalent "Fist of Justice" 105593 "spell_holy_fistofjustice";
+      "2" = mkTalent "Repentance" 20066 "spell_holy_prayerofhealing";
+      "3" = mkTalent "Evil is a Point of View" 110301 "ability_paladin_turnevil";
+    };
+    "3" = {
+      "1" = mkTalent "Selfless Healer" 85804 "ability_paladin_gaurdedbythelight";
+      "2" = mkTalent "Eternal Flame" 114163 "inv_torch_thrown";
+      "3" = mkTalent "Sacred Shield" 20925 "ability_paladin_blessedmending";
+    };
+    "4" = {
+      "1" = mkTalent "Hand of Purity" 114039 "spell_holy_sealofwisdom";
+      "2" = mkTalent "Unbreakable Spirit" 114154 "spell_holy_unyieldingfaith";
+      "3" = mkTalent "Clemency" 105622 "ability_paladin_clemency";
+    };
+    "5" = {
+      "1" = mkTalent "Holy Avenger" 105809 "ability_paladin_holyavenger";
+      "2" = mkTalent "Sanctified Wrath" 53376 "ability_paladin_sanctifiedwrath";
+      "3" = mkTalent "Divine Purpose" 86172 "spell_holy_divinepurpose";
+    };
+    "6" = {
+      "1" = mkTalent "Holy Prism" 114165 "spell_paladin_holyprism";
+      "2" = mkTalent "Light's Hammer" 114158 "spell_paladin_lightshammer";
+      "3" = mkTalent "Execution Sentence" 114157 "spell_paladin_executionsentence
";
+    };
+  };
+
+  ClassPriest = {
+    "1" = {
+      "1" = mkTalent "Void Tendrils" 108920 "spell_priest_voidtendrils";
+      "2" = mkTalent "Psyfiend" 108921 "spell_priest_psyfiend";
+      "3" = mkTalent "Dominate Mind" 605 "spell_shadow_shadowworddominate";
+    };
+    "2" = {
+      "1" = mkTalent "Body and Soul" 64129 "spell_holy_symbolofhope";
+      "2" = mkTalent "Angelic Feather" 121536 "ability_priest_angelicfeather";
+      "3" = mkTalent "Phantasm" 108942 "ability_priest_phantasm";
+    };
+    "3" = {
+      "1" = mkTalent "From Darkness, Comes Light" 109186 "spell_holy_surgeoflight
";
+      "2" = mkTalent "Mindbender" 123040 "spell_shadow_soulleech_3";
+      "3" = mkTalent "Solace and Insanity" 139139 "ability_priest_flashoflight";
+    };
+    "4" = {
+      "1" = mkTalent "Desperate Prayer" 19236 "spell_holy_testoffaith";
+      "2" = mkTalent "Spectral Guise" 112833 "spell_priest_spectralguise";
+      "3" = mkTalent "Angelic Bulwark" 108945 "ability_priest_angelicbulwark";
+    };
+    "5" = {
+      "1" = mkTalent "Twist of Fate" 109142 "spell_shadow_mindtwisting";
+      "2" = mkTalent "Power Infusion" 10060 "spell_holy_powerinfusion";
+      "3" = mkTalent "Divine Insight" 109175 "spell_priest_burningwill";
+    };
+    "6" = {
+      "1" = mkTalent "Cascade" 121135 "ability_priest_cascade";
+      "2" = mkTalent "Divine Star" 110744 "spell_priest_divinestar";
+      "3" = mkTalent "Halo" 120517 "ability_priest_halo";
+    };
+  };
+
+  ClassRogue = {
+    "1" = {
+      "1" = mkTalent "Nightstalker" 14062 "ability_stealth";
+      "2" = mkTalent "Subterfuge" 108208 "rogue_subterfuge";
+      "3" = mkTalent "Shadow Focus" 108209 "rogue_shadowfocus";
+    };
+    "2" = {
+      "1" = mkTalent "Deadly Throw" 26679 "inv_throwingknife_06";
+      "2" = mkTalent "Nerve Strike" 108210 "rogue_nerve-_strike";
+      "3" = mkTalent "Combat Readiness" 74001 "ability_rogue_combatreadiness";
+    };
+    "3" = {
+      "1" = mkTalent "Cheat Death" 31230 "ability_rogue_cheatdeath";
+      "2" = mkTalent "Leeching Poison" 108211 "rogue_leeching_poison";
+      "3" = mkTalent "Elusiveness" 79008 "ability_rogue_turnthetables";
+    };
+    "4" = {
+      "1" = mkTalent "Cloak and Dagger" 138106 "ability_rogue_unfairadvantage";
+      "2" = mkTalent "Shadowstep" 36554 "ability_rogue_shadowstep";
+      "3" = mkTalent "Burst of Speed" 108212 "rogue_burstofspeed";
+    };
+    "5" = {
+      "1" = mkTalent "Prey on the Weak" 131511 "ability_rogue_preyontheweak";
+      "2" = mkTalent "Paralytic Poison" 108215 "rogue_paralytic_poison";
+      "3" = mkTalent "Dirty Tricks" 108216 "ability_rogue_dirtydeeds";
+    };
+    "6" = {
+      "1" = mkTalent "Shuriken Toss" 114014 "inv_throwingknife_07";
+      "2" = mkTalent "Marked for Death" 137619 "achievement_bg_killingblow_berser
ker";
+      "3" = mkTalent "Anticipation" 114015 "ability_rogue_slaughterfromtheshadows
";
+    };
+  };
+
+  ClassShaman = {
+    "1" = {
+      "1" = mkTalent "Nature's Guardian" 30884 "spell_nature_natureguardian";
+      "2" = mkTalent "Stone Bulwark Totem" 108270 "ability_shaman_stonebulwark";
+      "3" = mkTalent "Astral Shift" 108271 "ability_shaman_astralshift";
+    };
+    "2" = {
+      "1" = mkTalent "Frozen Power" 63374 "spell_fire_bluecano";
+      "2" = mkTalent "Earthgrab Totem" 51485 "spell_nature_stranglevines";
+      "3" = mkTalent "Windwalk Totem" 108273 "ability_shaman_windwalktotem";
+    };
+    "3" = {
+      "1" = mkTalent "Call of the Elements" 108285 "ability_shaman_multitotemacti
vation";
+      "2" = mkTalent "Totemic Persistence" 108284 "ability_shaman_totemcooldownre
fund";
+      "3" = mkTalent "Totemic Projection" 108287 "ability_shaman_totemrelocation"
;
+    };
+    "4" = {
+      "1" = mkTalent "Elemental Mastery" 16166 "spell_nature_wispheal";
+      "2" = mkTalent "Ancestral Swiftness" 16188 "spell_shaman_elementaloath";
+      "3" = mkTalent "Echo of the Elements" 108283 "ability_shaman_echooftheeleme
nts";
+    };
+    "5" = {
+      "1" = mkTalent "Rushing Streams" 147074 "inv_spear_04";
+      "2" = mkTalent "Ancestral Guidance" 108281 "ability_shaman_ancestralguidanc
e";
+      "3" = mkTalent "Conductivity" 108282 "ability_shaman_fortifyingwaters";
+    };
+    "6" = {
+      "1" = mkTalent "Unleashed Fury" 117012 "shaman_talent_unleashedfury";
+      "2" = mkTalent "Primal Elementalist" 117013 "shaman_talent_primalelementali
st";
+      "3" = mkTalent "Elemental Blast" 117014 "shaman_talent_elementalblast";
+    };
+  };
+
+  ClassWarlock = {
+    "1" = {
+      "1" = mkTalent "Dark Regeneration" 108359 "spell_warlock_darkregeneration";
+      "2" = mkTalent "Soul Leech" 108370 "warlock_siphonlife";
+      "3" = mkTalent "Harvest Life" 108371 "spell_warlock_harvestoflife";
+    };
+    "2" = {
+      "1" = mkTalent "Demonic Breath" 47897 "ability_warlock_shadowflame";
+      "2" = mkTalent "Mortal Coil" 6789 "ability_warlock_mortalcoil";
+      "3" = mkTalent "Shadowfury" 30283 "ability_warlock_shadowfurytga";
+    };
+    "3" = {
+      "1" = mkTalent "Soul Link" 108415 "ability_warlock_soullink";
+      "2" = mkTalent "Sacrificial Pact" 108416 "warlock_sacrificial_pact";
+      "3" = mkTalent "Dark Bargain" 110913 "ability_deathwing_bloodcorruption_dea
th";
+    };
+    "4" = {
+      "1" = mkTalent "Blood Horror" 111397 "ability_deathwing_bloodcorruption_ear
th";
+      "2" = mkTalent "Burning Rush" 111400 "ability_deathwing_sealarmorbreachtga"
;
+      "3" = mkTalent "Unbound Will" 108482 "warlock_spelldrain";
+    };
+    "5" = {
+      "1" = mkTalent "Grimoire of Supremacy" 108499 "warlock_grimoireofcommand";
+      "2" = mkTalent "Grimoire of Service" 108501 "warlock_grimoireofservice";
+      "3" = mkTalent "Grimoire of Sacrifice" 108503 "warlock_grimoireofsacrifice"
;
+    };
+    "6" = {
+      "1" = mkTalent "Archimonde's Darkness" 108505 "achievement_boss_archimonde-
";
+      "2" = mkTalent "Kil'jaeden's Cunning" 137587 "achievement_boss_kiljaedan";
+      "3" = mkTalent "Mannoroth's Fury" 108508 "achievement_boss_magtheridon";
+    };
+  };
+
+  ClassWarrior = {
+    "1" = {
+      "1" = mkTalent "Juggernaut" 103826 "ability_warrior_bullrush";
+      "2" = mkTalent "Double Time" 103827 "inv_misc_horn_04";
+      "3" = mkTalent "Warbringer" 103828 "ability_warrior_warbringer";
+    };
+    "2" = {
+      "1" = mkTalent "Enraged Regeneration" 55694 "ability_warrior_focusedrage";
+      "2" = mkTalent "Second Wind" 29838 "ability_hunter_harass";
+      "3" = mkTalent "Impending Victory" 103840 "spell_impending_victory";
+    };
+    "3" = {
+      "1" = mkTalent "Staggering Shout" 107566 "ability_bullrush";
+      "2" = mkTalent "Piercing Howl" 12323 "spell_shadow_deathscream";
+      "3" = mkTalent "Disrupting Shout" 102060 "warrior_disruptingshout";
+    };
+    "4" = {
+      "1" = mkTalent "Bladestorm" 46924 "ability_warrior_bladestorm";
+      "2" = mkTalent "Shockwave" 46968 "ability_warrior_shockwave";
+      "3" = mkTalent "Dragon Roar" 118000 "ability_warrior_dragonroar";
+    };
+    "5" = {
+      "1" = mkTalent "Mass Spell Reflection" 114028 "ability_warrior_shieldbreak"
;
+      "2" = mkTalent "Safeguard" 114029 "ability_warrior_safeguard";
+      "3" = mkTalent "Vigilance" 114030 "ability_warrior_vigilance";
+    };
+    "6" = {
+      "1" = mkTalent "Avatar" 107574 "warrior_talent_icon_avatar";
+      "2" = mkTalent "Bloodbath" 12292 "ability_warrior_bloodbath";
+      "3" = mkTalent "Storm Bolt" 107570 "warrior_talent_icon_stormbolt";
+    };
+  };
+}
+

diff --git a/nix/lib/database/talents.nix b/
nix/lib/database/talents.nix
new file mode 100644
index 0000000..a3caff9
--- /dev/null
+++ b/nix/lib/database/talents.nix
@@ -0,0 +1,371 @@
+let
+  mkTalent = name: spellId: icon: {
+    inherit name spellId icon;
+  };
+in {
+  # talent string format: "123456" where ea
ch digit represents the choice (1-3) for eac
h tier (1-6)
+
+  ClassDeathKnight = {
+    "1" = {
+      "1" = mkTalent "Roiling Blood" 108170
 "ability_deathknight_roilingblood";
+      "2" = mkTalent "Plague Leech" 123693 
"ability_creature_disease_02";
+      "3" = mkTalent "Unholy Blight" 115989
 "spell_shadow_contagion";
+    };
+    "2" = {
+      "1" = mkTalent "Lichborne" 49039 "spe
ll_shadow_raisedead";
+      "2" = mkTalent "Anti-Magic Zone" 5105
2 "spell_deathknight_antimagiczone";
+      "3" = mkTalent "Purgatory" 114556 "in
v_misc_shadowegg";
+    };
+    "3" = {
+      "1" = mkTalent "Death's Advance" 9626
8 "spell_shadow_demonicempathy";
+      "2" = mkTalent "Chilblains" 50041 "sp
ell_frost_wisp";
+      "3" = mkTalent "Asphyxiate" 108194 "a
bility_deathknight_asphixiate";
+    };
+    "4" = {
+      "1" = mkTalent "Death Pact" 48743 "sp
ell_shadow_deathpact";
+      "2" = mkTalent "Death Siphon" 108196 
"ability_deathknight_deathsiphon";
+      "3" = mkTalent "Conversion" 119975 "a
bility_deathknight_deathsiphon2";
+    };
+    "5" = {
+      "1" = mkTalent "Blood Tap" 45529 "spe
ll_deathknight_bloodtap";
+      "2" = mkTalent "Runic Empowerment" 81
229 "inv_misc_rune_10";
+      "3" = mkTalent "Runic Corruption" 514
62 "spell_shadow_rune";
+    };
+    "6" = {
+      "1" = mkTalent "Gorefiend's Grasp" 10
8199 "ability_deathknight_aoedeathgrip";
+      "2" = mkTalent "Remorseless Winter" 1
08200 "ability_deathknight_remorselesswinter
s2";
+      "3" = mkTalent "Desecrated Ground" 10
8201 "ability_deathknight_desecratedground";
+    };
+  };
+
+  ClassDruid = {
+    "1" = {
+      "1" = mkTalent "Feline Swiftness" 131
768 "spell_druid_tirelesspursuit";
+      "2" = mkTalent "Displacer Best" 10228
0 "spell_druid_displacement";
+      "3" = mkTalent "Wild Charge" 102401 "
spell_druid_wildcharge";
+    };
+    "2" = {
+      "1" = mkTalent "Ysera's Gift" 145108 
"inv_misc_head_dragon_green";
+      "2" = mkTalent "Renewal" 108238 "spel
l_nature_natureblessing";
+      "3" = mkTalent "Cenarion Ward" 102351
 "ability_druid_naturalperfection";
+    };
+    "3" = {
+      "1" = mkTalent "Faerie Swarm" 106707 
"spell_druid_swarm";
+      "2" = mkTalent "Mass Entanglement" 10
2359 "spell_druid_massentanglement";
+      "3" = mkTalent "Typhoon" 132469 "abil
ity_druid_typhoon";
+    };
+    "4" = {
+      "1" = mkTalent "Soul of the Forest" 1
14107 "ability_druid_manatree";
+      "2" = mkTalent "Incarnation" 106731 "
spell_druid_incarnation";
+      "3" = mkTalent "Force of Nature" 1067
37 "ability_druid_forceofnature";
+    };
+    "5" = {
+      "1" = mkTalent "Disorienting Roar" 99
 "classic_ability_druid_demoralizingroar";
+      "2" = mkTalent "Ursol's Vortex" 10279
3 "spell_druid_ursolsvortex";
+      "3" = mkTalent "Mighty Bash" 5211 "ab
ility_druid_bash";
+    };
+    "6" = {
+      "1" = mkTalent "Heart of the Wild" 10
8288 "spell_holy_blessingofagility";
+      "2" = mkTalent "Dream of Cenarius" 10
8373 "ability_druid_dreamstate";
+      "3" = mkTalent "Nature's Vigil" 12497
4 "achievement_zone_feralas";
+    };
+  };
+
+  ClassHunter = {
+    "1" = {
+      "1" = mkTalent "Posthaste" 109215 "ab
ility_hunter_posthaste";
+      "2" = mkTalent "Narrow Escape" 109298
 "inv_misc_web_01";
+      "3" = mkTalent "Crouching Tiger, Hidd
en Chimera" 118675 "ability_hunter_pet_chime
ra";
+    };
+    "2" = {
+      "1" = mkTalent "Binding Shot" 109248 
"spell_shaman_bindelemental";
+      "2" = mkTalent "Wyvern Sting" 19386 "
inv_spear_02";
+      "3" = mkTalent "Intimidation" 19577 "
ability_devour";
+    };
+    "3" = {
+      "1" = mkTalent "Exhilaration" 109304 
"ability_hunter_onewithnature";
+      "2" = mkTalent "Aspect of the Iron Ha
wk" 109260 "spell_hunter_aspectoftheironhawk
";
+      "3" = mkTalent "Spirit Bond" 109212 "
classic_ability_druid_demoralizingroar";
+    };
+    "4" = {
+      "1" = mkTalent "Fervor" 82726 "abilit
y_hunter_aspectoftheviper";
+      "2" = mkTalent "Dire Beast" 120679 "a
bility_hunter_sickem";
+      "3" = mkTalent "Thrill of the Hunt" 1
09306 "ability_hunter_thrillofthehunt";
+    };
+    "5" = {
+      "1" = mkTalent "A Murder of Crows" 13
1894 "ability_hunter_murderofcrows";
+      "2" = mkTalent "Blink Strikes" 130392
 "spell_arcane_arcane04";
+      "3" = mkTalent "Lynx Rush" 120697 "ab
ility_hunter_catlikereflexes";
+    };
+    "6" = {
+      "1" = mkTalent "Glaive Toss" 117050 "
ability_glaivetoss";
+      "2" = mkTalent "Powershot" 109259 "ab
ility_hunter_resistanceisfutile";
+      "3" = mkTalent "Barrage" 120360 "abil
ity_hunter_rapidregeneration";
+    };
+  };
+
+  ClassMage = {
+    "1" = {
+      "1" = mkTalent "Presence of Mind" 120
43 "spell_nature_enchantarmor";
+      "2" = mkTalent "Blazing Speed" 108843
 "spell_fire_burningspeed";
+      "3" = mkTalent "Ice Floes" 108839 "sp
ell_mage_iceflows";
+    };
+    "2" = {
+      "1" = mkTalent "Temporal Shield" 1156
10 "spell_mage_temporalshield";
+      "2" = mkTalent "Flameglow" 140468 "in
v_elemental_primal_fire";
+      "3" = mkTalent "Ice Barrier" 11426 "s
pell_ice_lament";
+    };
+    "3" = {
+      "1" = mkTalent "Ring of Frost" 113724
 "spell_frost_ring-of-frost";
+      "2" = mkTalent "Ice Ward" 111264 "spe
ll_frost_frostward";
+      "3" = mkTalent "Frostjaw" 102051 "abi
lity_mage_frostjaw";
+    };
+    "4" = {
+      "1" = mkTalent "Greater Invisibility"
 110959 "ability_mage_greaterinvisibility";
+      "2" = mkTalent "Cauterize" 86949 "spe
ll_fire_rune";
+      "3" = mkTalent "Cold Snap" 11958 "spe
ll_frost_wizardmark";
+    };
+    "5" = {
+      "1" = mkTalent "Nether Tempest" 11492
3 "spell_mage_nethertempest";
+      "2" = mkTalent "Living Bomb" 44457 "a
bility_mage_livingbomb";
+      "3" = mkTalent "Frost Bomb" 112948 "s
pell_mage_frostbomb";
+    };
+    "6" = {
+      "1" = mkTalent "Invocation" 114003 "s
pell_arcane_arcane03";
+      "2" = mkTalent "Rune of Power" 116011
 "spell_mage_runeofpower";
+      "3" = mkTalent "Incanter's Ward" 1463
 "spell_shadow_detectlesserinvisibility";
+    };
+  };
+
+  ClassMonk = {
+    "1" = {
+      "1" = mkTalent "Celerity" 115173 "abi
lity_monk_quipunch";
+      "2" = mkTalent "Tiger's Lust" 116841 
"ability_monk_tigerslust";
+      "3" = mkTalent "Momentum" 115174 "abi
lity_monk_standingkick";
+    };
+    "2" = {
+      "1" = mkTalent "Chi Wave" 115098 "abi
lity_monk_chiwave";
+      "2" = mkTalent "Zen Sphere" 124081 "a
bility_monk_forcesphere";
+      "3" = mkTalent "Chi Burst" 123986 "sp
ell_arcane_arcanetorrent";
+    };
+    "3" = {
+      "1" = mkTalent "Power Strikes" 121817
 "ability_monk_powerstrikes";
+      "2" = mkTalent "Ascension" 115396 "ab
ility_monk_ascension";
+      "3" = mkTalent "Chi Brew" 115399 "abi
lity_monk_chibrew";
+    };
+    "4" = {
+      "1" = mkTalent "Ring of Peace" 116844
 "spell_monk_ringofpeace";
+      "2" = mkTalent "Charging Ox Wave" 119
392 "ability_monk_chargingoxwave";
+      "3" = mkTalent "Leg Sweep" 119381 "ab
ility_monk_legsweep";
+    };
+    "5" = {
+      "1" = mkTalent "Healing Elixirs" 1222
80 "ability_monk_jasmineforcetea";
+      "2" = mkTalent "Dampen Harm" 122278 "
ability_monk_dampenharm";
+      "3" = mkTalent "Diffuse Magic" 122783
 "spell_monk_diffusemagic";
+    };
+    "6" = {
+      "1" = mkTalent "Rushing Jade Wind" 11
6847 "ability_monk_rushingjadewind";
+      "2" = mkTalent "Invoke Xuen, the Whit
e Tiger" 123904 "ability_monk_summontigersta
tue";
+      "3" = mkTalent "Chi Torpedo" 115008 "
ability_monk_quitornado";
+    };
+  };
+
+  ClassPaladin = {
+    "1" = {
+      "1" = mkTalent "Speed of Light" 85499
 "ability_paladin_speedoflight";
+      "2" = mkTalent "Long Arm of the Law" 
87172 "ability_paladin_longarmofthelaw";
+      "3" = mkTalent "Pursuit of Justice" 2
6023 "ability_paladin_veneration";
+    };
+    "2" = {
+      "1" = mkTalent "Fist of Justice" 1055
93 "spell_holy_fistofjustice";
+      "2" = mkTalent "Repentance" 20066 "sp
ell_holy_prayerofhealing";
+      "3" = mkTalent "Evil is a Point of Vi
ew" 110301 "ability_paladin_turnevil";
+    };
+    "3" = {
+      "1" = mkTalent "Selfless Healer" 8580
4 "ability_paladin_gaurdedbythelight";
+      "2" = mkTalent "Eternal Flame" 114163
 "inv_torch_thrown";
+      "3" = mkTalent "Sacred Shield" 20925 
"ability_paladin_blessedmending";
+    };
+    "4" = {
+      "1" = mkTalent "Hand of Purity" 11403
9 "spell_holy_sealofwisdom";
+      "2" = mkTalent "Unbreakable Spirit" 1
14154 "spell_holy_unyieldingfaith";
+      "3" = mkTalent "Clemency" 105622 "abi
lity_paladin_clemency";
+    };
+    "5" = {
+      "1" = mkTalent "Holy Avenger" 105809 
"ability_paladin_holyavenger";
+      "2" = mkTalent "Sanctified Wrath" 533
76 "ability_paladin_sanctifiedwrath";
+      "3" = mkTalent "Divine Purpose" 86172
 "spell_holy_divinepurpose";
+    };
+    "6" = {
+      "1" = mkTalent "Holy Prism" 114165 "s
pell_paladin_holyprism";
+      "2" = mkTalent "Light's Hammer" 11415
8 "spell_paladin_lightshammer";
+      "3" = mkTalent "Execution Sentence" 1
14157 "spell_paladin_executionsentence";
+    };
+  };
+
+  ClassPriest = {
+    "1" = {
+      "1" = mkTalent "Void Tendrils" 108920
 "spell_priest_voidtendrils";
+      "2" = mkTalent "Psyfiend" 108921 "spe
ll_priest_psyfiend";
+      "3" = mkTalent "Dominate Mind" 605 "s
pell_shadow_shadowworddominate";
+    };
+    "2" = {
+      "1" = mkTalent "Body and Soul" 64129 
"spell_holy_symbolofhope";
+      "2" = mkTalent "Angelic Feather" 1215
36 "ability_priest_angelicfeather";
+      "3" = mkTalent "Phantasm" 108942 "abi
lity_priest_phantasm";
+    };
+    "3" = {
+      "1" = mkTalent "From Darkness, Comes 
Light" 109186 "spell_holy_surgeoflight";
+      "2" = mkTalent "Mindbender" 123040 "s
pell_shadow_soulleech_3";
+      "3" = mkTalent "Solace and Insanity" 
139139 "ability_priest_flashoflight";
+    };
+    "4" = {
+      "1" = mkTalent "Desperate Prayer" 192
36 "spell_holy_testoffaith";
+      "2" = mkTalent "Spectral Guise" 11283
3 "spell_priest_spectralguise";
+      "3" = mkTalent "Angelic Bulwark" 1089
45 "ability_priest_angelicbulwark";
+    };
+    "5" = {
+      "1" = mkTalent "Twist of Fate" 109142
 "spell_shadow_mindtwisting";
+      "2" = mkTalent "Power Infusion" 10060
 "spell_holy_powerinfusion";
+      "3" = mkTalent "Divine Insight" 10917
5 "spell_priest_burningwill";
+    };
+    "6" = {
+      "1" = mkTalent "Cascade" 121135 "abil
ity_priest_cascade";
+      "2" = mkTalent "Divine Star" 110744 "
spell_priest_divinestar";
+      "3" = mkTalent "Halo" 120517 "ability
_priest_halo";
+    };
+  };
+
+  ClassRogue = {
+    "1" = {
+      "1" = mkTalent "Nightstalker" 14062 "
ability_stealth";
+      "2" = mkTalent "Subterfuge" 108208 "r
ogue_subterfuge";
+      "3" = mkTalent "Shadow Focus" 108209 
"rogue_shadowfocus";
+    };
+    "2" = {
+      "1" = mkTalent "Deadly Throw" 26679 "
inv_throwingknife_06";
+      "2" = mkTalent "Nerve Strike" 108210 
"rogue_nerve-_strike";
+      "3" = mkTalent "Combat Readiness" 740
01 "ability_rogue_combatreadiness";
+    };
+    "3" = {
+      "1" = mkTalent "Cheat Death" 31230 "a
bility_rogue_cheatdeath";
+      "2" = mkTalent "Leeching Poison" 1082
11 "rogue_leeching_poison";
+      "3" = mkTalent "Elusiveness" 79008 "a
bility_rogue_turnthetables";
+    };
+    "4" = {
+      "1" = mkTalent "Cloak and Dagger" 138
106 "ability_rogue_unfairadvantage";
+      "2" = mkTalent "Shadowstep" 36554 "ab
ility_rogue_shadowstep";
+      "3" = mkTalent "Burst of Speed" 10821
2 "rogue_burstofspeed";
+    };
+    "5" = {
+      "1" = mkTalent "Prey on the Weak" 131
511 "ability_rogue_preyontheweak";
+      "2" = mkTalent "Paralytic Poison" 108
215 "rogue_paralytic_poison";
+      "3" = mkTalent "Dirty Tricks" 108216 
"ability_rogue_dirtydeeds";
+    };
+    "6" = {
+      "1" = mkTalent "Shuriken Toss" 114014
 "inv_throwingknife_07";
+      "2" = mkTalent "Marked for Death" 137
619 "achievement_bg_killingblow_berserker";
+      "3" = mkTalent "Anticipation" 114015 
"ability_rogue_slaughterfromtheshadows";
+    };
+  };
+
+  ClassShaman = {
+    "1" = {
+      "1" = mkTalent "Nature's Guardian" 30
884 "spell_nature_natureguardian";
+      "2" = mkTalent "Stone Bulwark Totem" 
108270 "ability_shaman_stonebulwark";
+      "3" = mkTalent "Astral Shift" 108271 
"ability_shaman_astralshift";
+    };
+    "2" = {
+      "1" = mkTalent "Frozen Power" 63374 "
spell_fire_bluecano";
+      "2" = mkTalent "Earthgrab Totem" 5148
5 "spell_nature_stranglevines";
+      "3" = mkTalent "Windwalk Totem" 10827
3 "ability_shaman_windwalktotem";
+    };
+    "3" = {
+      "1" = mkTalent "Call of the Elements"
 108285 "ability_shaman_multitotemactivation
";
+      "2" = mkTalent "Totemic Persistence" 
108284 "ability_shaman_totemcooldownrefund";
+      "3" = mkTalent "Totemic Projection" 1
08287 "ability_shaman_totemrelocation";
+    };
+    "4" = {
+      "1" = mkTalent "Elemental Mastery" 16
166 "spell_nature_wispheal";
+      "2" = mkTalent "Ancestral Swiftness" 
16188 "spell_shaman_elementaloath";
+      "3" = mkTalent "Echo of the Elements"
 108283 "ability_shaman_echooftheelements";
+    };
+    "5" = {
+      "1" = mkTalent "Rushing Streams" 1470
74 "inv_spear_04";
+      "2" = mkTalent "Ancestral Guidance" 1
08281 "ability_shaman_ancestralguidance";
+      "3" = mkTalent "Conductivity" 108282 
"ability_shaman_fortifyingwaters";
+    };
+    "6" = {
+      "1" = mkTalent "Unleashed Fury" 11701
2 "shaman_talent_unleashedfury";
+      "2" = mkTalent "Primal Elementalist" 
117013 "shaman_talent_primalelementalist";
+      "3" = mkTalent "Elemental Blast" 1170
14 "shaman_talent_elementalblast";
+    };
+  };
+
+  ClassWarlock = {
+    "1" = {
+      "1" = mkTalent "Dark Regeneration" 10
8359 "spell_warlock_darkregeneration";
+      "2" = mkTalent "Soul Leech" 108370 "w
arlock_siphonlife";
+      "3" = mkTalent "Harvest Life" 108371 
"spell_warlock_harvestoflife";
+    };
+    "2" = {
+      "1" = mkTalent "Demonic Breath" 47897
 "ability_warlock_shadowflame";
+      "2" = mkTalent "Mortal Coil" 6789 "ab
ility_warlock_mortalcoil";
+      "3" = mkTalent "Shadowfury" 30283 "ab
ility_warlock_shadowfurytga";
+    };
+    "3" = {
+      "1" = mkTalent "Soul Link" 108415 "ab
ility_warlock_soullink";
+      "2" = mkTalent "Sacrificial Pact" 108
416 "warlock_sacrificial_pact";
+      "3" = mkTalent "Dark Bargain" 110913 
"ability_deathwing_bloodcorruption_death";
+    };
+    "4" = {
+      "1" = mkTalent "Blood Horror" 111397 
"ability_deathwing_bloodcorruption_earth";
+      "2" = mkTalent "Burning Rush" 111400 
"ability_deathwing_sealarmorbreachtga";
+      "3" = mkTalent "Unbound Will" 108482 
"warlock_spelldrain";
+    };
+    "5" = {
+      "1" = mkTalent "Grimoire of Supremacy
" 108499 "warlock_grimoireofcommand";
+      "2" = mkTalent "Grimoire of Service" 
108501 "warlock_grimoireofservice";
+      "3" = mkTalent "Grimoire of Sacrifice
" 108503 "warlock_grimoireofsacrifice";
+    };
+    "6" = {
+      "1" = mkTalent "Archimonde's Darkness
" 108505 "achievement_boss_archimonde-";
+      "2" = mkTalent "Kil'jaeden's Cunning"
 137587 "achievement_boss_kiljaedan";
+      "3" = mkTalent "Mannoroth's Fury" 108
508 "achievement_boss_magtheridon";
+    };
+  };
+
+  ClassWarrior = {
+    "1" = {
+      "1" = mkTalent "Juggernaut" 103826 "a
bility_warrior_bullrush";
+      "2" = mkTalent "Double Time" 103827 "
inv_misc_horn_04";
+      "3" = mkTalent "Warbringer" 103828 "a
bility_warrior_warbringer";
+    };
+    "2" = {
+      "1" = mkTalent "Enraged Regeneration"
 55694 "ability_warrior_focusedrage";
+      "2" = mkTalent "Second Wind" 29838 "a
bility_hunter_harass";
+      "3" = mkTalent "Impending Victory" 10
3840 "spell_impending_victory";
+    };
+    "3" = {
+      "1" = mkTalent "Staggering Shout" 107
566 "ability_bullrush";
+      "2" = mkTalent "Piercing Howl" 12323 
"spell_shadow_deathscream";
+      "3" = mkTalent "Disrupting Shout" 102
060 "warrior_disruptingshout";
+    };
+    "4" = {
+      "1" = mkTalent "Bladestorm" 46924 "ab
ility_warrior_bladestorm";
+      "2" = mkTalent "Shockwave" 46968 "abi
lity_warrior_shockwave";
+      "3" = mkTalent "Dragon Roar" 118000 "
ability_warrior_dragonroar";
+    };
+    "5" = {
+      "1" = mkTalent "Mass Spell Reflection
" 114028 "ability_warrior_shieldbreak";
+      "2" = mkTalent "Safeguard" 114029 "ab
ility_warrior_safeguard";
+      "3" = mkTalent "Vigilance" 114030 "ab
ility_warrior_vigilance";
+    };
+    "6" = {
+      "1" = mkTalent "Avatar" 107574 "warri
or_talent_icon_avatar";
+      "2" = mkTalent "Bloodbath" 12292 "abi
lity_warrior_bloodbath";
+      "3" = mkTalent "Storm Bolt" 107570 "w
arrior_talent_icon_stormbolt";
+    };
+  };
+}
+
 

diff --git a/nix/lib/itemDatabase.nix b/nix/lib/itemDatabase.nix
index b0b9b4f..d00438e 100644
--- a/nix/lib/itemDatabase.nix
+++ b/nix/lib/itemDatabase.nix
@@ -4,6 +4,8 @@
   ...
 }: let
   wowsimsDb = lib.importJSON "${inputs.wowsims}/assets/database/db.json";
+  glyphDb = import ./database/glyphs.nix;
+  talentDb = import ./database/talents.nix;
 
   itemsById = lib.listToAttrs (map (item: {
       name = toString item.id;
@@ -17,6 +19,12 @@
     })
     wowsimsDb.gems);
 
+  consumablesById = lib.listToAttrs (map (consumable: {
+      name = toString consumable.id;
+      value = consumable;
+    })
+    wowsimsDb.consumables);
+
   glyphsById = lib.listToAttrs (lib.imap0 (index: glyph: {
       name = toString index;
       value = glyph;
@@ -29,50 +37,7 @@
     })
     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";
-    };
-  };
+  reforgeData = lib.importJSON "${inputs.wowsims}/assets/db_inputs/wowhead_reforg
e_stats.json";
 
   # stat name mappings (for reforging)
   statNames = {
@@ -103,7 +68,7 @@
   };
   # Helper functions for item database operations
   helpers = {
-    # get item data by ID  
+    # get item data by ID
     getItem = id: let
       idStr = toString id;
     in
@@ -119,6 +84,14 @@
       then gemsById.${idStr}
       else null;
 
+    # get consumable data by ID
+    getConsumable = id: let
+      idStr = toString id;
+    in
+      if consumablesById ? ${idStr}
+      then consumablesById.${idStr}
+      else null;
+
     # get enchant data by effect ID
     getEnchant = effectId: let
       idStr = toString effectId;
@@ -135,7 +108,7 @@
       then reforgeData.${idStr}
       else null;
 
-    # get glyph data by ID
+    # get glyph data by ID (from wowsims database)
     getGlyph = glyphId: let
       idStr = toString glyphId;
     in
@@ -143,6 +116,40 @@
       then glyphsById.${idStr}
       else null;
 
+    # get glyph data from our custom glyph database
+    getGlyphData = className: glyphId: let
+      idStr = toString glyphId;
+      classGlyphs = glyphDb.${className} or {};
+    in
+      if classGlyphs ? ${idStr}
+      then classGlyphs.${idStr}
+      else null;
+
+    # get talent data from talent database
+    getTalentData = className: tier: choice: let
+      tierStr = toString tier;
+      choiceStr = toString choice;
+      classTalents = talentDb.${className} or {};
+      tierTalents = classTalents.${tierStr} or {};
+    in
+      if tierTalents ? ${choiceStr}
+      then tierTalents.${choiceStr}
+      else null;
+
+    # parse talent string into individual choices
+    parseTalentString = talentString: let
+      # Convert string to list of characters, then to list of numbers
+      chars = lib.stringToCharacters talentString;
+      # Convert each character to a number (tier choice 1-3)
+      choices = map (char: 
+        let num = lib.toInt char;
+        in if num >= 1 && num <= 3 then num else 0
+      ) chars;
+      # Create list of {tier, choice} objects, filtering out invalid choices (0)
+      indexedChoices = lib.imap1 (tier: choice: {inherit tier choice;}) choices;
+      validChoices = lib.filter (choice: choice.choice != 0) indexedChoices;
+    in validChoices;
+
     # parse gem stats into readable format
     parseGemStats = gem:
       if gem ? stats
@@ -172,14 +179,19 @@
           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;
+          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;
@@ -217,40 +229,61 @@
 
     # enrich a single equipment item
     enrichItem = item:
-      if !(item ? id) then item
+      if !(item ? id)
+      then item
       else let
         itemData = helpers.getItem item.id;
-        enrichedGems = if item ? gems 
+        enrichedGems =
+          if item ? gems
           then lib.filter (gem: gem != null) (map helpers.enrichGem item.gems)
           else [];
-        enrichedEnchant = if item ? enchant 
+        enrichedEnchant =
+          if item ? enchant
           then helpers.enrichEnchant item.enchant
           else null;
-        enrichedReforge = if item ? reforging 
+        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"
+        itemStats =
+          if itemData != null && itemData ? scalingOptions && itemData.scalingOpt
ions ? "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) {
+        item
+        // {
+          name =
+            if itemData != null
+            then itemData.name
+            else "Item ${toString item.id}";
+          icon =
+            if itemData != null && itemData ? icon
+            then itemData.icon
+            else null;
+          quality =
+            if itemData != null && itemData ? quality
+            then itemData.quality
+            else null;
+          type =
+            if itemData != null && itemData ? type
+            then itemData.type
+            else null;
+        }
+        // lib.optionalAttrs (itemStats != null) {
           stats = itemStats;
-        } // lib.optionalAttrs (enrichedGems != []) {
+        }
+        // lib.optionalAttrs (enrichedGems != []) {
           gems = enrichedGems;
-        } // lib.optionalAttrs (enrichedEnchant != null) {
+        }
+        // lib.optionalAttrs (enrichedEnchant != null) {
           enchant = enrichedEnchant;
-        } // lib.optionalAttrs (enrichedReforge != null) {
+        }
+        // lib.optionalAttrs (enrichedReforge != null) {
           reforging = enrichedReforge;
         };
   };
-
 in {
   # get item data by ID
   getItem = helpers.getItem;
@@ -311,9 +344,14 @@ in {
   # get glyph name by ID
   getGlyphName = glyphId: let
     glyph = helpers.getGlyph glyphId;
-    item = if glyph != null then helpers.getItem glyph.itemId else null;
+    item =
+      if glyph != null
+      then helpers.getItem glyph.itemId
+      else null;
   in
-    if item != null then item.name else "Unknown Glyph ${toString glyphId}";
+    if item != null
+    then item.name
+    else "Unknown Glyph ${toString glyphId}";
 
   # enrich equipment item with name and other data
   enrichEquipmentItem = helpers.enrichItem;
@@ -330,116 +368,146 @@ in {
 
   # enrich consumables with item names and icons
   enrichConsumables = consumables:
-    if consumables == null then null
+    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}";
+      getConsumableDataSafe = id: let
+        consumable = helpers.getConsumable id;
+      in
+        if consumable != null
+        then {
+          name = consumable.name;
+          icon =
+            if consumable ? icon
+            then consumable.icon
+            else null;
+        }
+        else {
+          name = "Consumable ${toString id}";
           icon = null;
-          quality = null;
         };
     in
-      consumables // lib.optionalAttrs (consumables ? flaskId && consumables.flas
kId != 0) (
-        let flaskData = getItemDataSafe consumables.flaskId; in {
+      consumables
+      // lib.optionalAttrs (consumables ? flaskId && consumables.flaskId != 0) (
+        let
+          flaskData = getConsumableDataSafe 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 {
+      )
+      // lib.optionalAttrs (consumables ? foodId && consumables.foodId != 0) (
+        let
+          foodData = getConsumableDataSafe 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 {
+      )
+      // lib.optionalAttrs (consumables ? potId && consumables.potId != 0) (
+        let
+          potData = getConsumableDataSafe 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 {
+      )
+      // lib.optionalAttrs (consumables ? prepotId && consumables.prepotId != 0) 
(
+        let
+          prepotData = getConsumableDataSafe consumables.prepotId;
+        in {
           prepotName = prepotData.name;
           prepotIcon = prepotData.icon;
-          prepotQuality = prepotData.quality;
         }
       );
 
-  # enrich glyphs with item names and icons
-  enrichGlyphs = glyphs:
+  # enrich glyphs with names and icons from glyph database
+  enrichGlyphs = className: 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;
+        glyphData = helpers.getGlyphData className glyphId;
       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;
+        if glyphData != null then {
+          name = glyphData.name;
+          icon = if glyphData ? icon then glyphData.icon else null;
+          spellId = if glyphData ? spellId then glyphData.spellId else null;
         } else {
           name = "Glyph ${toString glyphId}";
           icon = null;
-          quality = null;
           spellId = null;
         };
     in
-      glyphs // lib.optionalAttrs (glyphs ? major1 && glyphs.major1 != 0) (
+      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 talents with names and icons from talent database
+  enrichTalents = className: talentString:
+    if talentString == null || talentString == ""
+    then null
+    else let
+      # Parse talent string into tier/choice pairs
+      talentChoices = helpers.parseTalentString talentString;
+      
+      # Get talent data for each choice and create enriched talent objects
+      enrichedTalents = map (choice: let
+        talentData = helpers.getTalentData className choice.tier choice.choice;
+      in {
+        tier = choice.tier;
+        choice = choice.choice;
+        name = if talentData != null then talentData.name else "Unknown Talent";
+        spellId = if talentData != null then talentData.spellId else null;
+        icon = if talentData != null then talentData.icon else null;
+      }) talentChoices;
+      
+    in {
+      talentString = talentString;
+      talents = enrichedTalents;
+    };
+
   # enrich a full loadout
   enrichLoadout = loadout:
     if loadout ? equipment
     then
       loadout
       // {
-        equipment = 
+        equipment =
           if loadout.equipment ? items
           then
             loadout.equipment
@@ -464,15 +532,16 @@ in {
               then
                 result
                 // {
-                  loadout = 
+                  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;
-                        };
+                        equipment =
+                          result.loadout.equipment
+                          // {
+                            items = map helpers.enrichItem result.loadout.equipme
nt.items;
+                          };
                         # TODO: Add consumables, gems, enchants enrichment
                       }
                     else result.loadout;
@@ -483,4 +552,3 @@ in {
       }
     else simData;
 }
-

diff --git a/nix/lib/itemDatabase.nix b/nix/
lib/itemDatabase.nix
index b0b9b4f..d00438e 100644
--- a/nix/lib/itemDatabase.nix
+++ b/nix/lib/itemDatabase.nix
@@ -4,6 +4,8 @@
   ...
 }: let
   wowsimsDb = lib.importJSON "${inputs.wows
ims}/assets/database/db.json";
+  glyphDb = import ./database/glyphs.nix;
+  talentDb = import ./database/talents.nix;
 
   itemsById = lib.listToAttrs (map (item: {
       name = toString item.id;
@@ -17,6 +19,12 @@
     })
     wowsimsDb.gems);
 
+  consumablesById = lib.listToAttrs (map (c
onsumable: {
+      name = toString consumable.id;
+      value = consumable;
+    })
+    wowsimsDb.consumables);
+
   glyphsById = lib.listToAttrs (lib.imap0 (
index: glyph: {
       name = toString index;
       value = glyph;
@@ -29,50 +37,7 @@
     })
     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";
-    };
-  };
+  reforgeData = lib.importJSON "${inputs.wo
wsims}/assets/db_inputs/wowhead_reforge_stat
s.json";
 
   # stat name mappings (for reforging)
   statNames = {
@@ -103,7 +68,7 @@
   };
   # Helper functions for item database oper
ations
   helpers = {
-    # get item data by ID  
+    # get item data by ID
     getItem = id: let
       idStr = toString id;
     in
@@ -119,6 +84,14 @@
       then gemsById.${idStr}
       else null;
 
+    # get consumable data by ID
+    getConsumable = id: let
+      idStr = toString id;
+    in
+      if consumablesById ? ${idStr}
+      then consumablesById.${idStr}
+      else null;
+
     # get enchant data by effect ID
     getEnchant = effectId: let
       idStr = toString effectId;
@@ -135,7 +108,7 @@
       then reforgeData.${idStr}
       else null;
 
-    # get glyph data by ID
+    # get glyph data by ID (from wowsims da
tabase)
     getGlyph = glyphId: let
       idStr = toString glyphId;
     in
@@ -143,6 +116,40 @@
       then glyphsById.${idStr}
       else null;
 
+    # get glyph data from our custom glyph 
database
+    getGlyphData = className: glyphId: let
+      idStr = toString glyphId;
+      classGlyphs = glyphDb.${className} or
 {};
+    in
+      if classGlyphs ? ${idStr}
+      then classGlyphs.${idStr}
+      else null;
+
+    # get talent data from talent database
+    getTalentData = className: tier: choice
: let
+      tierStr = toString tier;
+      choiceStr = toString choice;
+      classTalents = talentDb.${className} 
or {};
+      tierTalents = classTalents.${tierStr}
 or {};
+    in
+      if tierTalents ? ${choiceStr}
+      then tierTalents.${choiceStr}
+      else null;
+
+    # parse talent string into individual c
hoices
+    parseTalentString = talentString: let
+      # Convert string to list of character
s, then to list of numbers
+      chars = lib.stringToCharacters talent
String;
+      # Convert each character to a number 
(tier choice 1-3)
+      choices = map (char: 
+        let num = lib.toInt char;
+        in if num >= 1 && num <= 3 then num
 else 0
+      ) chars;
+      # Create list of {tier, choice} objec
ts, filtering out invalid choices (0)
+      indexedChoices = lib.imap1 (tier: cho
ice: {inherit tier choice;}) choices;
+      validChoices = lib.filter (choice: ch
oice.choice != 0) indexedChoices;
+    in validChoices;
+
     # parse gem stats into readable format
     parseGemStats = gem:
       if gem ? stats
@@ -172,14 +179,19 @@
           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;
+          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.conc
atStringsSep ", " stats}"
-            else gem.name;
         }
         else null
       else null;
@@ -217,40 +229,61 @@
 
     # enrich a single equipment item
     enrichItem = item:
-      if !(item ? id) then item
+      if !(item ? id)
+      then item
       else let
         itemData = helpers.getItem item.id;
-        enrichedGems = if item ? gems 
+        enrichedGems =
+          if item ? gems
           then lib.filter (gem: gem != null
) (map helpers.enrichGem item.gems)
           else [];
-        enrichedEnchant = if item ? enchant
 
+        enrichedEnchant =
+          if item ? enchant
           then helpers.enrichEnchant item.e
nchant
           else null;
-        enrichedReforge = if item ? reforgi
ng 
+        enrichedReforge =
+          if item ? reforging
           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"
+        itemStats =
+          if itemData != null && itemData ?
 scalingOptions && itemData.scalingOptions ?
 "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) {
+        item
+        // {
+          name =
+            if itemData != null
+            then itemData.name
+            else "Item ${toString item.id}"
;
+          icon =
+            if itemData != null && itemData
 ? icon
+            then itemData.icon
+            else null;
+          quality =
+            if itemData != null && itemData
 ? quality
+            then itemData.quality
+            else null;
+          type =
+            if itemData != null && itemData
 ? type
+            then itemData.type
+            else null;
+        }
+        // lib.optionalAttrs (itemStats != 
null) {
           stats = itemStats;
-        } // lib.optionalAttrs (enrichedGem
s != []) {
+        }
+        // lib.optionalAttrs (enrichedGems 
!= []) {
           gems = enrichedGems;
-        } // lib.optionalAttrs (enrichedEnc
hant != null) {
+        }
+        // lib.optionalAttrs (enrichedEncha
nt != null) {
           enchant = enrichedEnchant;
-        } // lib.optionalAttrs (enrichedRef
orge != null) {
+        }
+        // lib.optionalAttrs (enrichedRefor
ge != null) {
           reforging = enrichedReforge;
         };
   };
-
 in {
   # get item data by ID
   getItem = helpers.getItem;
@@ -311,9 +344,14 @@ in {
   # get glyph name by ID
   getGlyphName = glyphId: let
     glyph = helpers.getGlyph glyphId;
-    item = if glyph != null then helpers.ge
tItem glyph.itemId else null;
+    item =
+      if glyph != null
+      then helpers.getItem glyph.itemId
+      else null;
   in
-    if item != null then item.name else "Un
known Glyph ${toString glyphId}";
+    if item != null
+    then item.name
+    else "Unknown Glyph ${toString glyphId}
";
 
   # enrich equipment item with name and oth
er data
   enrichEquipmentItem = helpers.enrichItem;
@@ -330,116 +368,146 @@ in {
 
   # enrich consumables with item names and 
icons
   enrichConsumables = consumables:
-    if consumables == null then null
+    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}";
+      getConsumableDataSafe = id: let
+        consumable = helpers.getConsumable 
id;
+      in
+        if consumable != null
+        then {
+          name = consumable.name;
+          icon =
+            if consumable ? icon
+            then consumable.icon
+            else null;
+        }
+        else {
+          name = "Consumable ${toString id}
";
           icon = null;
-          quality = null;
         };
     in
-      consumables // lib.optionalAttrs (con
sumables ? flaskId && consumables.flaskId !=
 0) (
-        let flaskData = getItemDataSafe con
sumables.flaskId; in {
+      consumables
+      // lib.optionalAttrs (consumables ? f
laskId && consumables.flaskId != 0) (
+        let
+          flaskData = getConsumableDataSafe
 consumables.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 {
+      )
+      // lib.optionalAttrs (consumables ? f
oodId && consumables.foodId != 0) (
+        let
+          foodData = getConsumableDataSafe 
consumables.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 {
+      )
+      // lib.optionalAttrs (consumables ? p
otId && consumables.potId != 0) (
+        let
+          potData = getConsumableDataSafe c
onsumables.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 {
+      )
+      // lib.optionalAttrs (consumables ? p
repotId && consumables.prepotId != 0) (
+        let
+          prepotData = getConsumableDataSaf
e consumables.prepotId;
+        in {
           prepotName = prepotData.name;
           prepotIcon = prepotData.icon;
-          prepotQuality = prepotData.qualit
y;
         }
       );
 
-  # enrich glyphs with item names and icons
-  enrichGlyphs = glyphs:
+  # enrich glyphs with names and icons from
 glyph database
+  enrichGlyphs = className: 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;
+        glyphData = helpers.getGlyphData cl
assName glyphId;
       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;
+        if glyphData != null then {
+          name = glyphData.name;
+          icon = if glyphData ? icon then g
lyphData.icon else null;
+          spellId = if glyphData ? spellId 
then glyphData.spellId else null;
         } else {
           name = "Glyph ${toString glyphId}
";
           icon = null;
-          quality = null;
           spellId = null;
         };
     in
-      glyphs // lib.optionalAttrs (glyphs ?
 major1 && glyphs.major1 != 0) (
+      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 talents with names and icons fro
m talent database
+  enrichTalents = className: talentString:
+    if talentString == null || talentString
 == ""
+    then null
+    else let
+      # Parse talent string into tier/choic
e pairs
+      talentChoices = helpers.parseTalentSt
ring talentString;
+      
+      # Get talent data for each choice and
 create enriched talent objects
+      enrichedTalents = map (choice: let
+        talentData = helpers.getTalentData 
className choice.tier choice.choice;
+      in {
+        tier = choice.tier;
+        choice = choice.choice;
+        name = if talentData != null then t
alentData.name else "Unknown Talent";
+        spellId = if talentData != null the
n talentData.spellId else null;
+        icon = if talentData != null then t
alentData.icon else null;
+      }) talentChoices;
+      
+    in {
+      talentString = talentString;
+      talents = enrichedTalents;
+    };
+
   # enrich a full loadout
   enrichLoadout = loadout:
     if loadout ? equipment
     then
       loadout
       // {
-        equipment = 
+        equipment =
           if loadout.equipment ? items
           then
             loadout.equipment
@@ -464,15 +532,16 @@ in {
               then
                 result
                 // {
-                  loadout = 
+                  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
;
-                        };
+                        equipment =
+                          result.loadout.eq
uipment
+                          // {
+                            items = map hel
pers.enrichItem result.loadout.equipment.ite
ms;
+                          };
                         # TODO: Add consuma
bles, gems, enchants enrichment
                       }
                     else result.loadout;
@@ -483,4 +552,3 @@ in {
       }
     else simData;
 }
-
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET