diff --git a/README.md b/README.md index 84b77b9a..4a8cfa89 100644 --- a/README.md +++ b/README.md @@ -343,6 +343,12 @@ effects, you only have to specify the thresholds that you want to apply.
Config Examples +```yaml +# Take only mythic uniques +Uniques: + - mythic: true +``` + ```yaml # Take all uniques with item power > 800 Uniques: diff --git a/assets/lang/enUS/affixes.json b/assets/lang/enUS/affixes.json index 1c5e92d4..fdedba3e 100644 --- a/assets/lang/enUS/affixes.json +++ b/assets/lang/enUS/affixes.json @@ -30,6 +30,7 @@ "bone_critical_strike_chance": "bone critical strike chance", "bone_critical_strike_damage": "bone critical strike damage", "bone_damage": "bone damage", + "bone_prison_cooldown_reduction": "bone prison cooldown reduction", "bone_spirit_cooldown_reduction": "bone spirit cooldown reduction", "bone_spirit_damage": "bone spirit damage", "bone_spirit_explosion_size": "bone spirit explosion size", @@ -45,6 +46,7 @@ "casting_macabre_skills_restores_primary_resource": "casting macabre skills restores primary resource", "casting_ultimate_skills_restores_primary_resource": "casting ultimate skills restores primary resource", "casting_wrath_skills_restores_primary_resource": "casting wrath skills restores primary resource", + "cataclysm_cooldown_reduction": "cataclysm cooldown reduction", "cataclysm_damage": "cataclysm damage", "challenging_shout_cooldown_reduction": "challenging shout cooldown reduction", "chance_for_ball_lightning_projectiles_to_cast_twice": "chance for ball lightning projectiles to cast twice", @@ -89,6 +91,7 @@ "concealment_duration": "concealment duration", "conjuration_damage": "conjuration damage", "cooldown_reduction": "cooldown reduction", + "core_attack_speed": "core attack speed", "core_damage": "core damage", "corpse_attack_speed": "corpse attack speed", "corpse_explosion_damage": "corpse explosion damage", @@ -99,6 +102,8 @@ "crackling_energy_damage": "crackling energy damage", "critical_strike_chance": "critical strike chance", "critical_strike_chance_against_close_enemies": "critical strike chance against close enemies", + "critical_strike_chance_against_crowd_controlled_enemies": "critical strike chance against crowd controlled enemies", + "critical_strike_chance_against_enemies": "critical strike chance against enemies", "critical_strike_chance_against_injured_enemies": "critical strike chance against injured enemies", "critical_strike_damage": "critical strike damage", "crowd_control_duration": "crowd control duration", @@ -118,6 +123,7 @@ "damage_over_time": "damage over time", "damage_per_combo_point_spent": "damage per combo point spent", "damage_reduction": "damage reduction", + "damage_reduction_for_your_minions": "damage reduction for your minions", "damage_reduction_from_bleeding_enemies": "damage reduction from bleeding enemies", "damage_reduction_from_burning_enemies": "damage reduction from burning enemies", "damage_reduction_from_close_enemies": "damage reduction from close enemies", @@ -183,6 +189,7 @@ "dust_devil_damage": "dust devil damage", "dust_devil_duration": "dust devil duration", "dust_devil_size": "dust devil size", + "earth_attack_speed": "earth attack speed", "earth_critical_strike_chance": "earth critical strike chance", "earth_critical_strike_damage": "earth critical strike damage", "earth_damage": "earth damage", @@ -225,6 +232,7 @@ "golem_active_cooldown_reduction": "golem active cooldown reduction", "golems_damage": "golems damage", "golems_inherit_of_your_thorns": "golems inherit of your thorns", + "grizzly_rage_cooldown_reduction": "grizzly rage cooldown reduction", "grizzly_rage_duration": "grizzly rage duration", "ground_stomp_cooldown_reduction": "ground stomp cooldown reduction", "ground_stomp_damage": "ground stomp damage", @@ -238,9 +246,12 @@ "hurricane_duration": "hurricane duration", "hurricane_size": "hurricane size", "hydra_damage": "hydra damage", + "hydra_lucky_hit_chance": "hydra lucky hit chance", "hydra_resource_cost_reduction": "hydra resource cost reduction", + "ice_armor_duration": "ice armor duration", "ice_blades_cooldown_reduction": "ice blades cooldown reduction", "ice_blades_damage": "ice blades damage", + "ice_blades_lucky_hit_chance": "ice blades lucky hit chance", "ice_spike_damage": "ice spike damage", "ignores_durability_loss": "ignores durability loss", "imbued_critical_strike_damage": "imbued critical strike damage", @@ -262,6 +273,7 @@ "kick_cooldown_reduction": "kick cooldown reduction", "kick_damage": "kick damage", "kick_vulnerable_duration": "kick vulnerable duration", + "lacerate_cooldown_reduction": "lacerate cooldown reduction", "lacerate_damage": "lacerate damage", "lacerate_duration": "lacerate duration", "leap_cooldown_reduction": "leap cooldown reduction", @@ -277,6 +289,7 @@ "lightning_resistance": "lightning resistance", "lightning_spear_cooldown_reduction": "lightning spear cooldown reduction", "lightning_spear_damage": "lightning spear damage", + "lightning_spear_lucky_hit_chance": "lightning spear lucky hit chance", "lightning_storm_duration": "lightning storm duration", "lucky_hit_chance": "lucky hit chance", "lucky_hit_chance_while_you_have_a_barrier": "lucky hit chance while you have a barrier", @@ -320,6 +333,7 @@ "maximum_life": "maximum life", "maximum_mana": "maximum mana", "maximum_minion_life": "maximum minion life", + "maximum_resistance": "maximum resistance", "maximum_resistance_to_all_elements": "maximum resistance to all elements", "maximum_resource": "maximum resource", "maximum_spirit": "maximum spirit", @@ -338,6 +352,7 @@ "overpower_chance": "overpower chance", "overpower_damage": "overpower damage", "overpower_damage_with_twohanded_bludgeoning_weapons": "overpower damage with twohanded bludgeoning weapons", + "petrify_cooldown_reduction": "petrify cooldown reduction", "petrify_duration": "petrify duration", "physical_critical_strike_chance_against_elites": "physical critical strike chance against elites", "physical_damage": "physical damage", @@ -413,7 +428,9 @@ "skeleton_priest_effect_duration": "skeleton priest effect duration", "slow_duration_reduction": "slow duration reduction", "smoke_grenade_cooldown_reduction": "smoke grenade cooldown reduction", + "smoke_grenade_damage": "smoke grenade damage", "smoke_grenade_duration": "smoke grenade duration", + "smoke_grenade_size": "smoke grenade size", "spirit_cost_reduction": "spirit cost reduction", "spirit_on_kill": "spirit on kill", "spirit_per_second": "spirit per second", @@ -437,12 +454,12 @@ "thorns_while_fortified": "thorns while fortified", "to_abundance": "to abundance", "to_adrenaline_rush": "to adrenaline rush", + "to_aftermath": "to aftermath", "to_aggressive_resistance": "to aggressive resistance", "to_agile": "to agile", "to_agility_skills": "to agility skills", "to_alchemical_advantage": "to alchemical advantage", "to_alchemists_fortune": "to alchemists fortune", - "to_all_skills": "to all skills", "to_amplify_damage": "to amplify damage", "to_arc_lash": "to arc lash", "to_ball_lightning": "to ball lightning", @@ -457,6 +474,7 @@ "to_blood_mist": "to blood mist", "to_blood_surge": "to blood surge", "to_bone_prison": "to bone prison", + "to_bone_skills": "to bone skills", "to_bone_spear": "to bone spear", "to_bone_spirit": "to bone spirit", "to_bone_splinters": "to bone splinters", @@ -480,16 +498,18 @@ "to_compound_fracture": "to compound fracture", "to_concealment": "to concealment", "to_concussion": "to concussion", - "to_concussive": "to concussive", + "to_conduction": "to conduction", "to_conjuration_mastery": "to conjuration mastery", "to_conjuration_skills": "to conjuration skills", "to_consuming_shadows": "to consuming shadows", + "to_convulsions": "to convulsions", "to_core_skills": "to core skills", "to_corpse_explosion": "to corpse explosion", "to_corpse_skills": "to corpse skills", "to_corpse_tendrils": "to corpse tendrils", "to_counteroffensive": "to counteroffensive", "to_crippling_darkness": "to crippling darkness", + "to_crippling_flames": "to crippling flames", "to_crushing_earth": "to crushing earth", "to_curse_skills": "to curse skills", "to_cut_to_the_bone": "to cut to the bone", @@ -518,6 +538,7 @@ "to_elemental_dominance": "to elemental dominance", "to_endless_fury": "to endless fury", "to_endless_pyre": "to endless pyre", + "to_endless_tempest": "to endless tempest", "to_envenom": "to envenom", "to_evulsion": "to evulsion", "to_exploit": "to exploit", @@ -625,6 +646,7 @@ "to_skeletal_warrior_mastery": "to skeletal warrior mastery", "to_slaying_strike": "to slaying strike", "to_smoke_grenade": "to smoke grenade", + "to_snap_freeze": "to snap freeze", "to_spark": "to spark", "to_spiked_armor": "to spiked armor", "to_stand_alone": "to stand alone", @@ -644,6 +666,7 @@ "to_trap_mastery": "to trap mastery", "to_trick_attacks": "to trick attacks", "to_twisting_blades": "to twisting blades", + "to_unstable_elixirs": "to unstable elixirs", "to_upheaval": "to upheaval", "to_wallop": "to wallop", "to_war_cry": "to war cry", diff --git a/assets/lang/enUS/sigils.json b/assets/lang/enUS/sigils.json index 04c882b0..e2dc7298 100644 --- a/assets/lang/enUS/sigils.json +++ b/assets/lang/enUS/sigils.json @@ -187,6 +187,7 @@ "fire_damage": "fire damage you deal more fire damage.", "frost_damage": "frost damage you deal more frost damage.", "gold_find": "gold find you find more gold.", + "hell_touched": "hell touched extra infernal shrines and more glyph experience earned at the end of this dungeon.", "increased_critical_strike": "increased critical strike your critical strike chance is increased by .", "increased_healing": "increased healing your healing received is increased by .", "lightning_caller": "lightning caller you occasionally call down lightning strikes that damage nearby enemies.", diff --git a/assets/lang/enUS/uniques.json b/assets/lang/enUS/uniques.json index b0f9711b..eb07cb6f 100644 --- a/assets/lang/enUS/uniques.json +++ b/assets/lang/enUS/uniques.json @@ -60,6 +60,14 @@ ], "snoId": 941731 }, + "axial_conduit": { + "desc": "chain lightning alternates between orbiting you and seeking up to enemies. when it returns, it drains mana from you for each active chain lightning. after draining total mana, the bolt explodes for lightning damage. chain lightning expires if you dont have enough mana for it to drain.", + "full": "{c_important}Chain Lightning{/c} alternates between orbiting you and seeking up to {c_number}[Affix.\"Static Value 2\"]{/c} enemies. When it returns, it drains {c_number}[Affix.\"Static Value 0\"]{/c} Mana from you for each active {c_important}Chain Lightning{/c}. After draining {c_number}[Affix.\"Static Value 3\"]{/c} total Mana, the bolt explodes for {c_random}[(Owner.Weapon_Damage_Min_Total + Owner.Weapon_Damage_Delta_Total) * Affix_Value_1 * Affix.\"Static Value 1\"]{/c} Lightning Damage.\r\n                       \r\n{c_important}Chain Lightning{/c} expires if you don't have enough Mana for it to drain.", + "num_idx": [ + 3 + ], + "snoId": 1934444 + }, "azurewrath": { "desc": "lucky hit your skills have up to a chance to freeze enemies for seconds and deal cold damage to them.", "full": "", @@ -92,6 +100,14 @@ ], "snoId": 1704784 }, + "björnfangs_tusks": { + "desc": "cataclysms lightning strikes now prioritize enemies. while cataclysm is active, you gain unlimited spirit and deal increased damage.", + "full": "{c_important}Cataclysm{/c}'s lightning strikes now prioritize enemies.\r\n\r\nWhile {c_important}Cataclysm{/c} is active, you gain unlimited Spirit and deal {c_random}[Affix_Value_1|%x|]{/c} increased damage.", + "num_idx": [ + 0 + ], + "snoId": 1934419 + }, "black_river": { "desc": "corpse explosion consumes up to additional corpses around the initial corpse, dealing increased damage and with a larger radius per additional corpse.", "full": "{c_important}Corpse Explosion{/c} consumes up to {c_number}[Affix.\"Static Value 0\"]{/c} additional Corpses around the initial Corpse, dealing {c_random}[Affix_Value_1|%x|]{/c} increased damage and with a {c_random}[Affix_Value_2|%|]{/c} larger radius per additional Corpse.", @@ -199,8 +215,8 @@ "snoId": 1230484 }, "dolmen_stone": { - "desc": "casting boulder while hurricane is active will cause your boulders to rotate around you.", - "full": "Casting {c_important}Boulder{/c} while {c_important}Hurricane{/c} is active will cause your boulders to rotate around you.", + "desc": "casting boulder while hurricane is active will cause your boulders to rotate around you, up to a maximum of boulders. each rotating boulder increases their damage by .", + "full": "Casting {c_important}Boulder{/c} while {c_important}Hurricane{/c} is active will cause your boulders to rotate around you, up to a maximum of 10 boulders. Each rotating boulder increases their damage by {c_number}[Affix.\"Static Value 0 * 100\"|%x|]{/c}.", "num_idx": [], "snoId": 1610440 }, @@ -211,8 +227,8 @@ "snoId": 941869 }, "eaglehorn": { - "desc": "penetrating shot makes enemies hit vulnerable for seconds. every casts of penetrating shot will fire an arrow that bounces off walls and scenery and deals more damage.", - "full": "{c_important}Penetrating Shot{/c} makes enemies hit {c_important}{u}Vulnerable{/u}{/c} for {c_number}[Affix.\"Static Value 0\"]{/c} seconds. Every {c_number}[Affix.\"Static Value 1\"]{/c} casts of {c_important}Penetrating Shot{/c} will fire an arrow that bounces off walls and scenery and deals {c_random}[Affix_Value_1|%x|]{/c} more damage.", + "desc": "penetrating shot makes enemies hit vulnerable for seconds. every cast of penetrating shot will fire an arrow that bounces off walls and scenery and deals more damage.", + "full": "{c_important}Penetrating Shot{/c} makes enemies hit {c_important}{u}Vulnerable{/u}{/c} for {c_number}[Affix.\"Static Value 0\"]{/c} seconds. Every {c_number}[Affix.\"Static Value 1\"]{/c} cast of {c_important}Penetrating Shot{/c} will fire an arrow that bounces off walls and scenery and deals {c_random}[Affix_Value_1|%x|]{/c} more damage.", "num_idx": [ 2 ], @@ -220,7 +236,7 @@ }, "earthbreaker": { "desc": "casting landslide causes tectonic spikes to continue to deal damage over seconds. summoning landslide pillars in tectonic spikes has a chance to causes extra landslide pillars to spawn in the spikes.", - "full": "{icon:bullet}Casting {c_important}Landslide{/c} causes tectonic spikes to continue to deal {c_random}[(Owner.Weapon_Damage_Min_Total + Owner.Weapon_Damage_Delta_Total) * Affix_Value_1]{/c} damage over {c_number}[Affix.\"Static Value 0\"]{/c} seconds.\r\n\r\n{icon:bullet}Summoning {c_important}Landslide{/c} pillars in tectonic spikes has a {c_random}[Affix_Value_2 * 100|%|]{/c} chance to causes extra {c_important}Landslide{/c} pillars to spawn in the spikes. ", + "full": "Casting {c_important}Landslide{/c} causes tectonic spikes to continue to deal {c_random}[(Owner.Weapon_Damage_Min_Total + Owner.Weapon_Damage_Delta_Total) * Affix_Value_1]{/c} damage over {c_number}[Affix.\"Static Value 0\"]{/c} seconds.\r\n\r\nSummoning {c_important}Landslide{/c} pillars in tectonic spikes has a {c_random}[Affix_Value_2 * 100|%|]{/c} chance to causes extra {c_important}Landslide{/c} pillars to spawn in the spikes. ", "num_idx": [ 0, 2 @@ -284,8 +300,8 @@ "snoId": 1214791 }, "flameweaver": { - "desc": "casting fire bolt through your firewall causes it to split into bolts, each dealing more damage.", - "full": "Casting {c_important}Fire Bolt{/c} through your {c_important}Firewall{/c} causes it to split into {c_number}[Affix.\"Static Value 0\"]{/c} bolts, each dealing {c_random}[Affix_Value_1|%x|]{/c} more damage.", + "desc": "casting fire bolt through your firewall causes it to split into bolts, each dealing increased damage.", + "full": "Casting {c_important}Fire Bolt{/c} through your {c_important}Firewall{/c} causes it to split into {c_number}[Affix.\"Static Value 0\"]{/c} bolts, each dealing {c_random}[Affix_Value_1|%x|]{/c} increased damage.", "num_idx": [ 1 ], @@ -333,8 +349,8 @@ "snoId": 942564 }, "godslayer_crown": { - "desc": "when you stun, freeze, or immobilize an elite enemy, or damage a boss, it pulls in nearby enemies. you deal increased damage to them for seconds. this effect can only occur once every seconds.", - "full": "When you Stun, {c_important}{u}Freeze{/u}{/c}, or Immobilize an Elite enemy, or damage a Boss, it Pulls In Nearby enemies. You deal {c_random}[Affix_Value_1|%x|]{/c} increased damage to them for {c_number}[Affix.\"Static Value 0\"]{/c} seconds. This effect can only occur once every {c_number}[Affix.\"Static Value 1\"]{/c} seconds.", + "desc": "when you stun, freeze, or immobilize an elite enemy, or damage a boss, it pulls in nearby enemies. you deal increased damage to them for seconds. can only occur once every seconds.", + "full": "When you Stun, {c_important}{u}Freeze{/u}{/c}, or Immobilize an Elite enemy, or damage a Boss, it Pulls In Nearby enemies. You deal {c_random}[Affix_Value_1|%x|]{/c} increased damage to them for {c_number}[Affix.\"Static Value 0\"]{/c} seconds. Can only occur once every {c_number}[Affix.\"Static Value 1\"]{/c} seconds.", "num_idx": [ 0 ], @@ -469,6 +485,14 @@ ], "snoId": 1559327 }, + "path_of_tragoul": { + "desc": "bone prison traps a larger area and fires bone splinters at enemies trapped within. increase your maximum essence by for seconds each time these bone splinters hit an enemy.", + "full": "{c_important}Bone Prison{/c} traps a larger area and fires {c_random}[Affix_Value_1]{/c} {c_important}Bone Splinters{/c} at enemies trapped within. \r\n\r\nIncrease your Maximum Essence by {c_number}[Affix.\"Static Value 0\"]{/c} for {c_number}[Affix.\"Static Value 1\"]{/c} seconds each time these {c_important}Bone Splinters{/c} hit an enemy.", + "num_idx": [ + 0 + ], + "snoId": 1934440 + }, "penitent_greaves": { "desc": "you leave behind a trail of frost that chills enemies. you deal more damage to chilled enemies.", "full": "You leave behind a trail of frost that {c_important}{u}Chills{/u}{/c} enemies. You deal {c_random}[Affix_Value_1|%x|]{/c} more damage to {c_important}{u}Chilled{/u}{/c} enemies.", @@ -550,8 +574,8 @@ "snoId": 1706659 }, "saboteurs_signet": { - "desc": "casting flurry has a chance to release stun grenades that deal physical damage and stun enemies for second. your grenade skills have a lucky hit chance.", - "full": "Casting {c_important}Flurry{/c} has a {c_random}[Affix_Value_2|%|]{/c} chance to release {c_important}Stun Grenades{/c} that deal {c_random}[(Owner.Weapon_Damage_Min_Total + Owner.Weapon_Damage_Delta_Total) * Affix_Value_1]{/c} Physical damage and Stun enemies for {c_number}[PowerTag.Rogue_Grenades.\"Script Formula 2\"|2|]{/c} second.\r\n\r\nYour {c_important}Grenade{/c} Skills have a {c_number}[Affix.\"Static Value 0\"|%|]{/c} {c_label}Lucky Hit{/c} Chance.", + "desc": "casting a core skill has a chance to throw stun grenades that deal physical damage and stun enemies for second. your stun grenades gain lucky hit chance.", + "full": "Casting a {c_important}Core{/c} Skill has a {c_random}[Affix_Value_2|%|]{/c} chance to throw {c_important}Stun Grenades{/c} that deal {c_random}[(Owner.Weapon_Damage_Min_Total + Owner.Weapon_Damage_Delta_Total) * Affix_Value_1]{/c} Physical damage and Stun enemies for {c_number}[PowerTag.Rogue_Grenades.\"Script Formula 2\"||]{/c} second.\r\n\r\nYour {c_important}Stun Grenades{/c} gain {c_number}[Affix.\"Static Value 0\"|%|]{/c} {c_label}Lucky Hit{/c} Chance.", "num_idx": [ 0, 1 @@ -574,6 +598,15 @@ ], "snoId": 1612293 }, + "shroud_of_khanduras": { + "desc": "casting dark shroud makes you immune for seconds, but your evade cooldown is increased by seconds. evading while dark shroud is active leaves behind an explosion that deals shadow damage and pulls in enemies.", + "full": "Casting {c_important}Dark Shroud{/c} makes you {c_important}{u}Immune{/u}{/c} for {c_number}[Affix.\"Static Value 0\"]{/c} seconds, but your {c_important}Evade{/c} Cooldown is increased by {c_random}[Affix_Value_2|1|]{/c} seconds.\r\n\r\n{c_important}Evading{/c} while {c_important}Dark Shroud{/c} is active leaves behind an explosion that deals {c_random}[(Owner.Weapon_Damage_Min_Total + Owner.Weapon_Damage_Delta_Total) * Affix_Value_1]{/c} Shadow damage and Pulls In enemies.", + "num_idx": [ + 1, + 2 + ], + "snoId": 1934436 + }, "skyhunter": { "desc": "the first direct damage you deal to an enemy is a guaranteed critical strike. when you consume stacks of precision casting a skill, that skill gains increased critical strike damage and you gain energy.", "full": "The first direct damage you deal to an enemy is a guaranteed Critical Strike. When you consume stacks of {c_important}Precision{/c} casting a Skill, that Skill gains {c_random}[Affix_Value_1|%x|]{/c} increased Critical Strike Damage and you gain {c_random}[Affix_Value_1 + Affix.\"Static Value 0\"|1|]{/c} {c_important}Energy{/c}.", @@ -706,6 +739,15 @@ ], "snoId": 1901483 }, + "unbroken_chain": { + "desc": "casting steel grasp reduces iron maelstroms cooldown by seconds. enemies damaged by iron maelstrom deal less damage for seconds.", + "full": "Casting {c_important}Steel Grasp{/c} reduces {c_important}Iron Maelstrom's{/c} Cooldown by {c_random}[Affix_Value_1/3|1|]{/c} seconds. \r\n\r\nEnemies damaged by {c_important}Iron Maelstrom{/c} deal {c_random}[Affix_Value_1|%|]{/c} less damage for {c_number}[Affix_Value_2]{/c} seconds.", + "num_idx": [ + 0, + 1 + ], + "snoId": 1934407 + }, "unsung_ascetics_wraps": { "desc": "lightning storm gains additional strike each times it grows. lightning storm critical strikes cause lightning to strike twice, dealing increased damage.", "full": "{c_important}Lightning Storm{/c} gains {c_number}[Affix.\"Static Value 0\"]{/c} additional strike each times it grows.\r\n\r\n{c_important}Lightning Storm{/c} Critical Strikes cause lightning to strike twice, dealing {c_random}[Affix_Value_1|x%|]{/c} increased damage.", @@ -722,6 +764,12 @@ ], "snoId": 1306213 }, + "vox_omnium": { + "desc": "of omnipower", + "full": "of Omnipower", + "num_idx": [], + "snoId": 1944512 + }, "waxing_gibbous": { "desc": "gain stealth for seconds when killing enemies with shred. breaking stealth with an attack grants ambush which guarantees critical strikes for seconds.", "full": "Gain {c_important}{u}Stealth{/u}{/c} for {c_number}[Affix_Value_2]{/c} seconds when killing enemies with {c_important}Shred{/c}. Breaking {c_important}{u}Stealth{/u}{/c} with an attack grants Ambush which guarantees Critical Strikes for {c_random}[Affix_Value_1|1|]{/c} seconds.", @@ -739,22 +787,22 @@ "snoId": 1902138 }, "windforce": { - "desc": "lucky hit hits with this weapon have up to a chance to deal double damage and knock back the target.", - "full": "{c_label}Lucky Hit:{/c} Hits with this weapon have up to a {c_random}[Affix_Value_1|%|]{/c} chance to deal double damage and Knock Back the target.", + "desc": "barrage has a chance to knock back or knock down enemies with each hit and deal double damage.", + "full": "{c_important}Barrage{/c} has a {c_random}[Affix_Value_1|%|]{/c} chance to Knock Back or Knock Down enemies with each hit and deal double damage.", "num_idx": [ 0 ], "snoId": 941973 }, "word_of_hakan": { - "desc": "your rain of arrows is always imbued with all imbuements at once.", - "full": "Your {c_important}Rain of Arrows{/c} is always {c_important}Imbued{/c} with all {c_important}Imbuements{/c} at once.", + "desc": "your rain of arrows is always imbued with all imbuements and receives your arrow storm benefits.", + "full": "Your {c_important}Rain of Arrows{/c} is always {c_important}Imbued{/c} with all {c_important}Imbuements{/c} and receives your {c_important}Arrow Storm{/c} benefits.", "num_idx": [], "snoId": 1306258 }, "writhing_band_of_trickery": { - "desc": "casting a subterfuge skill leaves behind a decoy trap that continuously taunts and lures enemies. the decoy trap explodes after seconds dealing shadow damage. can occur every seconds.", - "full": "Casting a {c_important}Subterfuge{/c} Skill leaves behind a {c_important}Decoy Trap{/c} that continuously Taunts and lures enemies. The {c_important}Decoy Trap{/c} explodes after {c_number}[Affix.\"Static Value 1\"]{/c} seconds dealing {c_random}[Affix_Flat_Value_1]{/c} Shadow damage. Can occur every {c_number}[Affix.\"Static Value 0\"]{/c} seconds.", + "desc": "casting a subterfuge skill leaves behind a decoy trap that continuously taunts and lures enemies. the decoy trap explodes after seconds dealing shadow damage. can only occur once every seconds.", + "full": "Casting a {c_important}Subterfuge{/c} Skill leaves behind a {c_important}Decoy Trap{/c} that continuously Taunts and lures enemies. The {c_important}Decoy Trap{/c} explodes after {c_number}[Affix.\"Static Value 1\"]{/c} seconds dealing {c_random}[Affix_Flat_Value_1]{/c} Shadow damage. Can only occur once every {c_number}[Affix.\"Static Value 0\"]{/c} seconds.", "num_idx": [ 1 ], @@ -769,8 +817,8 @@ "snoId": 1559923 }, "yens_blessing": { - "desc": "casting a skill has a chance to cast a nonmobility, nonultimate skill that is currently on cooldown. this effect can only occur once every seconds.", - "full": "Casting a Skill has a {c_random}[Affix_Value_1|%|]{/c} chance to cast a Non-{c_important}Mobility{/c}, Non-{c_important}Ultimate{/c} Skill that is currently on Cooldown. This effect can only occur once every {c_number}[Affix.\"Static Value 0\"]{/c} seconds.", + "desc": "casting a skill has a chance to cast a nonmobility, nonultimate skill that is currently on cooldown. can only occur once every seconds.", + "full": "Casting a Skill has a {c_random}[Affix_Value_1|%|]{/c} chance to cast a Non-{c_important}Mobility{/c}, Non-{c_important}Ultimate{/c} Skill that is currently on Cooldown. Can only occur once every {c_number}[Affix.\"Static Value 0\"]{/c} seconds.", "num_idx": [ 0 ], diff --git a/pyproject.toml b/pyproject.toml index 700af153..796fb1e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ skip-magic-trailing-comma = false select = [ "A", "ASYNC", + "ASYNC1", "B", "C4", "DTZ", @@ -38,7 +39,6 @@ select = [ "T10", "TCH", "TID", - "TRIO", "UP", "W", "YTT", diff --git a/src/__init__.py b/src/__init__.py index e19ac198..2d3fb737 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -2,4 +2,4 @@ TP = concurrent.futures.ThreadPoolExecutor() -__version__ = "5.6.4" +__version__ = "5.7.0beta1" diff --git a/src/config/models.py b/src/config/models.py index 846c6361..956f1eba 100644 --- a/src/config/models.py +++ b/src/config/models.py @@ -445,6 +445,7 @@ class UniqueModel(BaseModel): itemType: list[ItemType] = [] minGreaterAffixCount: int = 0 minPower: int = 0 + mythic: bool = False @field_validator("minPower") def check_min_power(cls, v: int) -> int: diff --git a/src/item/data/rarity.py b/src/item/data/rarity.py index a8db0045..6c48fce6 100644 --- a/src/item/data/rarity.py +++ b/src/item/data/rarity.py @@ -5,5 +5,6 @@ class ItemRarity(Enum): Common = "common" Legendary = "legendary" Magic = "magic" + Mythic = "mythic" Rare = "rare" Unique = "unique" diff --git a/src/item/descr/read_descr.py b/src/item/descr/read_descr.py index 0e03b854..95943dad 100644 --- a/src/item/descr/read_descr.py +++ b/src/item/descr/read_descr.py @@ -50,7 +50,9 @@ def read_descr(rarity: ItemRarity, img_item_descr: np.ndarray, show_warnings: bo # ========================= affix_bullets = find_affix_bullets(img_item_descr, sep_short_match) futures["aspect_bullet"] = ( - TP.submit(find_aspect_bullet, img_item_descr, sep_short_match) if rarity in [ItemRarity.Legendary, ItemRarity.Unique] else None + TP.submit(find_aspect_bullet, img_item_descr, sep_short_match) + if rarity in [ItemRarity.Legendary, ItemRarity.Unique, ItemRarity.Mythic] + else None ) empty_sockets = find_empty_sockets(img_item_descr, sep_short_match) @@ -129,7 +131,7 @@ def _get_affixes(): # Find aspects of uniques # ========================= - if rarity == ItemRarity.Unique: + if rarity in [ItemRarity.Unique, ItemRarity.Mythic]: item.aspect, debug_str = find_aspect(img_item_descr, aspect_bullet) if item.aspect is None: item.aspect, debug_str = find_aspect(img_item_descr, aspect_bullet, False) diff --git a/src/item/descr/texture.py b/src/item/descr/texture.py index 58d0027f..cafe1aad 100644 --- a/src/item/descr/texture.py +++ b/src/item/descr/texture.py @@ -90,7 +90,7 @@ def find_aspect_bullet(img_item_descr: np.ndarray, sep_short_match: TemplateMatc aspect_bullets = _find_bullets( img_item_descr=img_item_descr, sep_short_match=sep_short_match, - template_list=["aspect_bullet_point", "unique_bullet_point"], + template_list=["aspect_bullet_point", "unique_bullet_point", "mythic_bullet_point"], threshold=0.8, mode="first", ) diff --git a/src/item/filter.py b/src/item/filter.py index 2eb4b194..74bec1f9 100644 --- a/src/item/filter.py +++ b/src/item/filter.py @@ -170,6 +170,9 @@ def _check_unique_item(self, item: Item) -> _FilterResult: return _FilterResult(True, []) for profile_name, profile_filter in self.unique_filters.items(): for filter_item in profile_filter: + # check mythic + if filter_item.mythic and item.rarity != ItemRarity.Mythic: + continue # check item type if not self._match_item_type(expected_item_types=filter_item.itemType, item_type=item.item_type): continue @@ -331,10 +334,10 @@ def should_keep(self, item: Item) -> _FilterResult: if item.item_type == ItemType.Sigil: return self._check_sigil(item) - if item.rarity == ItemRarity.Unique: + if item.rarity in [ItemRarity.Unique, ItemRarity.Mythic]: return self._check_unique_item(item) - if item.rarity != ItemRarity.Unique: + if item.rarity not in [ItemRarity.Unique, ItemRarity.Mythic]: keep_affixes = self._check_affixes(item) if keep_affixes.keep: return keep_affixes diff --git a/src/item/find_descr.py b/src/item/find_descr.py index a15b3a6a..d3098804 100644 --- a/src/item/find_descr.py +++ b/src/item/find_descr.py @@ -9,11 +9,12 @@ from src.utils.roi_operations import fit_roi_to_window_size map_template_rarity = { - "item_unique_top_left": ItemRarity.Unique, - "item_rare_top_left": ItemRarity.Rare, + "item_common_top_left": ItemRarity.Common, "item_leg_top_left": ItemRarity.Legendary, "item_magic_top_left": ItemRarity.Magic, - "item_common_top_left": ItemRarity.Common, + "item_mythic_top_left": ItemRarity.Mythic, + "item_rare_top_left": ItemRarity.Rare, + "item_unique_top_left": ItemRarity.Unique, } diff --git a/src/loot_filter.py b/src/loot_filter.py index 794c604c..aa9785aa 100644 --- a/src/loot_filter.py +++ b/src/loot_filter.py @@ -117,7 +117,11 @@ def check_items(inv: InventoryBase, force_refresh: ItemRefreshType): if not res.keep: keyboard.send("space") time.sleep(0.13) - elif res.keep and (matched_any_affixes or item_descr.rarity == ItemRarity.Unique) and IniConfigLoader().general.mark_as_favorite: + elif ( + res.keep + and (matched_any_affixes or item_descr.rarity in [ItemRarity.Unique, ItemRarity.Mythic]) + and IniConfigLoader().general.mark_as_favorite + ): LOGGER.info("Mark as favorite") keyboard.send("space") time.sleep(0.17) diff --git a/src/tools/gen_data.py b/src/tools/gen_data.py index aebaa9ef..8cb40444 100644 --- a/src/tools/gen_data.py +++ b/src/tools/gen_data.py @@ -4,6 +4,8 @@ import re from pathlib import Path +D4LF_BASE_DIR = Path(__file__).parent.parent.parent + def remove_content_in_braces(input_string) -> str: pattern = r"\{.*?\}" @@ -76,8 +78,10 @@ def main(d4data_dir: Path, companion_app_dir: Path): with open(json_file, encoding="utf-8") as file: data = json.load(file) snoId = data["__snoID__"] - name_idx, _ = (0, 1) if data["arStrings"][0]["szLabel"] == "Name" else (1, 0) - name = data["arStrings"][name_idx]["szText"] + name_item = [item for item in data["arStrings"] if item["szLabel"] == "Name"] + if not name_item: + continue + name = name_item[0]["szText"] name_clean = name.strip().replace(" ", "_").lower().replace("’", "").replace("'", "").replace(",", "") name_clean = check_ms(name_clean) # Open affix file for affix @@ -93,11 +97,10 @@ def main(d4data_dir: Path, companion_app_dir: Path): num_idx = get_random_number_idx(desc) unique_dict[name_clean] = {"desc": desc_clean, "snoId": snoId, "full": desc, "num_idx": num_idx} # add custom uniques that seem to be missing - json_file = f"src/tools/data/custom_uniques_{language}.json" - with open(json_file, encoding="utf-8") as file: - data = json.load(file) + with open(D4LF_BASE_DIR / f"src/tools/data/custom_uniques_{language}.json", encoding="utf-8") as json_file: + data = json.load(json_file) unique_dict.update(data) - with open(f"assets/lang/{language}/uniques.json", "w", encoding="utf-8") as json_file: + with open(D4LF_BASE_DIR / f"assets/lang/{language}/uniques.json", "w", encoding="utf-8") as json_file: json.dump(unique_dict, json_file, indent=4, ensure_ascii=False, sort_keys=True) json_file.write("\n") @@ -132,8 +135,7 @@ def main(d4data_dir: Path, companion_app_dir: Path): sigil_dict[affix_type][name.replace(" ", "_")] = f"{name} {remove_content_in_braces(desc)}" # Some of the unique specific affixes are missing. Add them manually - json_file = f"src/tools/data/custom_sigils_{language}.json" - with open(json_file, encoding="utf-8") as file: + with open(D4LF_BASE_DIR / f"src/tools/data/custom_sigils_{language}.json", encoding="utf-8") as file: data = json.load(file) for key, value in data.items(): if key in sigil_dict: @@ -149,7 +151,7 @@ def main(d4data_dir: Path, companion_app_dir: Path): else: sigil_dict[key] = value - with open(f"assets/lang/{language}/sigils.json", "w", encoding="utf-8") as json_file: + with open(D4LF_BASE_DIR / f"assets/lang/{language}/sigils.json", "w", encoding="utf-8") as json_file: json.dump(sigil_dict, json_file, indent=4, ensure_ascii=False, sort_keys=True) json_file.write("\n") @@ -194,21 +196,20 @@ def main(d4data_dir: Path, companion_app_dir: Path): name_str: str = check_ms(data["arStrings"][name_idx]["szText"]).lower().strip() if item_type in whitelist_types: item_typ_dict[item_type] = name_str - with open(f"assets/lang/{language}/item_types.json", "w", encoding="utf-8") as json_file: + with open(D4LF_BASE_DIR / f"assets/lang/{language}/item_types.json", "w", encoding="utf-8") as json_file: json.dump(item_typ_dict, json_file, indent=4, ensure_ascii=False, sort_keys=True) json_file.write("\n") print(f"Gen Tooltips for {language}") tooltip_dict = {} - tooltip_path = d4data_dir / f"json/{language}_Text/meta/StringList/UIToolTips.stl.json" - with open(tooltip_path, encoding="utf-8") as file: + with open(d4data_dir / f"json/{language}_Text/meta/StringList/UIToolTips.stl.json", encoding="utf-8") as file: data = json.load(file) for arString in data["arStrings"]: if arString["szLabel"] == "ItemPower": tooltip_dict["ItemPower"] = remove_content_in_braces(check_ms(arString["szText"].lower())) if arString["szLabel"] == "ItemTier": tooltip_dict["ItemTier"] = remove_content_in_braces(check_ms(arString["szText"].lower())) - with open(f"assets/lang/{language}/tooltips.json", "w", encoding="utf-8") as json_file: + with open(D4LF_BASE_DIR / f"assets/lang/{language}/tooltips.json", "w", encoding="utf-8") as json_file: json.dump(tooltip_dict, json_file, indent=4, ensure_ascii=False, sort_keys=True) json_file.write("\n") @@ -217,8 +218,7 @@ def main(d4data_dir: Path, companion_app_dir: Path): # https://github.com/josdemmers/D4DataParser/blob/main/D4DataParser/Parsers/AffixParser.cs print(f"Gen Affixes for {language}") affix_dict = {} - json_file = companion_app_dir / f"D4Companion/Data/Affixes.Full.{language}.json" - with open(json_file, encoding="utf-8") as file: + with open(companion_app_dir / f"D4Companion/Data/Affixes.Full.{language}.json", encoding="utf-8") as file: data = json.load(file) for affix in data: desc: str = affix["Description"] @@ -227,8 +227,7 @@ def main(d4data_dir: Path, companion_app_dir: Path): if len(desc) > 2: affix_dict[name] = desc # Some of the unique specific affixes are missing. Add them manually - json_file = f"src/tools/data/custom_affixes_{language}.json" - with open(json_file, encoding="utf-8") as file: + with open(D4LF_BASE_DIR / f"src/tools/data/custom_affixes_{language}.json", encoding="utf-8") as file: data = json.load(file) for key, value in data.items(): if key in affix_dict: @@ -239,7 +238,7 @@ def main(d4data_dir: Path, companion_app_dir: Path): affix_dict[key] = value else: affix_dict[key] = value - with open(f"assets/lang/{language}/affixes.json", "w", encoding="utf-8") as json_file: + with open(D4LF_BASE_DIR / f"assets/lang/{language}/affixes.json", "w", encoding="utf-8") as json_file: json.dump(affix_dict, json_file, indent=4, ensure_ascii=False, sort_keys=True) json_file.write("\n") @@ -250,8 +249,10 @@ def main(d4data_dir: Path, companion_app_dir: Path): import argparse parser = argparse.ArgumentParser(description="Path Argument Parser") - parser.add_argument("d4data_dir", type=str, help="Provide a path to d4data repo") - parser.add_argument("companion_app_dir", type=str, help="Provide a path to companion_app_dir repo") + parser.add_argument("d4data_dir", type=str, help="Provide a path to d4data repo") # https://github.com/DiabloTools/d4data.git + parser.add_argument( + "companion_app_dir", type=str, help="Provide a path to companion_app_dir repo" + ) # https://github.com/josdemmers/Diablo4Companion args = parser.parse_args() input_path = Path(args.d4data_dir) diff --git a/tests/item/filter/data/filters.py b/tests/item/filter/data/filters.py index f6b2db2f..01ed83d9 100644 --- a/tests/item/filter/data/filters.py +++ b/tests/item/filter/data/filters.py @@ -218,5 +218,6 @@ minPower=900, ), UniqueModel(aspect=AspectUniqueFilterModel(name="soulbrand", value=15, comparison=ComparisonType.smaller), minPower=900), + UniqueModel(mythic=True), ], ) diff --git a/tests/item/filter/data/uniques.py b/tests/item/filter/data/uniques.py index c612476f..2d070d79 100644 --- a/tests/item/filter/data/uniques.py +++ b/tests/item/filter/data/uniques.py @@ -57,4 +57,5 @@ def __init__(self, rarity=ItemRarity.Unique, item_type=ItemType.Shield, power=91 ), ("ok_2", ["test.black_river", "test.black_river"], TestUnique(item_type=ItemType.Scythe, aspect=Aspect(name="black_river", value=128))), ("ok_3", ["test.soulbrand"], TestUnique(aspect=Aspect(name="soulbrand", value=11))), + ("mythic", ["test.black_river"], TestUnique(aspect=Aspect(name="black_river"), rarity=ItemRarity.Mythic)), ]