summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--afxgems.txt511
-rw-r--r--docs/tlIItools.17
-rw-r--r--gem-afxs.afxgroup1924
-rw-r--r--gem-afxs.txt345
-rwxr-xr-xgenafx-base.sh6
-rwxr-xr-xgenafx-test.sh11
-rw-r--r--src/main/java/tlIItools/Affix.java431
-rw-r--r--src/main/java/tlIItools/AffixGroup.java130
-rw-r--r--src/main/java/tlIItools/AffixLister.java428
-rw-r--r--src/main/java/tlIItools/AffixSet.java47
-rw-r--r--src/main/java/tlIItools/Effect.java453
-rw-r--r--src/main/java/tlIItools/EffectGroup.java120
-rw-r--r--src/main/java/tlIItools/EffectRepo.java85
-rw-r--r--src/main/java/tlIItools/LevelRange.java135
-rw-r--r--src/main/java/tlIItools/Main.java6
-rw-r--r--src/main/java/tlIItools/NameFileReader.java124
-rw-r--r--src/main/java/tlIItools/ReplPair.java173
18 files changed, 3876 insertions, 1061 deletions
diff --git a/.gitignore b/.gitignore
index 2f3f6474..2a4c8b5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
*.class
bin/
output/
+old-output/
tags
synfiles
basefiles
diff --git a/afxgems.txt b/afxgems.txt
new file mode 100644
index 00000000..bea21a77
--- /dev/null
+++ b/afxgems.txt
@@ -0,0 +1,511 @@
+affix-socketables/Affixes/GEMS/gem_amethyst_armor.dat
+affix-socketables/Affixes/GEMS/gem_amethyst.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_armor_health.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT3.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_REGEN2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_REGEN3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_armor_health_regen.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_BLEED2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_BLEED3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_weapon_bleed.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_weapon_lifesteal.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT3.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash.dat
+affix-socketables/Affixes/GEMS/gem_citrine_armor.dat
+affix-socketables/Affixes/GEMS/gem_citrine.dat
+affix-socketables/Affixes/GEMS/gem_diamond_armor.dat
+affix-socketables/Affixes/GEMS/gem_diamond.dat
+affix-socketables/Affixes/GEMS/gem_emerald_armor.dat
+affix-socketables/Affixes/GEMS/gem_emerald.dat
+affix-socketables/Affixes/GEMS/gem_eye_armor.dat
+affix-socketables/Affixes/GEMS/gem_eye.dat
+affix-socketables/Affixes/GEMS/GEM_EYEOFGALLO_LIFESTEAL.DAT
+affix-socketables/Affixes/GEMS/GEM_FISH_BONE2.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_BONE3.dat
+affix-socketables/Affixes/GEMS/gem_fish_bone.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_DEVIL2.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_DEVIL3.dat
+affix-socketables/Affixes/GEMS/gem_fish_devil.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_UNICORN.DAT
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_armor.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR_PERCENT1.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR_PERCENT2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR_PERCENT3.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MELEEDAMAGE2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MELEEDAMAGE3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_meleedamage.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MISSILEDAMAGE2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MISSILEDAMAGE3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_missiledamage.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_THORNS2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_THORNS3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_thorns.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DAMAGEBONUS2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DAMAGEBONUS3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_weapon_damagebonus.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DEGRADEARMOR2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DEGRADEARMOR3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_weapon_degradearmor.dat
+affix-socketables/Affixes/GEMS/GEM_OFLEARNING_MONSTER250_CRIT.dat
+affix-socketables/Affixes/GEMS/GEM_PEARL_ARMOR.DAT
+affix-socketables/Affixes/GEMS/GEM_PEARL.DAT
+affix-socketables/Affixes/GEMS/gem_ruby_armor.dat
+affix-socketables/Affixes/GEMS/gem_ruby.dat
+affix-socketables/Affixes/GEMS/gem_saphire.dat
+affix-socketables/Affixes/GEMS/gem_sapphire_armor.dat
+affix-socketables/Affixes/GEMS/gem_skull_armor.dat
+affix-socketables/Affixes/GEMS/gem_skull.dat
+affix-socketables/Affixes/GEMS/GEM_UNIQUE_PERCENTLIFESTEAL.DAT
+affix-socketables/Affixes/GEMS/GEM_VOIDEMBER_ARMOR_MANA.DAT
+affix-socketables/Affixes/GEMS/GEM_VOIDEMBER_ARMOR_MANA_REGEN.DAT
+affix-socketables/Affixes/GEMS/GEM_VOIDEMBER_WEAPON_MANASTEAL.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_ELECTRIC3.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_ELECTRIC5.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_FIRE3.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_FIRE5.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_ICE3.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_ICE5.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_POISON3.DAT
+affix-socketables/Affixes/GEMS/GEM_WEAPON_DAMAGE_POISON5.DAT
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/gem_spark_weapon_dmgperc1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/gem_spark_weapon_dmgperc1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC6.dat
diff --git a/docs/tlIItools.1 b/docs/tlIItools.1
index c4bc33fa..318cbe98 100644
--- a/docs/tlIItools.1
+++ b/docs/tlIItools.1
@@ -21,6 +21,10 @@ File groups are a concept that was introduced to group together similar items.
Currently, there isn't much mechanical support they offer, but they help with
grouping things together in the output, and will likely be used for more in the
future.
+.SS Affix groups
+Affix groups are used to group together affixes that are mechanically similar.
+For instance, the affixes \fB+2 to strength\fP and \fB+4 to strength\fP would go
+into the same affix group, assuming that they spawned on the same sort of items.
.SH OPTIONS
.TP
\fB--\fP
@@ -100,5 +104,8 @@ the standard error stream.
Specify the regular expression that should guide any attempts to guess file
groups. The regular expression should match the file name, with a single
capture-group that will specify the name of the file group.
+.TP "--output-affix-groups \fIfile-name\fP"
+Specify that affix groups should be printed out to \fIfile-name\fP. By default,
+affix groups aren't output
.SH EXAMPLES
.SH SEE ALSO
diff --git a/gem-afxs.afxgroup b/gem-afxs.afxgroup
new file mode 100644
index 00000000..5c31be1d
--- /dev/null
+++ b/gem-afxs.afxgroup
@@ -0,0 +1,1924 @@
+Group ID 321149533 (6 affixes) Affix Type: SOCKETABLE
+Effects: ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED EMBER
+ GEM_ALL_ARMOR_FLAT1
+ Prefix: Virile [ITEM]
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(13.0-15.0) all Armor
+ GEM_ALL_ARMOR_FLAT2
+ Prefix: Athlete's [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(15.0-17.0) all Armor
+ GEM_ALL_ARMOR_FLAT3
+ Prefix: Fecund [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(17.0-19.0) all Armor
+ GEM_ALL_ARMOR_FLAT4
+ Prefix: Vigorous [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(19.0-21.0) all Armor
+ GEM_ALL_ARMOR_FLAT5
+ Prefix: Rapturous [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(21.0-23.0) all Armor
+ GEM_ALL_ARMOR_FLAT6
+ Prefix: Prime [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(23.0-25.0) all Armor
+
+Group ID 436050567 (3 affixes) Affix Type: SOCKETABLE
+Effects: ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_ARMOR_ARMOR
+ Suffix: [ITEM] of Light Armour
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(18.0-22.0) physical Armor
+ GEM_IRONEMBER_ARMOR_ARMOR2
+ Suffix: [ITEM] of Medium Armour
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(20.0-24.0) physical Armor
+ GEM_IRONEMBER_ARMOR_ARMOR3
+ Suffix: [ITEM] of Heavy Armour
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(22.0-28.0) physical Armor
+
+Group ID -397519612 (7 affixes) Affix Type: SOCKETABLE
+Effects: CRITICAL CHANCE (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE
+ Prefix: Needling [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-4.0)% more Critical Chance
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE2
+ Prefix: Stinging [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.5-4.5)% more Critical Chance
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE3
+ Prefix: Piercing [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-5.0)% more Critical Chance
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE4
+ Prefix: Rupturing [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.5-5.5)% more Critical Chance
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE5
+ Prefix: Penetrating [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-7.0)% more Critical Chance
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE6
+ Prefix: Incisive [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.5-6.5)% more Critical Chance
+ GEM_CHAOSEMBER_WEAPON_CRITCHANCE7
+ Prefix: Critical [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Critical Chance
+
+Group ID -1120944079 (7 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS SECONDARY (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_SPLASH
+ Prefix: Brute's [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (16.0-20.0)% more Damage to Secondary Targets
+ GEM_CHAOSEMBER_WEAPON_SPLASH2
+ Prefix: Wrestler's [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (19.0-23.0)% more Damage to Secondary Targets
+ GEM_CHAOSEMBER_WEAPON_SPLASH3
+ Prefix: Bear's [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (22.0-26.0)% more Damage to Secondary Targets
+ GEM_CHAOSEMBER_WEAPON_SPLASH4
+ Prefix: Lion's [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (25.0-29.0)% more Damage to Secondary Targets
+ GEM_CHAOSEMBER_WEAPON_SPLASH5
+ Prefix: Gorilla's [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (28.0-32.0)% more Damage to Secondary Targets
+ GEM_CHAOSEMBER_WEAPON_SPLASH6
+ Prefix: Goliath's [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (31.0-35.0)% more Damage to Secondary Targets
+ GEM_CHAOSEMBER_WEAPON_SPLASH7
+ Prefix: Leviathan's [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (34.0-38.0)% more Damage to Secondary Targets
+
+Group ID 858028269 (6 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED EMBER
+ GEM_ALL_WEAPON_DMGFLAT1
+ Suffix: [ITEM] of the Catalyst
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(13.0-15.0) all damage
+ GEM_ALL_WEAPON_DMGFLAT2
+ Suffix: [ITEM] of Infusion
+ Level Range: 20-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(15.0-17.0) all damage
+ GEM_ALL_WEAPON_DMGFLAT3
+ Suffix: [ITEM] of Empowerment
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(17.0-19.0) all damage
+ GEM_ALL_WEAPON_DMGFLAT4
+ Suffix: [ITEM] of Restrained Power
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(19.0-21.0) all damage
+ GEM_ALL_WEAPON_DMGFLAT5
+ Suffix: [ITEM] of Unleashed Power
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(21.0-23.0) all damage
+ GEM_ALL_WEAPON_DMGFLAT6
+ Suffix: [ITEM] of Overwhelming Power
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: +(23.0-25.0) all damage
+
+Group ID -686818385 (6 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED SHOCK EMBER
+ GEM_SPARK_WEAPON_DMGFLAT1
+ Suffix: [ITEM] of Humming
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) electric damage
+ GEM_SPARK_WEAPON_DMGFLAT2
+ Suffix: [ITEM] of Buzzing
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) electric damage
+ GEM_SPARK_WEAPON_DMGFLAT3
+ Suffix: [ITEM] of Snapping
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) electric damage
+ GEM_SPARK_WEAPON_DMGFLAT4
+ Suffix: [ITEM] of Crackling
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) electric damage
+ GEM_SPARK_WEAPON_DMGFLAT5
+ Suffix: [ITEM] of Sparking
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) electric damage
+ GEM_SPARK_WEAPON_DMGFLAT6
+ Suffix: [ITEM] of Arcing
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) electric damage
+
+Group ID -1686636738 (6 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED FIRE EMBER
+ GEM_FIRE_WEAPON_DMGFLAT1
+ Suffix: [ITEM] of Heat
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) fire damage
+ GEM_FIRE_WEAPON_DMGFLAT2
+ Suffix: [ITEM] of Smouldering
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) fire damage
+ GEM_FIRE_WEAPON_DMGFLAT3
+ Suffix: [ITEM] of Smoking
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) fire damage
+ GEM_FIRE_WEAPON_DMGFLAT4
+ Suffix: [ITEM] of Burning
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) fire damage
+ GEM_FIRE_WEAPON_DMGFLAT5
+ Suffix: [ITEM] of Flaming
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) fire damage
+ GEM_FIRE_WEAPON_DMGFLAT6
+ Suffix: [ITEM] of Scorching
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) fire damage
+
+Group ID -1113458408 (6 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED ICE EMBER
+ GEM_ICE_WEAPON_DMGFLAT1
+ Suffix: [ITEM] of Frost
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) ice damage
+ GEM_ICE_WEAPON_DMGFLAT2
+ Suffix: [ITEM] of Chills
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) ice damage
+ GEM_ICE_WEAPON_DMGFLAT3
+ Suffix: [ITEM] of Ice
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) ice damage
+ GEM_ICE_WEAPON_DMGFLAT4
+ Suffix: [ITEM] of Frigidity
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) ice damage
+ GEM_ICE_WEAPON_DMGFLAT5
+ Suffix: [ITEM] of Freezing
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) ice damage
+ GEM_ICE_WEAPON_DMGFLAT6
+ Suffix: [ITEM] of the Glacier
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) ice damage
+
+Group ID 486203290 (6 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED POISON EMBER
+ GEM_VENOM_WEAPON_DMGFLAT1
+ Suffix: [ITEM] of Waning
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) poison damage
+ GEM_VENOM_WEAPON_DMGFLAT2
+ Suffix: [ITEM] of Wasting
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) poison damage
+ GEM_VENOM_WEAPON_DMGFLAT3
+ Suffix: [ITEM] of Deterioration
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) poison damage
+ GEM_VENOM_WEAPON_DMGFLAT4
+ Suffix: [ITEM] of Atrophy
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) poison damage
+ GEM_VENOM_WEAPON_DMGFLAT5
+ Suffix: [ITEM] of Disintegration
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) poison damage
+ GEM_VENOM_WEAPON_DMGFLAT6
+ Suffix: [ITEM] of Annihilation
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) poison damage
+
+Group ID -1671038699 (3 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_WEAPON_DAMAGEBONUS
+ Prefix: Quality [ITEM]
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(27.0-30.0) physical damage
+ GEM_IRONEMBER_WEAPON_DAMAGEBONUS2
+ Prefix: Maiming [ITEM]
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(30.0-33.0) physical damage
+ GEM_IRONEMBER_WEAPON_DAMAGEBONUS3
+ Prefix: Slaying [ITEM]
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(33.0-36.0) physical damage
+
+Group ID -200259643 (3 affixes) Affix Type: SOCKETABLE
+Effects: DAMAGE REFLECTION (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_ARMOR_THORNS
+ Suffix: [ITEM] of Thorns
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: (30.0-33.0)% physical damage reflected
+ GEM_IRONEMBER_ARMOR_THORNS2
+ Suffix: [ITEM] of Spikes
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: (33.0-36.0)% physical damage reflected
+ GEM_IRONEMBER_ARMOR_THORNS3
+ Suffix: [ITEM] of Razors
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (36.0-39.0)% physical damage reflected
+
+Group ID -1969107240 (3 affixes) Affix Type: SOCKETABLE
+Effects: Transfer: DAMAGE (timed) (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: BLOOD EMBER
+ GEM_BLOODEMBER_WEAPON_BLEED
+ Prefix: Bloodletting [ITEM]
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: Does (13-17) physical damage over 5.00 seconds
+ GEM_BLOODEMBER_WEAPON_BLEED2
+ Prefix: Haemophilic [ITEM]
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: Does (16-21) physical damage over 5.00 seconds
+ GEM_BLOODEMBER_WEAPON_BLEED3
+ Prefix: Exsanguinating [ITEM]
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: Does (19-25) physical damage over 5.00 seconds
+
+Group ID -1208293348 (3 affixes) Affix Type: SOCKETABLE
+Effects: DEGRADE ARMOR (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_WEAPON_DEGRADEARMOR
+ Prefix: Puncturing [ITEM]
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: Degrade enemy armor by (13-15) on hit
+ GEM_IRONEMBER_WEAPON_DEGRADEARMOR2
+ Prefix: Bashing [ITEM]
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: Degrade enemy armor by (15-17) on hit
+ GEM_IRONEMBER_WEAPON_DEGRADEARMOR3
+ Prefix: Piercing [ITEM]
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: Degrade enemy armor by (17-19) on hit
+
+Group ID -576480627 (7 affixes) Affix Type: SOCKETABLE
+Effects: DODGE CHANCE BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_DODGE
+ Suffix: [ITEM] of the Shade
+ Max Level: 23
+ Spawn Weight: 3 Occupies no slots
+ Effects: (1.0-3.0)% more Dodge Chance
+ GEM_CHAOSEMBER_ARMOR_DODGE2
+ Suffix: [ITEM] of the Ghost
+ Level Range: 14-37
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-5.0)% more Dodge Chance
+ GEM_CHAOSEMBER_ARMOR_DODGE3
+ Suffix: [ITEM] of the Spectre
+ Level Range: 28-51
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-6.0)% more Dodge Chance
+ GEM_CHAOSEMBER_ARMOR_DODGE4
+ Suffix: [ITEM] of the Wraith
+ Level Range: 42-65
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-7.0)% more Dodge Chance
+ GEM_CHAOSEMBER_ARMOR_DODGE5
+ Suffix: [ITEM] of the Phantasm
+ Level Range: 56-79
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-8.0)% more Dodge Chance
+ GEM_CHAOSEMBER_ARMOR_DODGE6
+ Suffix: [ITEM] of the Nightmare
+ Level Range: 70-93
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.5-8.5)% more Dodge Chance
+ GEM_CHAOSEMBER_ARMOR_DODGE7
+ Suffix: [ITEM] of the Mirage
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-9.0)% more Dodge Chance
+
+Group ID -2138239831 (7 affixes) Affix Type: SOCKETABLE
+Effects: DUAL WIELDING BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD
+ Prefix: Lesser Paired [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Damage while Dual Wielding
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD2
+ Prefix: Paired [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-8.0)% more Damage while Dual Wielding
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD3
+ Prefix: Greater Paired [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (7.0-9.0)% more Damage while Dual Wielding
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD4
+ Prefix: Lesser Harmonic [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (8.0-10.0)% more Damage while Dual Wielding
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD5
+ Prefix: Harmonic [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (9.0-11.0)% more Damage while Dual Wielding
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD6
+ Prefix: Greater Harmonic [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (10.0-12.0)% more Damage while Dual Wielding
+ GEM_CHAOSEMBER_WEAPON_DUALWIELD7
+ Prefix: Resonant [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (11.0-13.0)% more Damage while Dual Wielding
+
+Group ID 578647017 (6 affixes) Affix Type: SOCKETABLE
+Effects: ELECTRICAL DEFENSE (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED SHOCK EMBER
+ GEM_SPARK_ARMOR_FLAT1
+ Prefix: Cloud's [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) electrical armor
+ GEM_SPARK_ARMOR_FLAT2
+ Prefix: Squall's [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) electrical armor
+ GEM_SPARK_ARMOR_FLAT3
+ Prefix: Storm's [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) electrical armor
+ GEM_SPARK_ARMOR_FLAT4
+ Prefix: Thunderhead's [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) electrical armor
+ GEM_SPARK_ARMOR_FLAT5
+ Prefix: Tempest's [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) electrical armor
+ GEM_SPARK_ARMOR_FLAT6
+ Prefix: Maelstrom's [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) electrical armor
+
+Group ID -1611359450 (6 affixes) Affix Type: SOCKETABLE
+Effects: FIRE DEFENSE (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED FIRE EMBER
+ GEM_FIRE_ARMOR_FLAT1
+ Prefix: Whelpling's [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) fire armor
+ GEM_FIRE_ARMOR_FLAT2
+ Prefix: Salamander's [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) fire armor
+ GEM_FIRE_ARMOR_FLAT3
+ Prefix: Drake's [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) fire armor
+ GEM_FIRE_ARMOR_FLAT4
+ Prefix: Kiln's [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) fire armor
+ GEM_FIRE_ARMOR_FLAT5
+ Prefix: Furnace's [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) fire armor
+ GEM_FIRE_ARMOR_FLAT6
+ Prefix: Volcano's [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) fire armor
+
+Group ID 1820338137 (3 affixes) Affix Type: SOCKETABLE
+Effects: HP RECHARGE PLAYER (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: BLOOD EMBER
+ GEM_BLOODEMBER_ARMOR_HEALTH_REGEN
+ Suffix: [ITEM] of Regeneration
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.0-2.0) HP/Second
+ GEM_BLOODEMBER_ARMOR_HEALTH_REGEN2
+ Suffix: [ITEM] of Tranquility
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.5-3.0) HP/Second
+ GEM_BLOODEMBER_ARMOR_HEALTH_REGEN3
+ Suffix: [ITEM] of Nature's Mercy
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(2.5-4.5) HP/Second
+
+Group ID -1124052117 (3 affixes) Affix Type: SOCKETABLE
+Effects: HP RECHARGE PLAYER (uses default graph)
+Affix can be socketed into: ARMOR, WEAPON, TRINKET
+Affix can spawn on: SOCKETABLE
+ GEM_FISH_BONE3
+ No Level Range
+ Spawn Weight: 0 Occupies no slots
+ Effects: +(3.0-4.0) HP/Second
+ GEM_FISH_BONE
+ No Level Range
+ Spawn Weight: 0 Occupies no slots
+ Effects: +(1.0-2.0) HP/Second
+ GEM_FISH_BONE2
+ No Level Range
+ Spawn Weight: 0 Occupies no slots
+ Effects: +(2.0-3.0) HP/Second
+
+Group ID -894515083 (6 affixes) Affix Type: SOCKETABLE
+Effects: ICE DEFENSE (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED ICE EMBER
+ GEM_ICE_ARMOR_FLAT1
+ Prefix: Inuit's [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) ice armor
+ GEM_ICE_ARMOR_FLAT2
+ Prefix: Seal's [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) ice armor
+ GEM_ICE_ARMOR_FLAT3
+ Prefix: Penguin's [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) ice armor
+ GEM_ICE_ARMOR_FLAT4
+ Prefix: Yeti's [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) ice armor
+ GEM_ICE_ARMOR_FLAT5
+ Prefix: Walrus's [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) ice armor
+ GEM_ICE_ARMOR_FLAT6
+ Prefix: Polar Bear's [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) ice armor
+
+Group ID 800449916 (7 affixes) Affix Type: SOCKETABLE
+Effects: KNOCK BACK (timed) (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK
+ Prefix: Pushing [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: -12.0 knockback
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK2
+ Prefix: Pulling [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: -14.0 knockback
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK3
+ Prefix: Shoving [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: -16.0 knockback
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK4
+ Prefix: Blowing [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: -18.0 knockback
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK5
+ Prefix: Galeforce [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: -20.0 knockback
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK6
+ Prefix: Tornado's [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: -22.0 knockback
+ GEM_CHAOSEMBER_WEAPON_KNOCKBACK7
+ Prefix: Hurricane's [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: -24.0 knockback
+
+Group ID -1491670784 (3 affixes) Affix Type: SOCKETABLE
+Effects: LIFE STEAL (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: BLOOD EMBER
+ GEM_BLOODEMBER_WEAPON_LIFESTEAL
+ Prefix: Mosquito's [ITEM]
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(230.0-255.0) Life Stolen
+ GEM_BLOODEMBER_WEAPON_LIFESTEAL2
+ Prefix: Hungering [ITEM]
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(250.0-270.0) Life Stolen
+ GEM_BLOODEMBER_WEAPON_LIFESTEAL3
+ Prefix: Vampiric [ITEM]
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(265.0-290.0) Life Stolen
+
+Group ID -552993701 (3 affixes) Affix Type: SOCKETABLE
+Effects: MAX HP (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: BLOOD EMBER
+ GEM_BLOODEMBER_ARMOR_HEALTH
+ Suffix: [ITEM] of the Hale
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(8.0-12.0) Max HP
+ GEM_BLOODEMBER_ARMOR_HEALTH2
+ Suffix: [ITEM] of the Whale
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(10.0-16.0) Max HP
+ GEM_BLOODEMBER_ARMOR_HEALTH3
+ Suffix: [ITEM] of the Healthy
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(14.0-20.0) Max HP
+
+Group ID -1644999067 (3 affixes) Affix Type: SOCKETABLE
+Effects: MELEEDAMAGEBONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_ARMOR_MELEEDAMAGE
+ Suffix: [ITEM] of Worth
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(6.0-10.0) physical Damage with Melee Weapons
+ GEM_IRONEMBER_ARMOR_MELEEDAMAGE2
+ Suffix: [ITEM] of Measure
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(10.0-14.0) physical Damage with Melee Weapons
+ GEM_IRONEMBER_ARMOR_MELEEDAMAGE3
+ Suffix: [ITEM] of Excellence
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(10.0-14.0) physical Damage with Melee Weapons
+
+Group ID -1567231087 (7 affixes) Affix Type: SOCKETABLE
+Effects: MISSILE RANGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE
+ Prefix: Extended [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(0.5-3.0) meters to ranged weapon range
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE2
+ Prefix: Darting [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(0.8-2.8) meters to ranged weapon range
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE3
+ Prefix: Flightful [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.0-3.0) meters to ranged weapon range
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE4
+ Prefix: Propelling [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.2-3.2) meters to ranged weapon range
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE5
+ Prefix: Zephyr's [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.4-3.4) meters to ranged weapon range
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE6
+ Prefix: Gale's [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.6-3.6) meters to ranged weapon range
+ GEM_CHAOSEMBER_WEAPON_MISSILERANGE7
+ Prefix: Horizon's [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(1.8-3.8) meters to ranged weapon range
+
+Group ID -785680140 (7 affixes) Affix Type: SOCKETABLE
+Effects: MISSILE REFLECT (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT
+ Suffix: [ITEM] of Deflection
+ Max Level: 23
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.5-6.0)% more Missile Reflect Chance
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT2
+ Suffix: [ITEM] of Evasion
+ Level Range: 14-37
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-7.0)% more Missile Reflect Chance
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT3
+ Suffix: [ITEM] of Protection
+ Level Range: 28-51
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-8.0)% more Missile Reflect Chance
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT4
+ Suffix: [ITEM] of Cover
+ Level Range: 42-65
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.5-8.5)% more Missile Reflect Chance
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT5
+ Suffix: [ITEM] of Asylum
+ Level Range: 56-79
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-9.0)% more Missile Reflect Chance
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT6
+ Suffix: [ITEM] of Refuge
+ Level Range: 70-93
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.5-9.5)% more Missile Reflect Chance
+ GEM_CHAOSEMBER_ARMOR_MISSILEREFLECT7
+ Suffix: [ITEM] of Sanctuary
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-10.0)% more Missile Reflect Chance
+
+Group ID 1312337698 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED EMBER
+ GEM_ALL_ARMOR_PERC1
+ Prefix: Shimmering [ITEM]
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: (1.0-3.0)% more all Armor
+ GEM_ALL_ARMOR_PERC2
+ Prefix: Rainbow [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (3.0-6.0)% more all Armor
+ GEM_ALL_ARMOR_PERC3
+ Prefix: Shimmering [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (6.0-9.0)% more all Armor
+ GEM_ALL_ARMOR_PERC4
+ Prefix: Scintillating [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: (9.0-12.0)% more all Armor
+ GEM_ALL_ARMOR_PERC5
+ Prefix: Prismatic [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (12.0-15.0)% more all Armor
+ GEM_ALL_ARMOR_PERC6
+ Prefix: Chromatic [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (15.0-18.0)% more all Armor
+
+Group ID -232508956 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED SHOCK EMBER
+ GEM_SPARK_ARMOR_PERC1
+ Prefix: Tangerine [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more electric Armor
+ GEM_SPARK_ARMOR_PERC2
+ Prefix: Ocher [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more electric Armor
+ GEM_SPARK_ARMOR_PERC3
+ Prefix: Coral [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more electric Armor
+ GEM_SPARK_ARMOR_PERC4
+ Prefix: Amber [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more electric Armor
+ GEM_SPARK_ARMOR_PERC5
+ Prefix: Peach [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more electric Armor
+ GEM_SPARK_ARMOR_PERC6
+ Prefix: Bittersweet [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more electric Armor
+
+Group ID -1232327309 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED FIRE EMBER
+ GEM_FIRE_ARMOR_PERC1
+ Prefix: Crimson [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more fire Armor
+ GEM_FIRE_ARMOR_PERC2
+ Prefix: Russet [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more fire Armor
+ GEM_FIRE_ARMOR_PERC3
+ Prefix: Garnet [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more fire Armor
+ GEM_FIRE_ARMOR_PERC4
+ Prefix: Ruby [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more fire Armor
+ GEM_FIRE_ARMOR_PERC6
+ Prefix: Cerise [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more fire Armor
+ GEM_FIRE_ARMOR_PERC5
+ Prefix: Scarlet [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more fire Armor
+
+Group ID -659148979 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED ICE EMBER
+ GEM_ICE_ARMOR_PERC1
+ Prefix: Azure [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more ice Armor
+ GEM_ICE_ARMOR_PERC4
+ Prefix: Sapphire [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more ice Armor
+ GEM_ICE_ARMOR_PERC6
+ Prefix: Royal Blue [ITEM]
+ Minimum Level: 10
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more ice Armor
+ GEM_ICE_ARMOR_PERC2
+ Prefix: Lapis [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more ice Armor
+ GEM_ICE_ARMOR_PERC3
+ Prefix: Cobalt [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more ice Armor
+ GEM_ICE_ARMOR_PERC5
+ Prefix: Aquamarine [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more ice Armor
+
+Group ID 940512719 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED POISON EMBER
+ GEM_VENOM_ARMOR_PERC1
+ Prefix: Beryl [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more poison Armor
+ GEM_VENOM_ARMOR_PERC2
+ Prefix: Viridian [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more poison Armor
+ GEM_VENOM_ARMOR_PERC3
+ Prefix: Viridian [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more poison Armor
+ GEM_VENOM_ARMOR_PERC4
+ Prefix: Emerald [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more poison Armor
+ GEM_VENOM_ARMOR_PERC5
+ Prefix: Olive [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more poison Armor
+ GEM_VENOM_ARMOR_PERC6
+ Prefix: Jade [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more poison Armor
+
+Group ID -1842290252 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR
+ Suffix: [ITEM] of Thick Skin
+ Max Level: 23
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-4.0)% more physical Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR2
+ Suffix: [ITEM] of Stone Skin
+ Level Range: 14-37
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-5.0)% more physical Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR3
+ Suffix: [ITEM] of Iron Skin
+ Level Range: 28-51
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.5-5.5)% more physical Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR4
+ Suffix: [ITEM] of Steel Skin
+ Level Range: 42-65
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-6.0)% more physical Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR5
+ Suffix: [ITEM] of Adamantine Skin
+ Level Range: 56-79
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-5.0)% more physical Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR6
+ Suffix: [ITEM] of Corundum Skin
+ Level Range: 70-93
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more physical Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTARMOR7
+ Suffix: [ITEM] of Unbreakable Skin
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.5-7.5)% more physical Armor
+
+Group ID 1427238732 (3 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ARMOR BONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_ARMOR_ARMOR_PERCENT1
+ Suffix: [ITEM] of the Beetle
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-6.0)% more physical Armor
+ GEM_IRONEMBER_ARMOR_ARMOR_PERCENT2
+ Suffix: [ITEM] of the Armadillo
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-8.0)% more physical Armor
+ GEM_IRONEMBER_ARMOR_ARMOR_PERCENT3
+ Suffix: [ITEM] of the Mammoth
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-10.0)% more physical Armor
+
+Group ID -1060252045 (3 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ATTACK SPEED (uses default graph)
+Affix can be socketed into: ARMOR, WEAPON, TRINKET
+Affix can spawn on: SOCKETABLE
+ GEM_FISH_DEVIL
+ No Level Range
+ Spawn Weight: 0 Occupies no slots
+ Effects: (2.0-6.0)% more Attack Speed
+ GEM_FISH_DEVIL3
+ No Level Range
+ Spawn Weight: 0 Occupies no slots
+ Effects: (6.0-10.0)% more Attack Speed
+ GEM_FISH_DEVIL2
+ No Level Range
+ Spawn Weight: 0 Occupies no slots
+ Effects: (4.0-8.0)% more Attack Speed
+
+Group ID -201705767 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT ATTACK SPEED (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED2
+ Prefix: Eased [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.5-4.5)% more Attack Speed
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED3
+ Prefix: Master's [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-5.0)% more Attack Speed
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED4
+ Prefix: Renowned [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.5-5.5)% more Attack Speed
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED5
+ Prefix: Acclaimed [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-6.0)% more Attack Speed
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED6
+ Prefix: Famed [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.5-6.5)% more Attack Speed
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED7
+ Prefix: Infamous [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Attack Speed
+ GEM_CHAOSEMBER_WEAPON_ATTACKSPEED
+ Prefix: Skilled [ITEM]
+ No Level Range
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-4.0)% more Attack Speed
+
+Group ID -1042266096 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT CAST SPEED (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED
+ Prefix: Talented [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-4.0)% more Cast Speed
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED2
+ Prefix: Nimble [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.5-4.5)% more Cast Speed
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED3
+ Prefix: Expert's [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-5.0)% more Cast Speed
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED4
+ Prefix: Legerdemainer's [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.5-5.5)% more Cast Speed
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED5
+ Prefix: Prestidigitator's [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-6.0)% more Cast Speed
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED6
+ Prefix: Sortilege's [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.5-6.5)% more Cast Speed
+ GEM_CHAOSEMBER_WEAPON_CASTSPEED7
+ Prefix: Finessed [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Cast Speed
+
+Group ID -1528963260 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT CRITICAL DAMAGE (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE
+ Prefix: Ireful [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (13.0-17.0)% more Critical Damage
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE2
+ Prefix: Angry [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (16.0-20.0)% more Critical Damage
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE3
+ Prefix: Raging [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (19.0-23.0)% more Critical Damage
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE4
+ Prefix: Furious [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (25.0-29.0)% more Critical Damage
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE5
+ Prefix: Ferocious [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (30.0-30.0)% more Critical Damage
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE6
+ Prefix: Destructive [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (31.0-35.0)% more Critical Damage
+ GEM_CHAOSEMBER_WEAPON_CRITDAMAGE7
+ Prefix: Hulking [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (34.0-38.0)% more Critical Damage
+
+Group ID 1520090312 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED EMBER
+ GEM_ALL_WEAPON_DMGPERC1
+ Suffix: [ITEM] of Worth
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: (1.0-3.0)% more all damage
+ GEM_ALL_WEAPON_DMGPERC2
+ Suffix: [ITEM] of Measure
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (3.0-6.0)% more all damage
+ GEM_ALL_WEAPON_DMGPERC3
+ Suffix: [ITEM] of Excellence
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (6.0-9.0)% more all damage
+ GEM_ALL_WEAPON_DMGPERC4
+ Suffix: [ITEM] of Performance
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: (9.0-12.0)% more all damage
+ GEM_ALL_WEAPON_DMGPERC5
+ Suffix: [ITEM] of Transcendence
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (12.0-15.0)% more all damage
+ GEM_ALL_WEAPON_DMGPERC6
+ Suffix: [ITEM] of Peerlessness
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (15.0-18.0)% more all damage
+
+Group ID -24756342 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED SHOCK EMBER
+ GEM_SPARK_WEAPON_DMGPERC1
+ Suffix: [ITEM] of Sparks
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more electric damage
+ GEM_SPARK_WEAPON_DMGPERC2
+ Suffix: [ITEM] of Static
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more electric damage
+ GEM_SPARK_WEAPON_DMGPERC3
+ Suffix: [ITEM] of Electricity
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more electric damage
+ GEM_SPARK_WEAPON_DMGPERC4
+ Suffix: [ITEM] of Voltage
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more electric damage
+ GEM_SPARK_WEAPON_DMGPERC5
+ Suffix: [ITEM] of Discharge
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more electric damage
+ GEM_SPARK_WEAPON_DMGPERC6
+ Suffix: [ITEM] of Arcing
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more electric damage
+
+Group ID -1024574695 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED FIRE EMBER
+ GEM_FIRE_WEAPON_DMGPERC1
+ Suffix: [ITEM] of Embers
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more fire damage
+ GEM_FIRE_WEAPON_DMGPERC2
+ Suffix: [ITEM] of Coals
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more fire damage
+ GEM_FIRE_WEAPON_DMGPERC3
+ Suffix: [ITEM] of Cinders
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more fire damage
+ GEM_FIRE_WEAPON_DMGPERC4
+ Suffix: [ITEM] of Flames
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more fire damage
+ GEM_FIRE_WEAPON_DMGPERC5
+ Suffix: [ITEM] of Immolation
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more fire damage
+ GEM_FIRE_WEAPON_DMGPERC6
+ Suffix: [ITEM] of Ashes
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more fire damage
+
+Group ID -451396365 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED ICE EMBER
+ GEM_ICE_WEAPON_DMGPERC1
+ Suffix: [ITEM] of Snow
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more ice damage
+ GEM_ICE_WEAPON_DMGPERC2
+ Suffix: [ITEM] of Sleet
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more ice damage
+ GEM_ICE_WEAPON_DMGPERC3
+ Suffix: [ITEM] of Ice
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more ice damage
+ GEM_ICE_WEAPON_DMGPERC4
+ Suffix: [ITEM] of Rime
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more ice damage
+ GEM_ICE_WEAPON_DMGPERC5
+ Suffix: [ITEM] of Floes
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more ice damage
+ GEM_ICE_WEAPON_DMGPERC6
+ Suffix: [ITEM] of Glaciation
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more ice damage
+
+Group ID 1148265333 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE BONUS (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: INFUSED POISON EMBER
+ GEM_VENOM_WEAPON_DMGPERC1
+ Suffix: [ITEM] of Blight
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: (3.0-6.0)% more poison damage
+ GEM_VENOM_WEAPON_DMGPERC2
+ Suffix: [ITEM] of Venom
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (6.0-9.0)% more poison damage
+ GEM_VENOM_WEAPON_DMGPERC3
+ Suffix: [ITEM] of Pestilence
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (9.0-12.0)% more poison damage
+ GEM_VENOM_WEAPON_DMGPERC4
+ Suffix: [ITEM] of Anthrax
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: (12.0-15.0)% more poison damage
+ GEM_VENOM_WEAPON_DMGPERC5
+ Suffix: [ITEM] of Toxin
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: (15.0-18.0)% more poison damage
+ GEM_VENOM_WEAPON_DMGPERC6
+ Suffix: [ITEM] of Poison
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: (18.0-21.0)% more poison damage
+
+Group ID -2141988048 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE TAKEN (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED EMBER
+ GEM_ALL_ARMOR_RED1
+ Prefix: Warding [ITEM]
+ Max Level: 60
+ Spawn Weight: 3 Occupies no slots
+ Effects: (1.0-3.0)% less all Damage Taken
+ GEM_ALL_ARMOR_RED2
+ Prefix: Sentinel's [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-6.0)% less all Damage Taken
+ GEM_ALL_ARMOR_RED3
+ Prefix: Guarding [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-9.0)% less all Damage Taken
+ GEM_ALL_ARMOR_RED4
+ Prefix: Negating [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 3 Occupies no slots
+ Effects: (9.0-12.0)% less all Damage Taken
+ GEM_ALL_ARMOR_RED5
+ Prefix: Denying [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 3 Occupies no slots
+ Effects: (12.0-15.0)% less all Damage Taken
+ GEM_ALL_ARMOR_RED6
+ Prefix: Forbidding [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 3 Occupies no slots
+ Effects: (15.0-18.0)% less all Damage Taken
+
+Group ID 608132594 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE TAKEN (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED SHOCK EMBER
+ GEM_SPARK_ARMOR_RED1
+ Prefix: Grounded [ITEM]
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: (3.0-6.0)% less electric Damage Taken
+ GEM_SPARK_ARMOR_RED2
+ Prefix: Earthing [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (6.0-9.0)% less electric Damage Taken
+ GEM_SPARK_ARMOR_RED3
+ Prefix: Non-conductive [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (9.0-12.0)% less electric Damage Taken
+ GEM_SPARK_ARMOR_RED4
+ Prefix: Draining [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: (12.0-15.0)% less electric Damage Taken
+ GEM_SPARK_ARMOR_RED5
+ Prefix: Lightningrod [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (15.0-17.0)% less electric Damage Taken
+ GEM_SPARK_ARMOR_RED6
+ Prefix: Nullspark [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (17.0-19.0)% less electric Damage Taken
+
+Group ID -391685759 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE TAKEN (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED FIRE EMBER
+ GEM_FIRE_ARMOR_RED1
+ Prefix: Cooling [ITEM]
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: (3.0-6.0)% less fire Damage Taken
+ GEM_FIRE_ARMOR_RED2
+ Prefix: Numbing [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (6.0-9.0)% less fire Damage Taken
+ GEM_FIRE_ARMOR_RED3
+ Prefix: Flame-Dampening [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (9.0-12.0)% less fire Damage Taken
+ GEM_FIRE_ARMOR_RED4
+ Prefix: Extinguishing [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: (12.0-15.0)% less fire Damage Taken
+ GEM_FIRE_ARMOR_RED5
+ Prefix: Flame-voiding [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (15.0-17.0)% less fire Damage Taken
+ GEM_FIRE_ARMOR_RED6
+ Prefix: Nullfire [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (17.0-19.0)% less fire Damage Taken
+
+Group ID 181492571 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE TAKEN (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED ICE EMBER
+ GEM_ICE_ARMOR_RED1
+ Prefix: Warming [ITEM]
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: (3.0-6.0)% less ice Damage Taken
+ GEM_ICE_ARMOR_RED2
+ Prefix: Insulated [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (6.0-9.0)% less ice Damage Taken
+ GEM_ICE_ARMOR_RED3
+ Prefix: Defrosting [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (9.0-12.0)% less ice Damage Taken
+ GEM_ICE_ARMOR_RED4
+ Prefix: Melting [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: (12.0-15.0)% less ice Damage Taken
+ GEM_ICE_ARMOR_RED5
+ Prefix: Salted [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (15.0-17.0)% less ice Damage Taken
+ GEM_ICE_ARMOR_RED6
+ Prefix: Nullfrost [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (17.0-19.0)% less ice Damage Taken
+
+Group ID 1781154269 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE TAKEN (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED POISON EMBER
+ GEM_VENOM_ARMOR_RED1
+ Prefix: Curing [ITEM]
+ Max Level: 60
+ Spawn Weight: 2 Occupies no slots
+ Effects: (3.0-6.0)% less poison Damage Taken
+ GEM_VENOM_ARMOR_RED2
+ Prefix: Neutralizing [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (6.0-9.0)% less poison Damage Taken
+ GEM_VENOM_ARMOR_RED3
+ Prefix: Anti-venom [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (9.0-12.0)% less poison Damage Taken
+ GEM_VENOM_ARMOR_RED4
+ Prefix: Restorative [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 2 Occupies no slots
+ Effects: (12.0-15.0)% less poison Damage Taken
+ GEM_VENOM_ARMOR_RED5
+ Prefix: Immune [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 2 Occupies no slots
+ Effects: (15.0-17.0)% less poison Damage Taken
+ GEM_VENOM_ARMOR_RED6
+ Prefix: Nullvenom [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 2 Occupies no slots
+ Effects: (17.0-19.0)% less poison Damage Taken
+
+Group ID -1001648702 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DAMAGE TAKEN (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE
+ Suffix: [ITEM] of Least Resistance
+ Max Level: 23
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-4.0)% less physical Damage Taken
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE2
+ Suffix: [ITEM] of Lesser Resistance
+ Level Range: 14-37
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.5-4.5)% less physical Damage Taken
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE3
+ Suffix: [ITEM] of Resistance
+ Level Range: 28-51
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-5.0)% less physical Damage Taken
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE4
+ Suffix: [ITEM] of Improved Resistance
+ Level Range: 42-65
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.5-5.5)% less physical Damage Taken
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE5
+ Suffix: [ITEM] of Greater Resistance
+ Level Range: 56-79
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-6.0)% less physical Damage Taken
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE6
+ Suffix: [ITEM] of Major Resistance
+ Level Range: 70-93
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.5-6.5)% less physical Damage Taken
+ GEM_CHAOSEMBER_ARMOR_PERCENTDAMAGE7
+ Suffix: [ITEM] of Mythical Resistance
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% less physical Damage Taken
+
+Group ID 1251862647 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT DUAL WIELDING ATTACK (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_EXECTUE
+ Prefix: Lesser Merciful [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (8.0-10.0)% more Execute Chance
+ GEM_CHAOSEMBER_WEAPON_EXECTUE2
+ Prefix: Merciful [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (9.5-11.5)% more Execute Chance
+ GEM_CHAOSEMBER_WEAPON_EXECTUE3
+ Prefix: Greater Merciful [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (11.0-13.0)% more Execute Chance
+ GEM_CHAOSEMBER_WEAPON_EXECTUE4
+ Prefix: Lesser Executioner's [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (12.5-14.5)% more Execute Chance
+ GEM_CHAOSEMBER_WEAPON_EXECTUE5
+ Prefix: Executioner's [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (14.0-16.0)% more Execute Chance
+ GEM_CHAOSEMBER_WEAPON_EXECTUE6
+ Prefix: Greater Executioner's [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (15.5-17.5)% more Execute Chance
+ GEM_CHAOSEMBER_WEAPON_EXECTUE7
+ Prefix: Sanson's [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (18.0-18.0)% more Execute Chance
+
+Group ID -620600678 (3 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT HP (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: BLOOD EMBER
+ GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT
+ Suffix: [ITEM] of Fortitude
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: (1.0-4.0)% more HP
+ GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT2
+ Suffix: [ITEM] of Tenacity
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-6.0)% more HP
+ GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT3
+ Suffix: [ITEM] of Endurance
+ Minimum Level: 55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-8.0)% more HP
+
+Group ID -346389156 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT KNOCK BACK RESISTANCE (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST
+ Suffix: [ITEM] of the Rock
+ Max Level: 23
+ Spawn Weight: 3 Occupies no slots
+ Effects: -12.0% knockback resistance
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST2
+ Suffix: [ITEM] of the Boulder
+ Level Range: 14-37
+ Spawn Weight: 3 Occupies no slots
+ Effects: -14.0% knockback resistance
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST3
+ Suffix: [ITEM] of the Hill
+ Level Range: 28-51
+ Spawn Weight: 3 Occupies no slots
+ Effects: -16.0% knockback resistance
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST4
+ Suffix: [ITEM] of the Mountain
+ Level Range: 42-65
+ Spawn Weight: 3 Occupies no slots
+ Effects: -18.0% knockback resistance
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST5
+ Suffix: [ITEM] of the Lesser Bulwark
+ Level Range: 56-79
+ Spawn Weight: 3 Occupies no slots
+ Effects: -20.0% knockback resistance
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST6
+ Suffix: [ITEM] of the Bulwark
+ Level Range: 70-93
+ Spawn Weight: 3 Occupies no slots
+ Effects: -22.0% knockback resistance
+ GEM_CHAOSEMBER_ARMOR_PERCENTKNOCKBACKRESIST7
+ Suffix: [ITEM] of the Juggernaut
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: -24.0% knockback resistance
+
+Group ID 915223941 (3 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT LIFE STOLEN (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: BLOOD EMBER
+ GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT
+ Prefix: Locust's [ITEM]
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: (1.0-4.0)% more HP stolen
+ GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT2
+ Prefix: Lamprey's [ITEM]
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.0-7.0)% more HP stolen
+ GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT3
+ Prefix: Leech's [ITEM]
+ Minimum Level: 66
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-9.0)% more HP stolen
+
+Group ID -638372876 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT PET ARMOR (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR
+ Suffix: [ITEM] of the Lesser Guardian
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Minion/Pet Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR2
+ Suffix: [ITEM] of the Guardian
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-8.0)% more Minion/Pet Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR3
+ Suffix: [ITEM] of the Greater Guardian
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (7.0-9.0)% more Minion/Pet Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR4
+ Suffix: [ITEM] of the Lesser Master
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (8.0-10.0)% more Minion/Pet Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR5
+ Suffix: [ITEM] of the Master
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (9.0-11.0)% more Minion/Pet Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR6
+ Suffix: [ITEM] of the Greater Master
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (10.0-12.0)% more Minion/Pet Armor
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETARMOR7
+ Suffix: [ITEM] of the Shephard
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (11.0-13.0)% more Minion/Pet Armor
+
+Group ID 1798808060 (7 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT PET DAMAGE (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE
+ Suffix: [ITEM] of the Lesser Ranger
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Minion/Pet Damage
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE2
+ Suffix: [ITEM] of the Ranger
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-8.0)% more Minion/Pet Damage
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE3
+ Suffix: [ITEM] of the Greater Ranger
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (7.0-9.0)% more Minion/Pet Damage
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE4
+ Suffix: [ITEM] of the Lesser Hunter
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (8.0-10.0)% more Minion/Pet Damage
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE5
+ Suffix: [ITEM] of the Hunter
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (9.0-11.0)% more Minion/Pet Damage
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE6
+ Suffix: [ITEM] of the Greater Hunter
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (10.0-12.0)% more Minion/Pet Damage
+ GEM_CHAOSEMBER_ARMOR_PERCENTPETDAMAGE7
+ Suffix: [ITEM] of the Commander
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (11.0-13.0)% more Minion/Pet Damage
+
+Group ID -248062691 (6 affixes) Affix Type: SOCKETABLE
+Effects: PERCENT SPEED (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_SPEED
+ Suffix: [ITEM] of the Runner
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.0-4.0)% more Movement Speed
+ GEM_CHAOSEMBER_ARMOR_SPEED2
+ Suffix: [ITEM] of the Sprinter
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (2.5-4.5)% more Movement Speed
+ GEM_CHAOSEMBER_ARMOR_SPEED4
+ Suffix: [ITEM] of the Thoroughbred
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (3.5-5.5)% more Movement Speed
+ GEM_CHAOSEMBER_ARMOR_SPEED5
+ Suffix: [ITEM] of the Gazelle
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-6.0)% more Movement Speed
+ GEM_CHAOSEMBER_ARMOR_SPEED6
+ Suffix: [ITEM] of the Cheetah
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.5-6.5)% more Movement Speed
+ GEM_CHAOSEMBER_ARMOR_SPEED7
+ Suffix: [ITEM] of the Hellion
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-7.0)% more Movement Speed
+
+Group ID -201842476 (6 affixes) Affix Type: SOCKETABLE
+Effects: POISON DEFENSE (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: INFUSED POISON EMBER
+ GEM_VENOM_ARMOR_FLAT1
+ Prefix: Lost [ITEM]
+ Max Level: 60
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(13.0-15.0) poison armor
+ GEM_VENOM_ARMOR_FLAT2
+ Prefix: Banished [ITEM]
+ Level Range: 20-80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(15.0-17.0) poison armor
+ GEM_VENOM_ARMOR_FLAT3
+ Prefix: Evicted [ITEM]
+ Level Range: 40-100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(17.0-19.0) poison armor
+ GEM_VENOM_ARMOR_FLAT4
+ Prefix: Expelled [ITEM]
+ Level Range: 60-120
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(19.0-21.0) poison armor
+ GEM_VENOM_ARMOR_FLAT5
+ Prefix: Expulsive [ITEM]
+ Minimum Level: 80
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(21.0-23.0) poison armor
+ GEM_VENOM_ARMOR_FLAT6
+ Prefix: Exiled [ITEM]
+ Minimum Level: 100
+ Spawn Weight: 1 Occupies no slots
+ Effects: +(23.0-25.0) poison armor
+
+Group ID 1992686663 (7 affixes) Affix Type: SOCKETABLE
+Effects: POTION EFFICIENCY (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY
+ Suffix: [ITEM] of Refills
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: (4.0-8.0)% more Effectiveness of Potions
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY2
+ Suffix: [ITEM] of Overflow
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: (5.0-9.0)% more Effectiveness of Potions
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY3
+ Suffix: [ITEM] of Sipping
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: (6.0-10.0)% more Effectiveness of Potions
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY4
+ Suffix: [ITEM] of Savouring
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: (7.0-11.0)% more Effectiveness of Potions
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY5
+ Suffix: [ITEM] of Recovery
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: (8.0-12.0)% more Effectiveness of Potions
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY6
+ Suffix: [ITEM] of Chemistry
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: (9.0-13.0)% more Effectiveness of Potions
+ GEM_CHAOSEMBER_ARMOR_POTIONEFFICIENCY7
+ Suffix: [ITEM] of Alchemy
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: (10.0-14.0)% more Effectiveness of Potions
+
+Group ID 976696658 (3 affixes) Affix Type: SOCKETABLE
+Effects: RANGEDDAMAGEBONUS (uses default graph)
+Affix can be socketed into: ARMOR, TRINKET
+Affix can spawn on: IRON EMBER
+ GEM_IRONEMBER_ARMOR_MISSILEDAMAGE
+ Suffix: [ITEM] of the Fletcher
+ Max Level: 33
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(6.0-10.0) physical damage with ranged weapons
+ GEM_IRONEMBER_ARMOR_MISSILEDAMAGE3
+ Suffix: [ITEM] of the Archer
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(10.0-14.0) physical damage with ranged weapons
+ GEM_IRONEMBER_ARMOR_MISSILEDAMAGE2
+ Suffix: [ITEM] of the Bowyer
+ Level Range: 25-66
+ Spawn Weight: 3 Occupies no slots
+ Effects: +(8.0-12.0) physical damage with ranged weapons
+
+Group ID 526288420 (7 affixes) Affix Type: SOCKETABLE
+Effects: Transfer: SILENCE (timed) (uses default graph)
+Affix can be socketed into: WEAPON
+Affix can spawn on: CHAOS EMBER
+ GEM_CHAOSEMBER_WEAPON_SILENCE
+ Prefix: Quiet [ITEM]
+ Max Level: 13
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (45.0-55.0)% chance to Silence for 2.0 seconds.
+ GEM_CHAOSEMBER_WEAPON_SILENCE2
+ Prefix: Muffled [ITEM]
+ Level Range: 14-27
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (50.0-60.0)% chance to Silence for 2.4 seconds.
+ GEM_CHAOSEMBER_WEAPON_SILENCE3
+ Prefix: Muted [ITEM]
+ Level Range: 28-41
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (55.0-65.0)% chance to Silence for 2.8 seconds.
+ GEM_CHAOSEMBER_WEAPON_SILENCE4
+ Prefix: Shushing [ITEM]
+ Level Range: 42-55
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (60.0-70.0)% chance to Silence for 3.2 seconds.
+ GEM_CHAOSEMBER_WEAPON_SILENCE5
+ Prefix: Librarian's [ITEM]
+ Level Range: 56-69
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (65.0-75.0)% chance to Silence for 3.6 seconds.
+ GEM_CHAOSEMBER_WEAPON_SILENCE6
+ Prefix: Silencing [ITEM]
+ Level Range: 70-83
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (70.0-80.0)% chance to Silence for 3.8 seconds.
+ GEM_CHAOSEMBER_WEAPON_SILENCE7
+ Prefix: Vacuum [ITEM]
+ Minimum Level: 84
+ Spawn Weight: 3 Occupies no slots
+ Effects: Inflict on Hit: (75.0-85.0)% chance to Silence for 4.0 seconds.
+
diff --git a/gem-afxs.txt b/gem-afxs.txt
new file mode 100644
index 00000000..f8c9b1d2
--- /dev/null
+++ b/gem-afxs.txt
@@ -0,0 +1,345 @@
+affix-socketables/Affixes/GEMS/gem_amethyst_armor.dat
+affix-socketables/Affixes/GEMS/gem_amethyst.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_armor_health.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT3.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_PERCENT.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_REGEN2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_ARMOR_HEALTH_REGEN3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_armor_health_regen.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_BLEED2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_BLEED3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_weapon_bleed.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL3.dat
+affix-socketables/Affixes/GEMS/gem_bloodember_weapon_lifesteal.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT2.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT3.dat
+affix-socketables/Affixes/GEMS/GEM_BLOODEMBER_WEAPON_LIFESTEAL_PERCENT.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_dodge.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_missilereflect.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentarmor.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentdamage.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentknockbackresist.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetarmor.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_percentpetdamage.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_potionefficiency.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_armor_speed.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_attackspeed.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_castspeed.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critchance.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_critdamage.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_dualwield.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_exectue.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_knockback.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_missilerange.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_silence.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash2.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash3.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash4.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash5.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash6.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash7.dat
+affix-socketables/Affixes/GEMS/gem_chaosember_weapon_splash.dat
+affix-socketables/Affixes/GEMS/gem_citrine_armor.dat
+affix-socketables/Affixes/GEMS/gem_citrine.dat
+affix-socketables/Affixes/GEMS/gem_diamond_armor.dat
+affix-socketables/Affixes/GEMS/gem_diamond.dat
+affix-socketables/Affixes/GEMS/gem_emerald_armor.dat
+affix-socketables/Affixes/GEMS/gem_emerald.dat
+affix-socketables/Affixes/GEMS/gem_eye_armor.dat
+affix-socketables/Affixes/GEMS/gem_eye.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_BONE2.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_BONE3.dat
+affix-socketables/Affixes/GEMS/gem_fish_bone.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_DEVIL2.dat
+affix-socketables/Affixes/GEMS/GEM_FISH_DEVIL3.dat
+affix-socketables/Affixes/GEMS/gem_fish_devil.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_armor.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR_PERCENT1.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR_PERCENT2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_ARMOR_PERCENT3.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MELEEDAMAGE2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MELEEDAMAGE3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_meleedamage.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MISSILEDAMAGE2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_MISSILEDAMAGE3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_missiledamage.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_THORNS2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_ARMOR_THORNS3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_armor_thorns.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DAMAGEBONUS2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DAMAGEBONUS3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_weapon_damagebonus.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DEGRADEARMOR2.dat
+affix-socketables/Affixes/GEMS/GEM_IRONEMBER_WEAPON_DEGRADEARMOR3.dat
+affix-socketables/Affixes/GEMS/gem_ironember_weapon_degradearmor.dat
+affix-socketables/Affixes/GEMS/GEM_OFLEARNING_MONSTER250_CRIT.dat
+affix-socketables/Affixes/GEMS/gem_ruby_armor.dat
+affix-socketables/Affixes/GEMS/gem_ruby.dat
+affix-socketables/Affixes/GEMS/gem_saphire.dat
+affix-socketables/Affixes/GEMS/gem_sapphire_armor.dat
+affix-socketables/Affixes/GEMS/gem_skull_armor.dat
+affix-socketables/Affixes/GEMS/gem_skull.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ALL_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_FIRE_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_ICE_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/gem_spark_weapon_dmgperc1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_SPARK_WEAPON_DMGPERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_FLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_PERC6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_ARMOR_RED6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGFLAT6.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC1.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC2.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC3.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC4.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC5.dat
+affix-socketables/Affixes/INFUSED_GEMS/GEM_VENOM_WEAPON_DMGPERC6.dat
diff --git a/genafx-base.sh b/genafx-base.sh
new file mode 100755
index 00000000..bf68a28b
--- /dev/null
+++ b/genafx-base.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Runs AffixLister with the provided arguments
+set -e
+
+mvn compile exec:java -Dexec.args="$@"
diff --git a/genafx-test.sh b/genafx-test.sh
index 4f9726f9..b84e0fd7 100755
--- a/genafx-test.sh
+++ b/genafx-test.sh
@@ -1,16 +1,21 @@
#!/bin/bash
+# Runs the AffixLister, formats the resulting text to PDF format, and then
+# displays it
+# The first argument is the base output name; the rest of the arguments are the
+# files to input
set -e
baseout="$1"
shift 1
fileset="$@"
-listopts="-z -n named"
+listopts="--guess-groups -z -n named"
outputfle=output/"$baseout".txt
errfle=output/"$baseout".err
-outputopts="-o $outputfle -e $errfle"
+afxgroupfle=output/"$baseout".afxgroup
+outputopts="-o $outputfle -e $errfle --output-affix-groups $afxgroupfle"
-mvn clean compile exec:java -Dexec.args="$outputopts $listopts $fileset"
+mvn compile exec:java -Dexec.args="$outputopts $listopts $fileset"
#java AffixLister $listopts $fileset > output/"$baseout".txt 2> output/"$baseout".err
tail -n 2 "$errfle"
diff --git a/src/main/java/tlIItools/Affix.java b/src/main/java/tlIItools/Affix.java
index d90c0108..c49e6e2a 100644
--- a/src/main/java/tlIItools/Affix.java
+++ b/src/main/java/tlIItools/Affix.java
@@ -1,112 +1,79 @@
package tlIItools;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Scanner;
-
-/**
- * Represents a Torchlight II affix.
- */
+import java.util.*;
+
+/** Represents a Torchlight II affix.
+ *
+ * @author Ben Culkin */
public class Affix {
- /**
- * Whether or not to record timing info.
- */
+ /** Details which sort of affix this is.
+ *
+ * @author Ben Culkin */
+ public enum AffixType {
+ /** An affix that applies to a normal item. */
+ ITEM,
+ /** An affix that applies to a socketable. */
+ SOCKETABLE,
+ /** An affix that applies to a monster/person. */
+ PERSONAL,
+ /** An affix that applies as an item enchantment. */
+ ENCHANTMENT
+ }
+
+ /** Whether or not to record timing info. */
public static boolean doTiming;
- /**
- * Internal name of the affix.
- */
+
+ /** Internal name of the affix. */
public String intName;
- /*
- * The prefix/suffix attached to the affix.
+ /* The prefix/suffix attached to the affix.
*
* In general, only one of these is set for a given affix.
*
* NOTE: Some affixes have a mis-set suffix/prefix ordering, and may need to be
- * changed in their files.
- */
- /**
- * The 'suffix name' attached to this affix.
+ * changed in their files. */
+
+ /** The 'suffix name' attached to this affix.
*
* Include the word [ITEM] to indicate where the item name should go.
*
* Note that it is not guaranteed that this is actually a suffix; it could be a
- * prefix.
- */
+ * prefix. */
public String affixSuffix;
- /**
- * The 'prefix name' attached to this affix.
+ /** The 'prefix name' attached to this affix.
*
* Include the word [ITEM] to indicate where the item name should go.
*
* Note that it is not guaranteed that this is actually a prefix; it could be a
- * suffix.
- */
+ * suffix. */
public String affixPrefix;
- /*
- * The min/max levels the affix can spawn at.
- */
- /**
- * The minimum level of an item this affix can spawn on.
- */
- public int minLevel;
- /**
- * The maximum level of an item this affix can spawn on.
- */
- public int maxLevel;
-
- /**
- * The spawn weight for the affix.
- */
+ /** The min/max levels the affix can spawn at. */
+ public LevelRange spawnRange = new LevelRange();
+
+ /** The spawn weight for the affix. */
public int weight;
- /**
- * The number of affix slots taken up.
- */
+ /** The number of affix slots taken up. */
public int slots;
- /**
- * Whether this is a socketable affix.
- */
- public boolean isSocketable;
- /**
- * Whether this is an enchantment, instead of a standard affix.
- */
- public boolean isEnchantment;
- /**
- * Whether this is a mob/player affix.
- */
- public boolean isPerson;
-
- /**
- * The types of equipment this can spawn on.
- */
+ /** The type of thing this affix applies to. */
+ public AffixType type = AffixType.ITEM;
+
+ /** The types of equipment this can spawn on. */
public List<String> equipTypes;
- /**
- * The types of equipment this can't spawn on.
- */
+ /** The types of equipment this can spawn on. */
public List<String> nonequipTypes;
-
- /**
- * Places to get this enchantment from;
- */
+ /** The types of equipment this can spawn on. */
public List<String> enchantSources;
-
- /**
- * The socketable types this spawns on.
- */
+ /** The types of equipment this can spawn on. */
public List<String> socketableTypes;
- /**
- * The effects attached to this affix.
- */
+ /** The effects attached to this affix. */
public List<Effect> effects;
- /**
- * Is this affix in an 'affix group' with another affix?
+ /** Determine whether this affix is in an 'affix group' with another affix.
*
* By 'affix group', what we mean is that it is essentially the same affix, with
* generally just differing levels/values.
@@ -115,50 +82,51 @@ public class Affix {
* strength would be considered to be in the same affix group (assuming that
* both of those strength bonuses had the same effect name).
*
- * @param afx
- * The affix to check if we are in an affix group with.
+ * @param afx The affix to check if we are in an affix group with.
*
- * @return Whether or now we are in an affix group with the specified affix.
- */
+ * @return Whether or now we are in an affix group with the specified affix. */
public boolean isInAffixGroup(Affix afx) {
if (effects == null) {
- if (afx.effects != null)
- return false;
- } else if (!effects.equals(afx.effects)) // TODO this isn't actually an equals;
- // maybe make this a 'isEquivalent' or
- // 'isSimilarEffect'?
- return false;
+ if (afx.effects != null) return false;
+ } else if (!effects.equals(afx.effects)) {
+ Iterator<Effect> leftIter = effects.iterator();
+ Iterator<Effect> rightIter = afx.effects.iterator();
+
+ while (leftIter.hasNext() && rightIter.hasNext()) {
+ Effect left = leftIter.next();
+ Effect right = rightIter.next();
+
+ if (!left.group.equals(right.group)) return false;
+ }
+ }
+
if (enchantSources == null) {
- if (afx.enchantSources != null)
- return false;
- } else if (!enchantSources.equals(afx.enchantSources))
- return false;
- if (equipTypes == null) {
- if (afx.equipTypes != null)
- return false;
- } else if (!equipTypes.equals(afx.equipTypes))
+ if (afx.enchantSources != null) return false;
+ } else if (!enchantSources.equals(afx.enchantSources)) {
return false;
- if (isEnchantment != afx.isEnchantment)
+ } else if (equipTypes == null) {
+ if (afx.equipTypes != null) return false;
+ } else if (!equipTypes.equals(afx.equipTypes)) {
return false;
- if (isPerson != afx.isPerson)
+ } else if (type == null) {
+ if (afx.type != null) return false;
+ } else if (type != afx.type) {
return false;
- if (isSocketable != afx.isSocketable)
+ } else if (nonequipTypes == null) {
+ if (afx.nonequipTypes != null) return false;
+ } else if (!nonequipTypes.equals(afx.nonequipTypes)) {
return false;
- if (nonequipTypes == null) {
- if (afx.nonequipTypes != null)
- return false;
- } else if (!nonequipTypes.equals(afx.nonequipTypes))
- return false;
- if (socketableTypes == null) {
- if (afx.socketableTypes != null)
- return false;
- } else if (!socketableTypes.equals(afx.socketableTypes))
+ } else if (socketableTypes == null) {
+ if (afx.socketableTypes != null) return false;
+ } else if (!socketableTypes.equals(afx.socketableTypes)) {
return false;
+ } else {
+ return true;
+ }
return true;
}
-
- /**
- * Gets the name of the 'affix group' that this affix is in.
+
+ /** Gets the 'affix group' that this affix is in.
*
* By 'affix group', what we mean is that it is essentially the same affix, with
* generally just differing levels/values.
@@ -168,85 +136,62 @@ public class Affix {
* both of those strength bonuses had the same effect name, and applied to the
* same sorts of items).
*
- * @return The name of the affix group
- */
- public String getAffixGroupName() {
- StringBuilder sb = new StringBuilder();
-
- for (Effect eft : effects) {
- sb.append(eft.getEffectGroup());
- }
-
- for (String enchantSource : enchantSources) {
- sb.append(enchantSource);
- }
-
- for (String equipType : equipTypes) {
- sb.append(equipType);
- }
-
- for (String nonEquipType : nonequipTypes) {
- sb.append(nonEquipType);
- }
-
- for (String socketableType : socketableTypes) {
- sb.append(socketableType);
- }
-
- return sb.toString();
+ * @return The affix group */
+ public AffixGroup toAffixGroup() {
+ AffixGroup group = new AffixGroup();
+
+ group.type = type;
+ group.enchantSources = enchantSources;
+ group.equipTypes = equipTypes;
+ group.nonequipTypes = nonequipTypes;
+ group.socketableTypes = socketableTypes;
+
+ for (Effect eff : effects) group.effects.add(eff.group);
+
+ return group;
}
- /*
- * Are invalid equip types being added?
+ /* Are invalid equip types being added?
*
* NOTE: This is kinda bad practice. It should really be handled via two
* separate affix methods, one to add a valid affix, one to add an invalid, w/
- * the caller keeping tracking/calling the right one.
- */
+ * the caller keeping tracking/calling the right one. */
private boolean inNonEquip;
- /**
- * Create a new blank affix.
- */
+ /** Create a new blank affix. */
public Affix() {
- equipTypes = new ArrayList<>();
- nonequipTypes = new ArrayList<>();
- enchantSources = new ArrayList<>();
+ equipTypes = new ArrayList<>();
+ nonequipTypes = new ArrayList<>();
+ enchantSources = new ArrayList<>();
socketableTypes = new ArrayList<>();
- effects = new ArrayList<>();
+ effects = new ArrayList<>();
}
- /**
- * Sets the set of equip types that is added to.
+ /** Sets the set of equip types that is added to.
*
- * @param nonequip
- * True if the equip types being added are prohibited, or false
- * if they are allowed.
- */
+ * @param nonequip True if the equip types being added are prohibited, or false
+ * if they are allowed. */
public void setEquipType(boolean nonequip) {
inNonEquip = nonequip;
}
- /**
- * Add an equip type to the right set of equip types.
+ /** Add an equip type to the right set of equip types.
*
- * @param type
- * The equip type to add.
- */
- public void addEquipType(String type) {
+ * @param equipType The equip type to add. */
+ public void addEquipType(String equipType) {
if (inNonEquip) {
- nonequipTypes.add(type);
+ nonequipTypes.add(equipType);
} else {
- if (type.equals("SOCKETABLE") || type.contains(" EMBER")) {
- isSocketable = true;
- socketableTypes.add(type);
- } else if (type.startsWith("ENCHANTER")) {
- isEnchantment = true;
- enchantSources.add(type.substring(10));
- } else if (type.equals("MONSTER") || type.equals("PLAYER")) {
- isPerson = true;
+ if (equipType.equals("SOCKETABLE") || equipType.contains(" EMBER")) {
+ type = AffixType.SOCKETABLE;
+ socketableTypes.add(equipType);
+ } else if (equipType.startsWith("ENCHANTER")) {
+ type = AffixType.ENCHANTMENT;
+ enchantSources.add(equipType.substring(10));
+ } else if (equipType.equals("MONSTER") || equipType.equals("PLAYER")) {
+ type = AffixType.PERSONAL;
} else {
- equipTypes.add(type);
+ equipTypes.add(equipType);
}
}
}
@@ -256,22 +201,71 @@ public class Affix {
return intName;
}
- /**
- * Print out the full details of this affix.
+ /** Print out a 'short-form' of this affix.
+ *
+ * @return The short form of this affix. */
+ public String toShortString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(intName);
+ sb.append("\n");
+
+ if (affixSuffix != null) {
+ sb.append("\tSuffix: ");
+ sb.append(affixSuffix);
+ sb.append("\n");
+ }
+
+ if (affixPrefix != null) {
+ sb.append("\tPrefix: ");
+ sb.append(affixPrefix);
+ sb.append("\n");
+
+ }
+
+ sb.append("\t");
+ sb.append(spawnRange.toString());
+ sb.append("\n");
+
+ sb.append("\tSpawn Weight: ");
+ sb.append(weight);
+ sb.append("\t");
+
+ if (slots == 0) {
+ sb.append("\tOccupies no slots\n");
+ } else {
+ sb.append("\tSlots: ");
+ sb.append(slots);
+ sb.append("\n");
+ }
+
+ if (effects.size() != 0) {
+ sb.append("\tEffects: ");
+ for (Effect eft : effects) {
+ if (effects.size() > 1) sb.append("\n\t\t");
+ sb.append(eft.toString());
+ }
+ sb.append("\n");
+ }
+
+ return sb.toString();
+ }
+
+ /** Print out the full details of this affix.
*
- * @return The full details of the affix.
- */
+ * @return The full details of the affix. */
public String toLongString() {
StringBuilder sb = new StringBuilder();
- if (isSocketable) {
+
+ if (type == AffixType.SOCKETABLE) {
sb.append("Socketable ");
- } else if (isPerson || (intName != null && intName.startsWith("HERO_"))) {
+ } else if (type == AffixType.PERSONAL || (intName != null && intName.startsWith("HERO_"))) {
sb.append("Personal ");
} else if (intName != null & intName.startsWith("MAP_")) {
sb.append("Area ");
}
- if (isEnchantment) {
+ if (type == AffixType.ENCHANTMENT) {
sb.append("Enchantment: ");
} else {
sb.append("Affix: ");
@@ -294,21 +288,7 @@ public class Affix {
}
sb.append("\t");
- if (minLevel <= 1 && maxLevel == 999) {
- sb.append("No Level Range");
- } else if (minLevel != 1 && maxLevel != 999) {
- sb.append("Level Range: ");
- sb.append(minLevel);
- sb.append("-");
- sb.append(maxLevel);
- } else if (minLevel <= 1) {
- sb.append("Max Level: ");
- sb.append(maxLevel);
- } else if (maxLevel == 999) {
- sb.append("Minimum Level: ");
- sb.append(minLevel);
- }
-
+ sb.append(spawnRange);
sb.append("\n");
sb.append("\tSpawn Weight: ");
@@ -324,18 +304,20 @@ public class Affix {
}
if (equipTypes.size() != 0) {
- if (isSocketable)
+ if (type == AffixType.SOCKETABLE) {
sb.append("\tSocketable Into: ");
- else if (isEnchantment)
+ } else if (type == AffixType.ENCHANTMENT) {
sb.append("\tEnchants Onto: ");
- else
+ } else {
sb.append("\tSpawns On: ");
+ }
+
sb.append(equipTypes);
sb.append("\n");
}
// Socketables & enchantments use this as a duplicate.
- if (!isSocketable && !isEnchantment && nonequipTypes.size() != 0) {
+ if (type != AffixType.SOCKETABLE && type != AffixType.ENCHANTMENT && nonequipTypes.size() != 0) {
sb.append("\tCan't Spawn On: ");
sb.append(nonequipTypes);
sb.append("\n");
@@ -362,6 +344,7 @@ public class Affix {
sb.append("\n");
}
+ /*
sb.append("Affix Group: ");
String afxGroupName = getAffixGroupName();
sb.append(afxGroupName);
@@ -369,36 +352,29 @@ public class Affix {
sb.append(afxGroupName.hashCode());
sb.append(")");
sb.append("\n");
+ */
return sb.toString();
}
- /**
- * Load an affix from an input source.
+ /** Load an affix from an input source.
*
- * @param scn
- * The input source to load from.
+ * @param scn The input source to load from.
*
- * @param fName
- * The name of the input source in question.
+ * @param fName The name of the input source in question.
*
- * @return The loaded affix.
- */
+ * @return The loaded affix. */
public static Affix loadAffix(Scanner scn, String fName) {
return loadAffix(scn, fName, new ArrayList<>());
}
- /**
- * Load an affix from an input source.
+ /** Load an affix from an input source.
*
- * @param scn
- * The input source to read from.
- * @param scnName
- * The name of the input source.
- * @param errors
- * A list to stick errors encountered during loading the affix.
- * @return The affix, loaded from the file.
- */
+ * @param scn The input source to read from.
+ * @param scnName The name of the input source.
+ * @param errors A list to stick errors encountered during loading the affix.
+ *
+ * @return The affix, loaded from the file. */
public static Affix loadAffix(Scanner scn, String scnName, List<String> errors) {
Affix afx = new Affix();
@@ -428,27 +404,32 @@ public class Affix {
afx.affixPrefix = splits[1];
break;
default:
- errors.add(
- String.format("Misformed affix translation: (%s) (%s) (%s)\n",
- splits[0], splits[1], scnName));
+ String msg = String.format(
+ "Misformed affix translation: (%s) (%s) (%s)\n",
+ splits[0], splits[1], scnName);
+ errors.add(msg);
}
} else if (ln.contains("MIN_SPAWN_RANGE")) {
- afx.minLevel = Integer.parseInt(splits[1]);
+ afx.spawnRange.minLevel = Integer.parseInt(splits[1]);
} else if (ln.contains("MAX_SPAWN_RANGE")) {
- afx.maxLevel = Integer.parseInt(splits[1]);
+ afx.spawnRange.maxLevel = Integer.parseInt(splits[1]);
} else if (ln.contains("WEIGHT:")) {
afx.weight = Integer.parseInt(splits[1]);
} else if (ln.contains("SLOTS_OCCUPY")) {
afx.weight = Integer.parseInt(splits[1]);
} else if (ln.contains("UNITTYPE") && !ln.contains("/")) {
if (splits.length == 1)
- errors.add(String.format("Malformed equip type: (%s) (%s)\n",
+ errors.add(String.format(
+ "Malformed equip type: (%s) (%s)\n",
splits[0], scnName));
+
afx.addEquipType(splits[1]);
} else if (splits[0].equals("<STRING>NAME")) {
if (splits.length == 1)
- errors.add(String.format("Malformed name: (%s) (%s)\n", splits[0],
- scnName));
+ errors.add(String.format(
+ "Malformed name: (%s) (%s)\n",
+ splits[0], scnName));
+
afx.intName = splits[1];
} else if (ln.contains("[EFFECT]")) {
List<String> eftErrs = new ArrayList<>();
@@ -459,21 +440,21 @@ public class Affix {
afx.effects.add(eft);
}
}
+
// Sort effects, so that they are in a stable order, even if specified out of
// order
afx.effects.sort(Comparator.comparingInt((val) -> val.hashCode()));
long endTime = System.nanoTime();
if (doTiming) {
- String fmt
- = "\tProcessed affix %s from %s in %d nanoseconds (%.2f seconds)\n\n";
+ String fmt = "\tProcessed affix %s from %s in %d nanoseconds (%.2f seconds)\n\n";
double seconds = ((double) (endTime - startTime) / 1000000000);
- errors.add(String.format(fmt, afx.intName, scnName, endTime - startTime,
- seconds));
+ errors.add(String.format(
+ fmt,
+ afx.intName, scnName, endTime - startTime, seconds));
}
return afx;
}
-
}
diff --git a/src/main/java/tlIItools/AffixGroup.java b/src/main/java/tlIItools/AffixGroup.java
new file mode 100644
index 00000000..5835614d
--- /dev/null
+++ b/src/main/java/tlIItools/AffixGroup.java
@@ -0,0 +1,130 @@
+package tlIItools;
+
+import java.util.*;
+
+import tlIItools.Affix.*;
+
+/**
+ * Represents a 'group' of related affixes.
+ *
+ * These are affixes that are do the same thing, but could be of varying levels;
+ * or have effects of differing intensity.
+ *
+ * @author Ben Culkin */
+public class AffixGroup implements Comparable<AffixGroup> {
+ public List<EffectGroup> effects;
+ /** The types of enchanters who can add this. */
+ public List<String> enchantSources;
+ /** The types of equipment this can spawn on. */
+ public List<String> equipTypes;
+ /** The types of equipment this cannot spawn on. */
+ public List<String> nonequipTypes;
+ /** The types of equipment this can be socketed into on. */
+ public List<String> socketableTypes;
+
+ /** The type of thing this affix applies to. */
+ public AffixType type = AffixType.ITEM;
+
+ /** Create a new affix group. */
+ public AffixGroup() {
+ effects = new ArrayList<>();
+ enchantSources = new ArrayList<>();
+ equipTypes = new ArrayList<>();
+ nonequipTypes = new ArrayList<>();
+ socketableTypes = new ArrayList<>();
+ }
+
+ /** Determine whether this affix group contains a particular affix.
+ *
+ * By 'affix group', what we mean is that it is essentially the same affix, with
+ * generally just differing levels/values.
+ *
+ * For instance, an affix that granted +2 strength, and one that granted +4
+ * strength would be considered to be in the same affix group (assuming that
+ * both of those strength bonuses had the same effect name).
+ *
+ * @param afx The affix to check if it is in the affix group with.
+ *
+ * @return Whether or the specified affix is in this affix group. */
+ public boolean contains(Affix afx) {
+ return afx.toAffixGroup().equals(this);
+ }
+
+ public String groupSummary() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("Affix Type: ");
+ sb.append(type);
+ sb.append("\n");
+ sb.append("Effects: ");
+ if (effects.size() > 1) sb.append("\n");
+ for (EffectGroup group : effects) {
+ if (effects.size() > 1) sb.append("\t");
+ sb.append(group.summary());
+ sb.append("\n");
+ }
+
+ if (type == AffixType.SOCKETABLE) {
+ sb.append("Affix can be socketed into: ");
+ sb.append(String.join(", ", equipTypes));
+ sb.append("\nAffix can spawn on: ");
+ sb.append(String.join(", ", socketableTypes));
+ sb.append("\n");
+ } else {
+ sb.append("Affix can spawn on: ");
+ sb.append(String.join(", ", equipTypes));
+ sb.append("\nAffix can't spawn on: ");
+ sb.append(String.join(", ", nonequipTypes));
+ sb.append("\n");
+ }
+
+ if (type == AffixType.ENCHANTMENT) {
+ sb.append("Affix can be enchanted by: ");
+ sb.append(String.join(", ", socketableTypes));
+ sb.append("\n");
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public int compareTo(AffixGroup other) {
+ return toString().compareTo(other.toString());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ for (EffectGroup group: effects) sb.append(group);
+ for (String enchantSource: enchantSources) sb.append(enchantSource);
+ for (String equipType: equipTypes) sb.append(equipType);
+ for (String nonEquipType: nonequipTypes) sb.append(nonEquipType);
+ for (String socketableType: socketableTypes) sb.append(socketableType);
+
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(effects, enchantSources, equipTypes, nonequipTypes,
+ socketableTypes, type);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ AffixGroup other = (AffixGroup) obj;
+ return Objects.equals(effects, other.effects)
+ && Objects.equals(enchantSources, other.enchantSources)
+ && Objects.equals(equipTypes, other.equipTypes)
+ && Objects.equals(nonequipTypes, other.nonequipTypes)
+ && Objects.equals(socketableTypes, other.socketableTypes)
+ && type == other.type;
+ }
+}
diff --git a/src/main/java/tlIItools/AffixLister.java b/src/main/java/tlIItools/AffixLister.java
index 3a096bd2..c9cce52a 100644
--- a/src/main/java/tlIItools/AffixLister.java
+++ b/src/main/java/tlIItools/AffixLister.java
@@ -5,79 +5,56 @@ import java.io.*;
import java.util.*;
import java.util.Map.Entry;
-/**
- * Lists randomly generated affixes for Torchlight II gear.
+/** Lists randomly generated affixes for Torchlight II gear.
*
- * @author Ben Culkin
- */
+ * @author Ben Culkin */
public class AffixLister {
- /*
- * Count of effects this class has loaded.
- */
+ /* Count of effects this class has loaded. */
private static int effectCount = 0;
- /**
- * Should the class record timing data?
- */
+ /** Should the class record timing data? */
public static boolean doTiming = false;
- /**
- * Should the file name be attached to things?
- */
+ /** Should the file name be attached to things? */
public static boolean addFileName = false;
- /*
- * Output streams to use.
- */
- /**
- * The normal output to use.
- */
+ /* Output streams to use. */
+
+ /** The normal output to use. */
public static PrintStream normOut = System.out;
- /**
- * The error output to use.
- */
+ /** The error output to use. */
public static PrintStream errOut = System.err;
+ /** The output to write affix groups to. */
+ public static PrintStream affixGroupDest = null;
- /**
- * Indicates how to treat affixes with regards to their names.
- */
+ /** Indicates how to treat affixes with regards to their names. */
public static enum NameMode {
- /**
- * Show all affixes.
- */
+ /** Show all affixes. */
ALL,
- /**
- * Show only unnamed affixes.
- */
+ /** Show only unnamed affixes. */
UNNAMED,
- /**
- * Show only named affixes.
- */
+ /** Show only named affixes. */
NAMED;
}
- /**
- * Main method.
+ /** Main method.
*
- * @param args The names of the files to read affix data from.
- */
+ * @param args The names of the files to read affix data from. */
public static void main(String[] args) {
listAffixes(args);
}
- /**
- * Main method that actually does stuff.
+ /** Main method that actually does stuff.
*
- * @param args
- * The names of the files to read affix data from.
- */
+ * @param args The names of the files to read affix data from.
+ *
+ * @return The listed affix set. */
+ @SuppressWarnings("unused")
public static AffixSet listAffixes(String[] args) {
- AffixSet affixSetByName = new AffixSet();
+ boolean doingArgs = true;
- boolean doingArgs = true;
-
- boolean omitZeros = false;
- boolean listZeros = false;
+ boolean omitZeros = false;
+ boolean listZeros = false;
NameMode nameMode = NameMode.ALL;
@@ -86,193 +63,164 @@ public class AffixLister {
int zeroCount = 0;
int groupCount = 0;
- boolean outputAffixGroups = false;
- OutputStream affixGroupDest = null;
-
- Map<String, Set<Affix>> groupContents = affixSetByName.affixGroups;
+ boolean outputAffixGroups = false;
- Set<Affix> nonGroupContents = affixSetByName.ungroupedAffixes;
+ Map<String, Set<Affix>> groupContents = new HashMap<>();
+ Set<Affix> nonGroupContents = new HashSet<>();
NameFileReader nfr = new NameFileReader(false);
nfr.groupRx = ".*/mods/([^/]+)/*";
long startTime = System.nanoTime();
- for(int i = 0; i < args.length; i++) {
+ for (int i = 0; i < args.length; i++) {
String fName = args[i];
if (doingArgs && fName.startsWith("-")) {
boolean isArg = true;
switch (fName) {
- case "--":
- doingArgs = false;
- break;
-
- case "--omit-zero":
- case "-z":
- omitZeros = true;
- break;
-
- case "--no-omit-zero":
- case "-Z":
- omitZeros = false;
- break;
-
- case "--list-zero":
- case "-l":
- listZeros = true;
- break;
-
- case "--no-list-zero":
- case "-L":
- listZeros = false;
- break;
-
- case "--timing":
- case "-t":
- doTiming = true;
- Effect.doTiming = true;
+ case "--":
+ doingArgs = false;
+ break;
+ case "--omit-zero":
+ case "-z":
+ omitZeros = true;
+ break;
+ case "--no-omit-zero":
+ case "-Z":
+ omitZeros = false;
+ break;
+ case "--list-zero":
+ case "-l":
+ listZeros = true;
+ break;
+ case "--no-list-zero":
+ case "-L":
+ listZeros = false;
+ break;
+ case "--timing":
+ case "-t":
+ doTiming = true;
+ Effect.doTiming = true;
+ break;
+ case "--no-timing":
+ case "-T":
+ doTiming = false;
+ Effect.doTiming = false;
+ break;
+ case "--file-names":
+ case "-f":
+ addFileName = true;
+ break;
+ case "--no-file-names":
+ case "-F":
+ addFileName = true;
+ break;
+ case "--name-mode":
+ case "-n":
+ if (i + 1 >= args.length) {
+ errOut.printf("ERROR: name mode argument requires the mode to use be specified (all, unnamed or named)\n");
break;
+ }
- case "--no-timing":
- case "-T":
- doTiming = false;
- Effect.doTiming = false;
- break;
-
- case "--file-names":
- case "-f":
- addFileName = true;
- break;
-
- case "--no-file-names":
- case "-F":
- addFileName = true;
- break;
-
- case "--name-mode":
- case "-n":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: name mode argument requires the mode to use be specified (all, unnamed or named)\n");
- break;
- }
-
- nameMode = NameMode.valueOf(args[++i].toUpperCase());
-
- break;
-
- case "--file-group":
- case "-g":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: file group argument requires the group name to use be specified\n");
- break;
- }
-
- nfr.swapGroup(args[++i]);
-
- break;
-
- case "--guess-groups":
- nfr.guessGroups = true;
-
- break;
-
- case "--no-guess-groups":
- nfr.guessGroups = false;
-
+ nameMode = NameMode.valueOf(args[++i].toUpperCase());
+ break;
+ case "--file-group":
+ case "-g":
+ if (i + 1 >= args.length) {
+ errOut.printf("ERROR: file group argument requires the group name to use be specified\n");
break;
+ }
- case "--read-names-from-file":
- case "-r":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: read name file argument requires the file to use be specified\n");
- break;
- }
-
- nfr.readFrom(args[++i]);
-
+ nfr.swapGroup(args[++i]);
+ break;
+ case "--guess-groups":
+ nfr.guessGroups = true;
+ break;
+ case "--no-guess-groups":
+ nfr.guessGroups = false;
+ break;
+ case "--read-names-from-file":
+ case "-r":
+ if (i + 1 >= args.length) {
+ errOut.printf("ERROR: read name file argument requires the file to use be specified\n");
break;
+ }
- case "--output":
- case "-o":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: output file argument requires the file to use be specified\n");
- break;
- }
-
- try {
- normOut = new PrintStream(args[++i]);
-
- nfr.normOut = normOut;
- } catch (IOException ioex) {
- errOut.printf("Could not open output file %s\n", args[i]);
-
- ioex.printStackTrace(errOut);
- }
-
+ nfr.readFrom(args[++i]);
+ break;
+ case "--output":
+ case "-o":
+ if (i + 1 >= args.length) {
+ errOut.printf("ERROR: output file argument requires the file to use be specified\n");
break;
+ }
- case "--output-errors":
- case "-e":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: error output file argument requires the file to use be specified\n");
- break;
- }
-
- try {
- errOut = new PrintStream(args[++i]);
-
- nfr.errOut = errOut;
- } catch (IOException ioex) {
- errOut.printf("Could not open error output file %s\n", args[i]);
-
- ioex.printStackTrace(errOut);
- }
+ try {
+ normOut = new PrintStream(args[++i]);
+ nfr.normOut = normOut;
+ } catch (IOException ioex) {
+ errOut.printf("Could not open output file %s\n", args[i]);
+ ioex.printStackTrace(errOut);
+ }
+ break;
+ case "--output-errors":
+ case "-e":
+ if (i + 1 >= args.length) {
+ errOut.printf("ERROR: error output file argument requires the file to use be specified\n");
break;
+ }
- case "--guess-regex":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: group regex argument requires the regex to use be specified\n");
- break;
- }
-
- nfr.groupRx = args[++i];
+ try {
+ errOut = new PrintStream(args[++i]);
+ nfr.errOut = errOut;
+ } catch (IOException ioex) {
+ errOut.printf("Could not open error output file %s\n", args[i]);
+ ioex.printStackTrace(errOut);
+ }
+ break;
+ case "--guess-regex":
+ if (i + 1 >= args.length) {
+ errOut.printf("ERROR: group regex argument requires the regex to use be specified\n");
break;
- case "--output-affix-groups":
- if (i + 1 >= args.length) {
- errOut.printf("ERROR: to output affix-groups, must specify a file to output them to");
- break;
- }
-
- try {
- affixGroupDest = new PrintStream(args[++i]);
-
- outputAffixGroups = true;
- } catch (IOException ioex) {
- errOut.printf("ERROR: Couldn't open file %s to write affix groups to\n", affixGroupDest);
- }
+ }
+ nfr.groupRx = args[++i];
+ break;
+ case "--output-affix-groups":
+ if (i + 1 >= args.length) {
+ errOut.printf(
+ "ERROR: to output affix-groups, must specify a file to output them to");
break;
- default:
- isArg = false;
- }
+ }
- if (isArg) {
- continue;
+ try {
+ affixGroupDest = new PrintStream(args[++i]);
+ outputAffixGroups = true;
+ } catch (IOException ioex) {
+ errOut.printf(
+ "ERROR: Couldn't open file %s to write affix groups to\n",
+ affixGroupDest);
+ }
+ break;
+ default:
+ isArg = false;
}
- nfr.addFile(fName);
+ if (isArg) continue;
+ else nfr.addFile(fName);
} else {
nfr.addFile(fName);
}
}
AffixSet affixSetByContents = new AffixSet();
-
+
for (Entry<String, List<String>> fGroup : nfr.fNames.entrySet()) {
if (fGroup.getValue().size() == 0) continue;
+
normOut.printf("\nFile Group '%s' starting\n", fGroup.getKey());
for (String fName : fGroup.getValue()) {
try (FileReader fr = new FileReader(fName)) {
@@ -280,7 +228,7 @@ public class AffixLister {
Affix afx = Affix.loadAffix(sc, fName);
affixSetByContents.addAffixByContents(afx);
-
+
effectCount += afx.effects.size();
if (afx.intName != null && afx.weight != 0) {
@@ -290,10 +238,12 @@ public class AffixLister {
if (!groupContents.containsKey(groupName)) {
groupCount += 1;
- // errOut.printf("\tTRACE: Counted distinct group %s from %s\n", groupName, afx.intName);
+ // errOut.printf("\tTRACE: Counted distinct group %s from
+ // %s\n", groupName, afx.intName);
if (hasGroup) {
- // errOut.printf("\tTRACE: Counted actual group %s from %s\n", groupName, afx.intName);
+ // errOut.printf("\tTRACE: Counted actual group %s from
+ // %s\n", groupName, afx.intName);
groupContents.put(groupName, new HashSet<>());
} else {
@@ -309,58 +259,88 @@ public class AffixLister {
if (afx.weight == 0) zeroCount += 1;
if (afx.weight == 0 && !listZeros) {
- if (!omitZeros)
- normOut.printf("\nAffix %s has zero spawn weight\n", afx.intName);
+ if (!omitZeros) normOut.printf(
+ "\nAffix %s has zero spawn weight\n",
+ afx.intName);
} else {
- boolean isNamed = (afx.affixSuffix != null) || (afx.affixPrefix != null);
+ boolean isNamed =
+ (afx.affixSuffix != null) || (afx.affixPrefix != null);
- if (isNamed) namedCount += 1;
+ if (isNamed) namedCount += 1;
else unnamedCount += 1;
- if (nameMode == NameMode.UNNAMED && isNamed) continue;
+ if (nameMode == NameMode.UNNAMED && isNamed) continue;
if (nameMode == NameMode.NAMED && !isNamed) continue;
normOut.printf("\n%s\n", afx.toLongString());
}
} catch (Exception ex) {
- errOut.printf("Something bad happened for file %s:%s\n", fName, ex.getMessage());
+ errOut.printf(
+ "Something bad happened for file %s:%s\n",
+ fName, ex.getMessage());
ex.printStackTrace(errOut);
-
errOut.println();
}
}
+
normOut.printf("\nFile Group '%s' ending\n", fGroup.getKey());
}
errOut.println("\nGroup Contents: ");
- for (Entry<String, Set<Affix>> ent: groupContents.entrySet()) {
+
+ for (Entry<String, Set<Affix>> ent : groupContents.entrySet()) {
errOut.printf("\t%s: %s\n", ent.getKey(), ent.getValue());
}
+
errOut.println();
errOut.println();
if (outputAffixGroups) {
- for (Entry<String, Set<Affix>> ent : affixSetByContents.affixGroups.entrySet()) {
- String groupName = ent.getKey();
- Set<Affix> affixes = ent.getValue();
-
- boolean isFirstAfx = true;
- for (Affix afx : affixes) {
- if (isFirstAfx) {
- // Print the header for this
- // group
- isFirstAfx = false;
- }
- // print this affix in the group format
+ for (Entry<AffixGroup, Set<Affix>> entry
+ : affixSetByContents.affixGroups.entrySet())
+ {
+ AffixGroup group = entry.getKey();
+ Set<Affix> affixes = entry.getValue();
+
+ // Skip one-affix groups
+ if (affixes.size() == 1) continue;
+
+ List<Affix> affixList = new ArrayList<>(affixes);
+ Collections.sort(affixList, (lhs, rhs) -> lhs.spawnRange.compareTo(rhs.spawnRange));
+
+ boolean isFirstAfx = true;
+ for (Affix afx : affixList) {
+ // @TODO actually implement this -bculkin, 12/29/2020
+
+ // Print the header for this group
+ if (isFirstAfx) {
+ isFirstAfx = false;
+ affixGroupDest.printf("Group ID %s (%d affixes)\t",
+ group.hashCode(), affixes.size());
+ affixGroupDest.print(group.groupSummary());
}
+
+ affixGroupDest.print("\t");
+ affixGroupDest.print(afx.toShortString().replaceAll("\n(?=.)", "\n\t"));
+ // print this affix in the group format
}
+ affixGroupDest.println();
+ }
}
long endTime = System.nanoTime();
- errOut.printf("\nProcessed %,d affixes (%,d named, %,d unnamed, %,d zero-weight) (%,d effects) (%,d distinct groups, %,d actual groups, %,d nongrouped affixes) out of %,d files (%,d groups) in %,d nanoseconds (%.2f seconds)\n", nfr.fCount, namedCount, unnamedCount, zeroCount, effectCount, groupCount, groupContents.size(), nonGroupContents.size(), nfr.fCount, nfr.fNames.size(), endTime - startTime, ((double)(endTime - startTime) / 1000000000));
- errOut.printf("\tOptions: Name Mode: %s, Special-case zero weight: %s, Noting zero-weight in special case: %s\n", nameMode, !listZeros, !omitZeros);
-
- return affixSetByName;
+ errOut.printf(
+ "\nProcessed %,d affixes (%,d named, %,d unnamed, %,d zero-weight) (%,d effects) (%,d distinct groups, %,d actual groups, %,d nongrouped affixes) out of %,d files (%,d groups) in %,d nanoseconds (%.2f seconds)\n",
+ nfr.fCount, namedCount, unnamedCount, zeroCount, effectCount, groupCount,
+ groupContents.size(), nonGroupContents.size(), nfr.fCount,
+ nfr.fNames.size(), endTime - startTime,
+ ((double) (endTime - startTime) / 1000000000));
+
+ errOut.printf(
+ "\tOptions: Name Mode: %s, Special-case zero weight: %s, Noting zero-weight in special case: %s\n",
+ nameMode, !listZeros, !omitZeros);
+
+ return affixSetByContents;
}
}
diff --git a/src/main/java/tlIItools/AffixSet.java b/src/main/java/tlIItools/AffixSet.java
index d7f14211..fa5c8d19 100644
--- a/src/main/java/tlIItools/AffixSet.java
+++ b/src/main/java/tlIItools/AffixSet.java
@@ -2,65 +2,50 @@ package tlIItools;
import java.util.*;
-/**
- * Container of a set of affixes.
+/** Container of a set of affixes.
*
- * @author Ben Culkin
- */
+ * @author Ben Culkin */
public class AffixSet {
private static class AffixComparator implements Comparator<Affix> {
+ @Override
public int compare(Affix a1, Affix a2) {
- if (a1.minLevel == a2.minLevel) {
- return a1.maxLevel - a2.maxLevel;
- }
-
- return a1.minLevel - a2.minLevel;
+ return a1.spawnRange.compareTo(a2.spawnRange);
}
}
- /**
- * All of the affix groups contained in this set.
+ /** All of the affix groups contained in this set.
*
* An affix group is a set of affixes that generally have the same or
- * similar effects, but have different intensities or spawn levels.
- */
- public Map<String, Set<Affix>> affixGroups;
+ * similar effects, but have different intensities or spawn levels. */
+ public Map<AffixGroup, Set<Affix>> affixGroups;
- /**
- * All of the ungrouped affixes contained in this set.
- */
+ /** All of the ungrouped affixes contained in this set. */
public Set<Affix> ungroupedAffixes;
- /**
- * Create a new blank affix set.
- */
+ /** Create a new blank affix set. */
public AffixSet() {
affixGroups = new TreeMap<>();
ungroupedAffixes = new TreeSet<>(new AffixComparator());
}
-
- /**
- * Add an affix to this set.
+
+ /** Add an affix to this set.
*
- * @param afx The affix to add.
- */
+ * @param afx The affix to add. */
public void addAffixByContents(Affix afx) {
- String afxGroup = afx.getAffixGroupName();
-
+ AffixGroup group = afx.toAffixGroup();
+ String afxGroup = group.toString();
+
if (afxGroup.equals("")) {
ungroupedAffixes.add(afx);
} else {
- affixGroups.compute(afxGroup, (key, val) -> {
+ affixGroups.compute(group, (key, val) -> {
if (val == null) {
Set<Affix> afxSet = new HashSet<>();
-
afxSet.add(afx);
-
return afxSet;
} else {
val.add(afx);
-
return val;
}
});
diff --git a/src/main/java/tlIItools/Effect.java b/src/main/java/tlIItools/Effect.java
index d641b112..fd7867d0 100644
--- a/src/main/java/tlIItools/Effect.java
+++ b/src/main/java/tlIItools/Effect.java
@@ -1,255 +1,139 @@
package tlIItools;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
+import java.util.*;
-/**
- * Represents an effect attached to an affix.
+/** Represents an effect attached to an affix.
*
- * @author Ben Culkin
- */
+ * @author Ben Culkin */
public class Effect {
- /**
- * Count of all loaded effects.
- */
+ /** Count of all loaded effects. */
public static int effectCount = 0;
- /**
- * Do timing analysis when loading effects.
- */
+ /** Do timing analysis when loading effects. */
public static boolean doTiming;
- /**
- * The file name this effect came from.
- */
+ /** The file name this effect came from. */
public String fName;
-
- /**
- * The name of the effect.
- */
- public String name;
-
- /**
- * The specific effect that happens.
- */
- public String type;
-
- /**
- * Damage type for the effect, if applicable.
- */
- public String damageType = "physical";
-
- /**
- * Duration of the effect.
- */
+ /** Duration of the effect. */
public double duration;
- /**
- * Whether or not we have a duration or not.
- */
- public boolean hasDuration;
-
- /**
- * Minimum value for the effect.
- */
+ /** Minimum value for the effect. */
public double minValue;
- /**
- * Maximum value for the effect.
- */
+ /** Maximum value for the effect. */
public double maxValue;
- /**
- * The name of the stat that applies to this affect.
- */
- public String statName;
- /**
- * The percent of the stat value to apply.
- */
+ /** The percent of the stat value to apply. */
public double statPercent;
- /**
- * Whether or not this stat is a bonus value.
- */
- public boolean isStatBonus;
-
- /**
- * Whether or not this uses the owners level to modify
- * any applicable graph.
- */
- public boolean ownerLevel;
-
- /**
- * The graph to use instead of the default graph.
- */
- public String graphOverride;
-
- /**
- * Whether or not a graph is used for this effect.
- */
- public boolean useGraph = true;
-
- /**
- * Whether this effect can stack with itself.
- */
- public boolean exclusive;
-
- /**
- * The amount the targets armor is reduced by for this
- * effect.
- */
+ /** The amount the targets armor is reduced by for this effect. */
public double soakScale = 1.0;
- /**
- * Level of the effect.
- */
+ /** Level of the effect. */
public int level = -1;
- /**
- * Whether or not this effect is a 'transfer'
- * effect (Applied to the enemy on a hit).
- */
- public boolean isTransfer;
-
- /**
- * The amount to resist/do knockback by.
- */
+ /** The 'effect group' this effect belongs to.
+ *
+ * An 'effect group is essentially any other effect that is the same general sort of effect, just with different details.
+ *
+ * For instance, an effect that grants +4 strength would group with one granting +8 strength,
+ * assuming that most other details were equal. */
+ public EffectGroup group = new EffectGroup();
+
+ /** The amount to resist/do knockback by. */
public double resist;
- /**
- * Minimum value per monster.
- */
+ /** Minimum value per monster. */
public double minPer;
- /**
- * Maximum value per monster.
- */
+ /** Maximum value per monster. */
public double maxPer;
- /**
- * Range for effect.
- */
+ /** Range for effect. */
public double range;
- /**
- * Maximum count of monsters.
- */
+ /** Maximum count of monsters. */
public double maxCount;
- /**
- * The rate at which the effect fires.
- */
+ /** The rate at which the effect fires. */
public double pulse;
- /**
- * Create a new blank effect.
- */
+ /** Create a new blank effect. */
public Effect() {
}
-
- /**
- * Gets the 'effect group' this effect belongs to.
- *
- * An 'effect group is essentially any other effect that is the same general sort of effect, just with different details.
- *
- * For instance, an effect that grants +4 strength would group with one granting +8 strength,
- * assuming that most other details were equal.
- *
- * @return The 'effect group' this effect belongs to.
- */
- public String getEffectGroup() {
- StringBuilder sb = new StringBuilder();
-
- sb.append(name);
- sb.append(type);
- sb.append(damageType);
- sb.append(hasDuration);
- sb.append(statName);
- sb.append(isStatBonus);
- sb.append(ownerLevel);
- sb.append(graphOverride);
- sb.append(useGraph);
- sb.append(exclusive);
- sb.append(isTransfer);
-
- return sb.toString();
- }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- if (isTransfer) {
+ if (group.isTransfer) {
sb.append("Inflict on Hit: ");
}
- Map<String, String> detMap = hasDuration ? EffectRepo.timeDetals : EffectRepo.detals;
+ Map<String, String> detMap = group.hasDuration ? EffectRepo.timeDetals : EffectRepo.detals;
- if (detMap.containsKey(type) || (hasDuration && !EffectRepo.timeDetals.containsKey(type) && EffectRepo.detals.containsKey(type))) {
+ if (detMap.containsKey(group.type) ||
+ (group.hasDuration
+ && !EffectRepo.timeDetals.containsKey(group.type)
+ && EffectRepo.detals.containsKey(group.type)))
+ {
String fmt;
- if (hasDuration && !EffectRepo.timeDetals.containsKey(type) && EffectRepo.detals.containsKey(type)) {
- AffixLister.errOut.printf("Improvised details for timed %s\n", type);
- fmt = EffectRepo.detals.get(type) + "for <DUR> seconds";
+ if (group.hasDuration
+ && !EffectRepo.timeDetals.containsKey(group.type)
+ && EffectRepo.detals.containsKey(group.type))
+ {
+ AffixLister.errOut.printf("Improvised details for timed %s\n", group.type);
+ fmt = EffectRepo.detals.get(group.type) + "for <DUR> seconds";
} else {
- fmt = detMap.get(type);
+ fmt = detMap.get(group.type);
}
// Expand aliases first.
- for (ReplPair repl : EffectRepo.replList) {
- fmt = fmt.replaceAll(repl.find, repl.replace);
- }
+ for (ReplPair repl : EffectRepo.replList) fmt = fmt.replaceAll(repl.find, repl.replace);
- if (minValue <= 0 && maxValue <= 0) {
- fmt = fmt.replaceAll("<C\\|([^|>]+)\\|([^|>]+)>", "$1");
- }
-
- if (minValue >= 0 && maxValue >= 0) {
- fmt = fmt.replaceAll("<C\\|([^|>]+)\\|([^|>]+)>", "$2");
- }
-
- if (minPer <= 0 && maxPer <= 0) {
- fmt = fmt.replaceAll("<MC\\|(\\w+)\\|(\\w+)>", "$1");
- }
-
- if (minPer >= 0 && maxPer >= 0) {
- fmt = fmt.replaceAll("<MC\\|([^|>]+)\\|([^|>]+)>", "$2");
- }
+ if (minValue <= 0 && maxValue <= 0) { fmt = fmt.replaceAll("<C\\|([^|>]+)\\|([^|>]+)>", "$1"); }
+ if (minValue >= 0 && maxValue >= 0) { fmt = fmt.replaceAll("<C\\|([^|>]+)\\|([^|>]+)>", "$2"); }
+ if (minPer <= 0 && maxPer <= 0) { fmt = fmt.replaceAll("<MC\\|(\\w+)\\|(\\w+)>", "$1"); }
+ if (minPer >= 0 && maxPer >= 0) { fmt = fmt.replaceAll("<MC\\|([^|>]+)\\|([^|>]+)>", "$2"); }
if (fmt.contains("<") || fmt.contains(">")) {
- AffixLister.errOut.printf("WARN: Details for effect %s are malformatted (contains < or >):\n\t%s\n", type, fmt);
+ AffixLister.errOut.printf("WARN: Details for effect %s are malformatted (contains < or >):\n\t%s\n", group.type, fmt);
}
- sb.append(String.format(fmt, Math.abs(minValue), Math.abs(maxValue), duration, damageType.toLowerCase(), level, resist, name, Math.abs(minPer), Math.abs(maxPer), range, maxCount, pulse));
+ sb.append(String.format(fmt,
+ Math.abs(minValue), Math.abs(maxValue),
+ duration, group.damageType.toLowerCase(), level, resist, group.name,
+ Math.abs(minPer), Math.abs(maxPer), range, maxCount, pulse));
} else {
sb.append("No effect details for effect ");
- sb.append(type);
- sb.append(String.format(" with parameters (min %.2f, max %.2f, dur %.2f, type %s, level %d)", minValue, maxValue, duration, damageType.toLowerCase(), level, name));
+ sb.append(group.type);
+ sb.append(String.format(
+ " with parameters (min %.2f, max %.2f, dur %.2f, type %s, level %d)",
+ minValue, maxValue, duration, group.damageType.toLowerCase(), level, group.name));
if (AffixLister.addFileName) {
sb.append(" from file ");
sb.append(fName);
}
- if (hasDuration) AffixLister.errOut.print("TIMED: ");
+ if (group.hasDuration) AffixLister.errOut.print("TIMED: ");
AffixLister.errOut.println(sb.toString());
}
- if (name != null) {
+ if (group.name != null) {
sb.append(" (named ");
- sb.append(name);
+ sb.append(group.name);
sb.append(")");
}
- if (exclusive) sb.append(" (Exclusive)");
+ if (group.exclusive) sb.append(" (Exclusive)");
- if (graphOverride != null) {
+ if (group.graphOverride != null) {
sb.append(" (Uses ");
- sb.append(graphOverride);
+ sb.append(group.graphOverride);
sb.append(" graph)");
}
- if (ownerLevel) {
+ if (group.ownerLevel) {
sb.append(" (Uses owner level for graph)");
}
@@ -265,154 +149,77 @@ public class Effect {
sb.append(")");
}
- if (statName != null) {
- sb.append(String.format(" (%.2f of stat %s", statPercent, statName));
- if (isStatBonus) sb.append(" as bonus)");
+ if (group.statName != null) {
+ sb.append(String.format(" (%.2f of stat %s", statPercent, group.statName));
+
+ if (group.isStatBonus) sb.append(" as bonus)");
else sb.append(")");
}
- if (!useGraph) sb.append(" (Ignoring graph)");
+ if (!group.useGraph) sb.append(" (Ignoring graph)");
return sb.toString();
}
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((damageType == null) ? 0 : damageType.hashCode());
- long temp;
- temp = Double.doubleToLongBits(duration);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- result = prime * result + (exclusive ? 1231 : 1237);
- result = prime * result
- + ((graphOverride == null) ? 0 : graphOverride.hashCode());
- result = prime * result + (hasDuration ? 1231 : 1237);
- result = prime * result + (isStatBonus ? 1231 : 1237);
- result = prime * result + (isTransfer ? 1231 : 1237);
- result = prime * result + level;
- temp = Double.doubleToLongBits(maxCount);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(maxPer);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(maxValue);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(minPer);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(minValue);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- result = prime * result + (ownerLevel ? 1231 : 1237);
- temp = Double.doubleToLongBits(pulse);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(range);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(resist);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(soakScale);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- result = prime * result + ((statName == null) ? 0 : statName.hashCode());
- temp = Double.doubleToLongBits(statPercent);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- result = prime * result + ((type == null) ? 0 : type.hashCode());
- result = prime * result + (useGraph ? 1231 : 1237);
- return result;
- }
@Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- Effect other = (Effect) obj;
- if (damageType == null) {
- if (other.damageType != null)
- return false;
- } else if (!damageType.equals(other.damageType))
- return false;
- if (Double.doubleToLongBits(duration) != Double.doubleToLongBits(other.duration))
- return false;
- if (exclusive != other.exclusive)
- return false;
- if (graphOverride == null) {
- if (other.graphOverride != null)
- return false;
- } else if (!graphOverride.equals(other.graphOverride))
- return false;
- if (hasDuration != other.hasDuration)
- return false;
- if (isStatBonus != other.isStatBonus)
- return false;
- if (isTransfer != other.isTransfer)
- return false;
- if (level != other.level)
- return false;
- if (Double.doubleToLongBits(maxCount) != Double.doubleToLongBits(other.maxCount))
- return false;
- if (Double.doubleToLongBits(maxPer) != Double.doubleToLongBits(other.maxPer))
- return false;
- if (Double.doubleToLongBits(maxValue) != Double.doubleToLongBits(other.maxValue))
- return false;
- if (Double.doubleToLongBits(minPer) != Double.doubleToLongBits(other.minPer))
- return false;
- if (Double.doubleToLongBits(minValue) != Double.doubleToLongBits(other.minValue))
- return false;
- if (name == null) {
- if (other.name != null)
- return false;
- } else if (!name.equals(other.name))
- return false;
- if (ownerLevel != other.ownerLevel)
- return false;
- if (Double.doubleToLongBits(pulse) != Double.doubleToLongBits(other.pulse))
- return false;
- if (Double.doubleToLongBits(range) != Double.doubleToLongBits(other.range))
- return false;
- if (Double.doubleToLongBits(resist) != Double.doubleToLongBits(other.resist))
- return false;
- if (Double.doubleToLongBits(soakScale)
- != Double.doubleToLongBits(other.soakScale))
- return false;
- if (statName == null) {
- if (other.statName != null)
- return false;
- } else if (!statName.equals(other.statName))
- return false;
- if (Double.doubleToLongBits(statPercent)
- != Double.doubleToLongBits(other.statPercent))
- return false;
- if (type == null) {
- if (other.type != null)
- return false;
- } else if (!type.equals(other.type))
- return false;
- if (useGraph != other.useGraph)
- return false;
- return true;
- }
-
- /**
- * Parse an effect.
+ public int hashCode() {
+ return Objects.hash(duration, fName, group, level, maxCount, maxPer, maxValue,
+ minPer, minValue, pulse, range, resist, soakScale, statPercent);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Effect other = (Effect) obj;
+ return Double.doubleToLongBits(duration)
+ == Double.doubleToLongBits(other.duration)
+ && Objects.equals(fName, other.fName)
+ && Objects.equals(group, other.group) && level == other.level
+ && Double.doubleToLongBits(maxCount)
+ == Double.doubleToLongBits(other.maxCount)
+ && Double.doubleToLongBits(maxPer)
+ == Double.doubleToLongBits(other.maxPer)
+ && Double.doubleToLongBits(maxValue)
+ == Double.doubleToLongBits(other.maxValue)
+ && Double.doubleToLongBits(minPer)
+ == Double.doubleToLongBits(other.minPer)
+ && Double.doubleToLongBits(minValue)
+ == Double.doubleToLongBits(other.minValue)
+ && Double.doubleToLongBits(pulse) == Double.doubleToLongBits(other.pulse)
+ && Double.doubleToLongBits(range) == Double.doubleToLongBits(other.range)
+ && Double.doubleToLongBits(resist)
+ == Double.doubleToLongBits(other.resist)
+ && Double.doubleToLongBits(soakScale)
+ == Double.doubleToLongBits(other.soakScale)
+ && Double.doubleToLongBits(statPercent)
+ == Double.doubleToLongBits(other.statPercent);
+ }
+
+ /** Parse an effect.
+ *
* @param afx The affix the effect belongs to.
* @param scn The scanner to read from.
* @param scnSource The name of the scanner.
+ *
* @return An effect, read from the scanner.
*/
public static Effect parseEffect(Affix afx, Scanner scn, String scnSource) {
return parseEffect(afx, scn, scnSource, new ArrayList<>());
}
- /**
- * Parse an effect.
+ /** Parse an effect.
+ *
* @param afx The affix the effect belongs to.
* @param scn The scanner to read from.
* @param scnSource The name of the scanner.
* @param errs Repository for errors found while parsing.
- * @return An effect, read from the scanner.
- */
+ *
+ * @return An effect, read from the scanner. */
public static Effect parseEffect(Affix afx, Scanner scn, String scnSource, List<String> errs) {
Effect efct = new Effect();
@@ -432,11 +239,11 @@ public class Effect {
if (splits.length == 1) continue;
if (ln.contains("NAME")) {
- efct.name = splits[1];
+ efct.group.name = splits[1];
} else if (ln.contains("DAMAGE_TYPE")) {
- efct.damageType = splits[1];
+ efct.group.damageType = splits[1];
} else if (ln.contains("TYPE")) {
- efct.type = splits[1];
+ efct.group.type = splits[1];
} else if (ln.contains("ACTIVATION")) {
switch (splits[1]) {
case "DYNAMIC":
@@ -446,31 +253,31 @@ public class Effect {
// actual difference.
break;
case "TRANSFER":
- efct.isTransfer = true;
+ efct.group.isTransfer = true;
break;
default:
- errs.add(String.format("Malformed activation type: (%s) (%s) (%s)\n", splits[1], efct.name, afx.intName));
+ errs.add(String.format("Malformed activation type: (%s) (%s) (%s)\n", splits[1], efct.group.name, afx.intName));
}
} else if (ln.contains("DURATION")) {
if (splits[1].equals("ALWAYS")) {
- efct.hasDuration = false;
+ efct.group.hasDuration = false;
efct.duration = Double.POSITIVE_INFINITY;
} else if (splits[1].equals("INSTANT")) {
- efct.hasDuration = false;
+ efct.group.hasDuration = false;
efct.duration = Double.NaN;
} else if (splits[1].equals("PERCENT")) {
- efct.hasDuration = false;
+ efct.group.hasDuration = false;
efct.duration = Double.NaN;
errs.add(String.format("WARN: Punting on DURATION:PERCENT for %s\n", scnSource));
} else if (splits[1].equals("0")) {
- efct.hasDuration = false;
+ efct.group.hasDuration = false;
efct.duration = 0.0;
} else {
- efct.hasDuration = true;
+ efct.group.hasDuration = true;
if (splits[1].equalsIgnoreCase("instant")) {
efct.duration = -1;
@@ -487,19 +294,19 @@ public class Effect {
} else if (ln.contains("LEVEL:")) {
efct.level = Integer.parseInt(splits[1]);
} else if (ln.contains("EXCLUSIVE")) {
- efct.exclusive = Boolean.parseBoolean(splits[1]);
+ efct.group.exclusive = Boolean.parseBoolean(splits[1]);
} else if (ln.contains("GRAPHOVERRIDE")) {
- efct.graphOverride = splits[1];
+ efct.group.graphOverride = splits[1];
} else if (ln.contains("USEOWNERLEVEL")) {
- efct.ownerLevel = Boolean.parseBoolean(splits[1]);
+ efct.group.ownerLevel = Boolean.parseBoolean(splits[1]);
} else if (ln.contains("NOGRAPH")) {
- efct.useGraph = Boolean.parseBoolean(splits[1]);
+ efct.group.useGraph = Boolean.parseBoolean(splits[1]);
} else if (ln.contains("STATNAME")) {
- efct.statName = splits[1];
+ efct.group.statName = splits[1];
} else if (ln.contains("STATPERCENT")) {
efct.statPercent = Double.parseDouble(splits[1]);
} else if (ln.contains("STATMODIFIERISBONUS")) {
- efct.isStatBonus = Boolean.parseBoolean(splits[1]);
+ efct.group.isStatBonus = Boolean.parseBoolean(splits[1]);
} else if (ln.contains("RESISTANCE:")) {
efct.resist = Double.parseDouble(splits[1]);
} else if (ln.contains("FORCE:")) {
@@ -525,13 +332,11 @@ public class Effect {
String fmt = "\t\tProcessed effect %s from %s in %d nanoseconds (%.2f seconds)\n";
double seconds = (((endTime - startTime) / 1000000000));
- errs.add(String.format(fmt, efct.name, scnSource, endTime - startTime, seconds));
+ errs.add(String.format(fmt, efct.group.name, scnSource, endTime - startTime, seconds));
}
effectCount += 1;
return efct;
}
-
-
}
diff --git a/src/main/java/tlIItools/EffectGroup.java b/src/main/java/tlIItools/EffectGroup.java
new file mode 100644
index 00000000..9f4ad49b
--- /dev/null
+++ b/src/main/java/tlIItools/EffectGroup.java
@@ -0,0 +1,120 @@
+package tlIItools;
+
+import java.util.*;
+
+/** An 'effect group'.
+ *
+ * This groups similar effects together, the same way affix groups group affixs
+ * together.
+ *
+ * @author Ben Culkin */
+public class EffectGroup {
+ /** The name of the effect. */
+ public String name;
+ /** The specific effect that happens. */
+ public String type;
+ /** Damage type for the effect, if applicable. */
+ public String damageType = "physical";
+ /** Whether or not we have a duration or not. */
+ public boolean hasDuration;
+ /** The name of the stat that applies to this affect. */
+ public String statName;
+ /** Whether or not this stat is a bonus value. */
+ public boolean isStatBonus;
+ /** Whether or not this uses the owners level to modify any applicable graph. */
+ public boolean ownerLevel;
+ /** Whether or not a graph is used for this effect. */
+ public boolean useGraph = true;
+ /** The graph to use instead of the default graph. */
+ public String graphOverride;
+ /** Whether this effect can stack with itself. */
+ public boolean exclusive;
+ /** Whether or not this effect is a 'transfer' effect (Applied to the enemy on a hit). */
+ public boolean isTransfer;
+
+ public String summary() {
+ StringBuilder sb = new StringBuilder();
+
+ if (isTransfer) sb.append("Transfer: ");
+ sb.append(type);
+ if (name != null && !name.equals("")) {
+ sb.append(" (named ");
+ sb.append(name);
+ sb.append(")");
+ }
+
+ if (hasDuration) sb.append(" (timed)");
+ if (statName != null && !name.equals("")) {
+ sb.append(" (uses stat ");
+ sb.append(statName);
+ if (isStatBonus) sb.append(" as a bonus");
+ sb.append(")");
+ }
+
+ if (ownerLevel) sb.append(" (uses owner level)");
+ if (useGraph) {
+ sb.append(" (uses ");
+ if (graphOverride != null && !graphOverride.equals("")) sb.append(graphOverride);
+ else sb.append("default");
+ sb.append(" graph)");
+ }
+
+ if (exclusive) sb.append(" (exclusive)");
+
+ // @TODO Ben Culkin 12/31/2020 :FancyEffectSummary
+ //
+ // EffectGroups should probably use something from EffectRepo to better
+ // output the summary for a particular group type
+ // String fmt = "%s (name %s, damageType %s, hasDuration %s, stat %s, isBonus %s, ownerLevel %s, useGraph %s, graphOverride %s, exclusive %s, isTransfer %s)";
+
+ // sb.append(String.format(fmt, type, name, damageType, hasDuration, statName, isStatBonus, ownerLevel, useGraph, graphOverride, exclusive, isTransfer));
+
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(name);
+ sb.append(type);
+ sb.append(damageType);
+ sb.append(hasDuration);
+ sb.append(statName);
+ sb.append(isStatBonus);
+ sb.append(ownerLevel);
+ sb.append(graphOverride);
+ sb.append(useGraph);
+ sb.append(exclusive);
+ sb.append(isTransfer);
+
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(damageType, exclusive, graphOverride, hasDuration,
+ isStatBonus, isTransfer, name, ownerLevel, statName, type, useGraph);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ EffectGroup other = (EffectGroup) obj;
+
+ return Objects.equals(damageType, other.damageType)
+ && Objects.equals(graphOverride, other.graphOverride)
+ && Objects.equals(name, other.name)
+ && Objects.equals(statName, other.statName)
+ && Objects.equals(type, other.type)
+ && exclusive == other.exclusive
+ && hasDuration == other.hasDuration
+ && isStatBonus == other.isStatBonus
+ && isTransfer == other.isTransfer
+ && ownerLevel == other.ownerLevel
+ && useGraph == other.useGraph;
+ }
+}
diff --git a/src/main/java/tlIItools/EffectRepo.java b/src/main/java/tlIItools/EffectRepo.java
index f2f0d076..05151a48 100644
--- a/src/main/java/tlIItools/EffectRepo.java
+++ b/src/main/java/tlIItools/EffectRepo.java
@@ -1,40 +1,26 @@
package tlIItools;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
+import java.io.*;
+import java.util.*;
import java.util.Map.Entry;
-/**
- * Repository class for storing information needed for parsing/outputing
+/** Repository class for storing information needed for parsing/outputing
* effects.
*
- * @author Ben Culkin
- *
- */
+ * @author Ben Culkin */
public class EffectRepo {
// NOTE: consider making these use function accessors in the future?
// --bculkin, 6/24/20
- /**
- * The list of detail strings for skills.
- */
+
+ /** The list of detail strings for skills. */
public static Map<String, String> detals;
- /**
- * The list of detail strings for timed skills.
- */
+ /** The list of detail strings for timed skills. */
public static Map<String, String> timeDetals;
- /**
- * The list of replacements for detail strings.
- */
+ /** The list of replacements for detail strings. */
public static List<ReplPair> replList;
- /*
- * Init. lists from files.
- */
+ /* Init. lists from files. */
static {
try (FileReader detalReader = new FileReader("data/affix-detals.txt")) {
detals = readDetails(new Scanner(detalReader));
@@ -42,8 +28,7 @@ public class EffectRepo {
AffixLister.errOut.println("Error loading affix detail text");
}
- try (FileReader timedDetalReader
- = new FileReader("data/timed-affix-detals.txt")) {
+ try (FileReader timedDetalReader = new FileReader("data/timed-affix-detals.txt")) {
timeDetals = readDetails(new Scanner(timedDetalReader));
} catch (IOException ioex) {
AffixLister.errOut.println("Error loading timed affix detail text");
@@ -56,36 +41,31 @@ public class EffectRepo {
}
}
- /**
- * Read effect detail strings from an input source.
+ /** Read effect detail strings from an input source.
+ *
+ * @param scn The source to read from.
*
- * @param scn
- * The source to read from.
- * @return The map of effect details to use.
- */
+ * @return The map of effect details to use. */
public static Map<String, String> readDetails(Scanner scn) {
Map<String, String> detalMap = new HashMap<>();
return readDetails(detalMap, scn);
}
- /**
- * Read effect detail strings from an input source, adding to an existing set.
+ /** Read effect detail strings from an input source, adding to an existing set.
+ *
+ * @param detalMap The details to add to.
+ * @param scn The source to read from.
*
- * @param detalMap
- * The details to add to.
- * @param scn
- * The source to read from.
- * @return The map of effect details to use.
- */
- public static Map<String, String> readDetails(Map<String, String> detalMap,
- Scanner scn) {
+ * @return The map of effect details to use. */
+ public static Map<String, String> readDetails(
+ Map<String, String> detalMap, Scanner scn)
+ {
while (scn.hasNextLine()) {
String name = scn.nextLine().trim();
- if (name.equals(""))
- continue;
- if (name.startsWith("#"))
- continue;
+
+ if (name.equals("")) continue;
+ if (name.startsWith("#")) continue;
String body;
do {
@@ -98,18 +78,17 @@ public class EffectRepo {
return detalMap;
}
- /**
- * Sanity check the loaded format strings.
- */
+ /** Sanity check the loaded format strings. */
public static void sanityCheckFormats() {
for (Entry<String, String> detal : detals.entrySet()) {
String fmt = detal.getValue();
- AffixLister.errOut.printf("\tTRACE: Applying replacements for %s\n",
- detal.getKey());
+ AffixLister.errOut.printf("\tTRACE: Applying replacements for %s\n", detal.getKey());
+
for (ReplPair repl : replList) {
String tmp = fmt;
fmt = fmt.replaceAll(repl.find, repl.replace);
+
if (!fmt.equals(tmp)) {
String outFmt = "\t\tTRACE: Replaced %s with %s: \n\t\t%s\n\t\t%s\n";
@@ -118,8 +97,7 @@ public class EffectRepo {
}
if (fmt.contains("<") || fmt.contains(">")) {
- String warnFmt
- = "WARN: Details for effect %s are malformated (contains < or >):\n\t%s\n";
+ String warnFmt = "WARN: Details for effect %s are malformated (contains < or >):\n\t%s\n";
AffixLister.errOut.printf(warnFmt, detal.getKey(), fmt);
}
@@ -133,8 +111,7 @@ public class EffectRepo {
}
if (fmt.contains("<") || fmt.contains(">")) {
- String warnFmt
- = "WARN: Details for timed effect %s are malformatted (contains < or >):\n\t%s\n";
+ String warnFmt = "WARN: Details for timed effect %s are malformatted (contains < or >):\n\t%s\n";
AffixLister.errOut.printf(warnFmt, detal.getKey(), fmt);
}
}
diff --git a/src/main/java/tlIItools/LevelRange.java b/src/main/java/tlIItools/LevelRange.java
new file mode 100644
index 00000000..b0d620bf
--- /dev/null
+++ b/src/main/java/tlIItools/LevelRange.java
@@ -0,0 +1,135 @@
+package tlIItools;
+
+import java.util.*;
+
+public class LevelRange implements Comparable<LevelRange> {
+ /*
+ * if (minLevel <= 1 && maxLevel == 999) {
+ * sb.append("No Level Range");
+ * } else if (minLevel != 1 && maxLevel != 999) {
+ * sb.append("Level Range: ");
+ * sb.append(minLevel);
+ * sb.append("-");
+ * sb.append(maxLevel);
+ * } else if (minLevel <= 1) {
+ * sb.append("Max Level: ");
+ * sb.append(maxLevel);
+ * } else if (maxLevel == 999) {
+ * sb.append("Minimum Level: ");
+ * sb.append(minLevel);
+ * } */
+
+ public int minLevel;
+ public int maxLevel;
+
+ public LevelRange() {
+ minLevel = 1;
+ maxLevel = 999;
+ }
+
+ public LevelRange(int minLevel, int maxLevel) {
+ this.minLevel = minLevel;
+ this.maxLevel = maxLevel;
+ }
+
+ private void clamp() {
+ minLevel = Math.max(1, minLevel);
+ maxLevel = Math.min(999, maxLevel);
+ }
+
+ public boolean isUnrestricted() {
+ return minLevel <= 1 && maxLevel >= 999;
+ }
+
+ public boolean noLowerBound() {
+ return minLevel <= 1;
+ }
+
+ public boolean noUpperBound() {
+ return maxLevel >= 999;
+ }
+
+ @Override
+ public String toString() {
+ clamp();
+
+ StringBuilder sb = new StringBuilder();
+
+ if (minLevel <= 1 && maxLevel >= 999) {
+ sb.append("No Level Range");
+ } else if (minLevel > 1 && maxLevel < 999) {
+ sb.append("Level Range: ");
+ sb.append(minLevel);
+ sb.append("-");
+ sb.append(maxLevel);
+ } else if (minLevel <= 1) {
+ sb.append("Max Level: ");
+ sb.append(maxLevel);
+ } else if (maxLevel >= 999) {
+ sb.append("Minimum Level: ");
+ sb.append(minLevel);
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ clamp();
+
+ return Objects.hash(maxLevel, minLevel);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+
+ LevelRange other = (LevelRange) obj;
+
+ clamp();
+ other.clamp();
+
+ if (isUnrestricted() && other.isUnrestricted()) {
+ return true;
+ } else if (noLowerBound()) {
+ if (other.noLowerBound()) return maxLevel == other.maxLevel;
+ else return false;
+ } else if (noUpperBound()) {
+ if (other.noUpperBound()) return minLevel == other.minLevel;
+ else return false;
+ } else {
+ return maxLevel == other.maxLevel && minLevel == other.minLevel;
+ }
+ }
+
+ @Override
+ public int compareTo(LevelRange other) {
+ clamp();
+ other.clamp();
+
+ if (this.equals(other)) return 0;
+
+ // Unrestricted ranges sort above all others
+ if (isUnrestricted()) return 1;
+ if (other.isUnrestricted()) return -1;
+
+ if (noLowerBound()) {
+ if (other.noLowerBound()) {
+ return maxLevel - other.maxLevel;
+ } else {
+ return -1;
+ }
+ } else if (noUpperBound()) {
+ if (other.noUpperBound()) {
+ return minLevel - other.minLevel;
+ } else {
+ return 1;
+ }
+ } else {
+ if (minLevel == other.minLevel) return maxLevel - other.maxLevel;
+ else return minLevel - other.minLevel;
+ }
+ }
+}
diff --git a/src/main/java/tlIItools/Main.java b/src/main/java/tlIItools/Main.java
index 234f0bd3..a4d09bff 100644
--- a/src/main/java/tlIItools/Main.java
+++ b/src/main/java/tlIItools/Main.java
@@ -1,6 +1,12 @@
package tlIItools;
+/** Main method for TLIITools
+ *
+ * @author Ben Culkin */
public class Main {
+ /** Main method.
+ *
+ * @param args Unused CLI args. */
public static void main(String[] args) {
// TODO implement this
}
diff --git a/src/main/java/tlIItools/NameFileReader.java b/src/main/java/tlIItools/NameFileReader.java
index 87407162..a9646942 100644
--- a/src/main/java/tlIItools/NameFileReader.java
+++ b/src/main/java/tlIItools/NameFileReader.java
@@ -1,113 +1,71 @@
package tlIItools;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.io.Reader;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-
-/**
- * Reads in a list of file names to process.
+import java.io.*;
+
+import java.util.*;
+
+/** Reads in a list of file names to process.
*
- * @author Ben Culkin
- */
+ * @author Ben Culkin */
public class NameFileReader {
- /**
- * Are we attempting to guess group names?
- */
+ /** Are we attempting to guess group names? */
public boolean guessGroups;
-
- /**
- * Regex to use for guessing group names.
- */
+ /** Regex to use for guessing group names. */
public String groupRx;
-
- /**
- * The default group to put files into.
- */
+ /** The default group to put files into. */
public String defGroup;
-
- /**
- * The map of file groups.
- */
+ /** The map of file groups. */
public Map<String, List<String>> fNames;
- /*
- * The current group.
- */
+ /* The current group. */
private String curGroup;
- /*
- * The list of files for the current group.
- */
+ /* The list of files for the current group. */
private List<String> curList;
- /**
- * Counts the files read in.
- */
+ /** Counts the files read in. */
public int fCount;
+ /** Stream to write normal output to. */
public PrintStream normOut = System.out;
+ /** Stream to write error output to. */
public PrintStream errOut = System.err;
- /**
- * Create a new name reader using the default settings.
+ /** Create a new name reader using the default settings.
*
- * Guessing groups is disabled by default.
- */
+ * Guessing groups is disabled by default. */
public NameFileReader() {
this(false);
}
- /**
- * Create a new name reader using the default settings.
+ /** Create a new name reader using the default settings.
*
- * @param guessGroups
- * Controls whether or not to try to guess file groups from file
- * names.
- */
+ * @param guessGroups Controls whether or not to try to guess file groups from file names. */
public NameFileReader(boolean guessGroups) {
this(new HashMap<>(), "default", guessGroups);
}
- /**
- * Create a new name reader using the specified settings.
- *
- * @param fNames
- * The set of groups to add files to.
+ /** Create a new name reader using the specified settings.
*
- * @param defGroup
- * The name of the default file group.
- *
- * @param guessGroups
- * Whether or not to attempt to guess group names.
- */
+ * @param fNames The set of groups to add files to.
+ * @param defGroup The name of the default file group.
+ * @param guessGroups Whether or not to attempt to guess group names. */
public NameFileReader(Map<String, List<String>> fNames, String defGroup, boolean guessGroups) {
- if (!fNames.containsKey(defGroup))
- fNames.put(defGroup, new ArrayList<>());
+ if (!fNames.containsKey(defGroup)) fNames.put(defGroup, new ArrayList<>());
this.fNames = fNames;
- this.defGroup = defGroup;
-
+ this.defGroup = defGroup;
this.guessGroups = guessGroups;
+ this.curGroup = defGroup;
- this.curGroup = defGroup;
- this.curList = fNames.get(curGroup);
+ this.curList = fNames.get(curGroup);
this.fCount = 0;
}
- /**
- * Read in file names from a file.
+ /** Read in file names from a file.
*
- * @param from
- * The name of the file to read from.
- */
+ * @param from The name of the file to read from. */
public void readFrom(String from) {
try (FileReader fr = new FileReader(from)) {
readFrom(fr);
@@ -118,13 +76,9 @@ public class NameFileReader {
}
}
- /**
- * Read in file names from an input source.
- *
- * @param r
- * The input source to read from.
+ /** Read in file names from an input source.
*
- */
+ * @param r The input source to read from. */
public void readFrom(Reader r) {
Scanner scn = new Scanner(r);
@@ -153,12 +107,9 @@ public class NameFileReader {
scn.close();
}
- /**
- * Swap to a new file group.
+ /** Swap to a new file group.
*
- * @param groupName
- * The name of the group to swap to.
- */
+ * @param groupName The name of the group to swap to. */
public void swapGroup(String groupName) {
curGroup = groupName;
@@ -171,12 +122,19 @@ public class NameFileReader {
}
}
+ /** Add a file to this file reader.
+ *
+ * @param fName The file to add. */
public void addFile(String fName) {
curList.add(fName);
fCount += 1;
}
-
+
+ /** Add a file to this file reader.
+ *
+ * @param groupName The group to add the file to.
+ * @param fName The file to add. */
public void addFile(String groupName, String fName) {
fNames.computeIfAbsent(groupName, (key) -> new ArrayList<>()).add(fName);
diff --git a/src/main/java/tlIItools/ReplPair.java b/src/main/java/tlIItools/ReplPair.java
index 74012716..90717a0a 100644
--- a/src/main/java/tlIItools/ReplPair.java
+++ b/src/main/java/tlIItools/ReplPair.java
@@ -1,51 +1,31 @@
package tlIItools;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Scanner;
+import java.util.*;
-/**
- * String pairs for replacements.
+/** String pairs for replacements.
*
- * @author Ben Culkin
- */
+ * @author Ben Culkin */
public class ReplPair implements Comparable<ReplPair> {
- /**
- * Represents an error encountered parsing ReplPairs
+ /** Represents an error encountered parsing ReplPairs
*
- * @author Ben Culkin
- */
+ * @author Ben Culkin */
public static class ReplError {
- /**
- * The line the error occured on.
- */
+ /** The line the error occured on. */
public int line;
- /**
- * The number of pairs we have processed so far.
- */
+ /** The number of pairs we have processed so far. */
public int numPairs;
- /**
- * The text of the line we errored on.
- */
+ /** The text of the line we errored on. */
public String txt;
- /**
- * The message of the error.
- */
+ /** The message of the error. */
public String msg;
- /**
- * Create a new ReplPair parse error.
+ /** Create a new ReplPair parse error.
*
- * @param lne
- * The line the error occured on.
- * @param nPairs
- * The number of pairs processed up to this point.
- * @param msg
- * The message detailing the error.
- * @param txt
- * The text that caused the error.
- */
+ * @param lne The line the error occured on.
+ * @param nPairs The number of pairs processed up to this point.
+ * @param msg The message detailing the error.
+ * @param txt The text that caused the error. */
public ReplError(int lne, int nPairs, String msg, String txt) {
line = lne;
numPairs = nPairs;
@@ -65,86 +45,56 @@ public class ReplPair implements Comparable<ReplPair> {
}
}
- /**
- * The priority for this replacement.
- */
+ /** The priority for this replacement. */
public int priority;
-
- /**
- * The name of this replacement.
+ /** The name of this replacement.
*
- * Defaults to the 'find' string.
- */
+ * Defaults to the 'find' string. */
public String name;
- /**
- * The string to look for.
- */
+ /** The string to look for. */
public String find;
-
- /**
- * The string to replace it with.
- */
+ /** The string to replace it with. */
public String replace;
- /**
- * Create a new blank replacement pair.
- */
+ /** Create a new blank replacement pair. */
public ReplPair() {
this("", "", 1, null);
}
- /**
- * Create a new replacement pair with a priority of 1.
+ /** Create a new replacement pair with a priority of 1.
*
- * @param f
- * The string to find.
- * @param r
- * The string to replace.
+ * @param f The string to find.
+ * @param r The string to replace.
*/
public ReplPair(String f, String r) {
this(f, r, 1);
}
- /**
- * Create a new named replacement pair with a priority of 1.
+ /** Create a new named replacement pair with a priority of 1.
*
- * @param f
- * The string to find.
- * @param r
- * The string to replace.
- * @param n
- * The name of the replacement pair.
+ * @param f The string to find.
+ * @param r The string to replace.
+ * @param n The name of the replacement pair.
*/
public ReplPair(String f, String r, String n) {
this(f, r, 1, n);
}
- /**
- * Create a new replacement pair with a set priority.
+ /** Create a new replacement pair with a set priority.
*
- * @param f
- * The string to find.
- * @param r
- * The string to replace.
- * @param p
- * The priority for the replacement.
- */
+ * @param f The string to find.
+ * @param r The string to replace.
+ * @param p The priority for the replacement. */
public ReplPair(String f, String r, int p) {
this(f, r, p, f);
}
- /**
- * Create a new replacement pair with a set priority and name.
+ /** Create a new replacement pair with a set priority and name.
*
- * @param f
- * The string to find.
- * @param r
- * The string to replace.
- * @param n
- * The name of the replacement pair.
- * @param p
- * The priority for the replacement.
- */
+ * @param f The string to find.
+ * @param r The string to replace.
+ * @param n The name of the replacement pair.
+ * @param p The priority for the replacement. */
public ReplPair(String f, String r, int p, String n) {
find = f;
replace = r;
@@ -153,29 +103,23 @@ public class ReplPair implements Comparable<ReplPair> {
priority = p;
}
- /**
- * Read a list of replacement pairs from an input source.
+
+ /** Read a list of replacement pairs from an input source.
*
- * @param scn
- * The source to read the replacements from.
- * @return
- * The list of replacements.
- */
+ * @param scn The source to read the replacements from.
+ *
+ * @return The list of replacements. */
public static List<ReplPair> readList(Scanner scn) {
return ReplPair.readList(new ArrayList<>(), scn);
}
- /**
- * Read a list of replacement pairs from an input source, adding them to
+ /** Read a list of replacement pairs from an input source, adding them to
* an existing list.
*
- * @param detals
- * The list to add the replacements to.
- * @param scn
- * The source to read the replacements from.
- * @return
- * The list of replacements.
- */
+ * @param detals The list to add the replacements to.
+ * @param scn The source to read the replacements from.
+ *
+ * @return The list of replacements. */
public static List<ReplPair> readList(List<ReplPair> detals, Scanner scn) {
List<ReplError> errList = new ArrayList<>();
@@ -186,25 +130,22 @@ public class ReplPair implements Comparable<ReplPair> {
if (errList.size() == 0) errString = "An error";
else errString = "Errors";
- throw new IllegalArgumentException(String.format("%s occured parsing replacement pairs:\n%s", errString, errList));
+ throw new IllegalArgumentException(String.format(
+ "%s occured parsing replacement pairs:\n%s",
+ errString, errList));
}
return rplPar;
}
- /**
- * Read a list of replacement pairs from an input source, adding them to
+ /** Read a list of replacement pairs from an input source, adding them to
* an existing list.
*
- * @param detals
- * The list to add the replacements to.
- * @param scn
- * The source to read the replacements from.
- * @param errs
- * The list to stick errors in.
- * @return
- * The list of replacements.
- */
+ * @param detals The list to add the replacements to.
+ * @param scn The source to read the replacements from.
+ * @param errs The list to stick errors in.
+ *
+ * @return The list of replacements. */
public static List<ReplPair> readList(List<ReplPair> detals, Scanner scn, List<ReplError> errs) {
int lno = 0;
int pno = 0;
@@ -236,8 +177,7 @@ public class ReplPair implements Comparable<ReplPair> {
// Read in the next uncommented line
do {
if (!scn.hasNextLine()) {
- String msg =
- "Ran out of input looking for replacement body for raw name " + name;
+ String msg = "Ran out of input looking for replacement body for raw name " + name;
errs.add(new ReplError(lno, pno, msg, null));
break;
@@ -247,7 +187,6 @@ public class ReplPair implements Comparable<ReplPair> {
lno += 1;
} while (body.startsWith("#"));
-
rp.replace = body;
resList.add(rp);