Skip to content

Commit

Permalink
filter inherent affixes (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
New Name authored and uidn4233 committed Dec 24, 2023
1 parent edc63a3 commit f0e3fed
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 50 deletions.
51 changes: 41 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ![logo](assets/logo.png)

Filter items in your inventory based on affixes, aspects and thresholds of their values. For questions, feature request or issue reports join the [discord](https://discord.gg/YyzaPhAN6T) or use github issues.
Filter items and sigils in your inventory based on affixes, aspects and thresholds of their values. For questions, feature request or issue reports join the [discord](https://discord.gg/YyzaPhAN6T) or use github issues.

[![Alt text for thumbnail](assets/thumbnail.jpg)](https://www.youtube.com/watch?v=Paorfwl3GCg)

Expand All @@ -10,6 +10,7 @@ Filter items in your inventory based on affixes, aspects and thresholds of their
- Filter by affix and their values
- Filter by aspects and their values
- Filter uniques and their affix and aspect values
- Filter sigils by blacklisting locations and affixes
- Supported resolutions: 1920x1080, 3840x1080, 2560x1080, 2560x1440, 3440x1440, 5120x1440, 3840x2160

## How to Setup
Expand All @@ -23,14 +24,13 @@ Filter items in your inventory based on affixes, aspects and thresholds of their
- Download the latest version (.zip) from the releases: https://github.com/aeon0/d4lf/releases
- Execute d4lf.exe and go to your D4 screen
- There is a small overlay on the center bottom with buttons:
- max/min: Show or hide the console output. Good for checking ingame if there are some errors.
- start/stop: Turn filtering on/off
- scripts/stop: In case there are any scripts attached, run them
- max/min: Show or hide the console output
- filter: Auto filter inventory and stash if open (number of stash tabs configurable)
- vision: Turn vision mode (overlay) on/off
- Alternative use the hotkeys. e.g. f11 for filtering


### Limitations
- If you rerolled an affix, that affix (and the one above) will not be considered. So these items will most likely not show up with a green box
- The tool does not play well with HDR as it makes everything super bright

### Configs
Expand Down Expand Up @@ -61,6 +61,9 @@ The config folder contains:
| scripts | Running different scripts |

## How to filter

All items are whitelist filters. If a filter for an unique or a certain item type is not included in your .yaml filters, they will be discarded. All sigils are blacklist filted, meaning by default all sigils are good unless they match any of the blacklisted affixes. See more detailed descriptions below.

### Aspects
In your profile .yaml files any aspects can be added in the format of `[ASPECT_KEY, THRESHOLD, CONDITION]`. The condition can be any of `[larger, smaller]` and defaults to `larger` if no value is given. Smaller has to be used when the aspect go from high value to a lower value (eg. ‍Blood-bathed Aspect)

Expand All @@ -75,12 +78,12 @@ Aspects:
Aspect keys are lower case and spaces are replaced by underscore. You can find the full list of keys in [assets/aspect.json](assets/aspects.json). If Aspects is empty, all legendary items will be kept.
### Affixes
Affixes have the same structure of `[AFFIX_KEY, THRESHOLD, CONDITION]` as described above. Additionally, it can be filtered by `itemType`, `minPower` and `minAffixCount`. See the list of affix keys in [assets/affixes.json](assets/affixes.json). Uniques are by default always kept while Magic and Common items are not.
Affixes have the same structure of `[AFFIX_KEY, THRESHOLD, CONDITION]` as described above. Additionally, it can be filtered by `itemType`, `minPower` and `minAffixCount`. See the list of affix keys in [assets/affixes.json](assets/affixes.json). For items with inherent affixes `inherentPool` can be specified, then at least one of these has to match the inherent affixes on that item.

```yaml
Affixes:
# Search for armor and pants that have at least 3 affixes of the affixPool
- Armor:
- NiceArmor:
itemType: [armor, pants]
minPower: 725
affixPool:
Expand All @@ -90,21 +93,49 @@ Affixes:
- [total_armor, 9]
- [maximum_life, 700]
minAffixCount: 3
# Search for boots that have at least 2 of the specified affixes and
# either max evade charges or reduced evade cooldown as inherent affix
- GreatBoots:
itemType: boots
minPower: 800
inherentPool:
- maximum_evade_charges
- attacks_reduce_evades_cooldown
affixPool:
- [movement_speed, 16]
- [cold_resistance]
- [lightning_resistance]
minAffixCount: 2
```

### Uniques
Uniques look similar to affixes, but will need the full unique description of itemtype and affixes to be able to be matched:
Uniques are identified by their aspect (see [assets/aspects_unique.json](assets/aspects_unique.json)). They also have a affixPool, but since uniques have these fixed you only need to specify the ones you want to threshold.

```yaml
# Take all Tibault's Will pants that have item power > 900 and dmg reduction from close > 12 as well as aspect value > 25. Note that the other affixes still must be specified to be able to match the unique item to the config!
# Take all Tibault's Will pants that have item power > 900 and dmg reduction from close > 12 as well as aspect value > 25
Uniques:
- aspect: [tibaults_will]
minPower: 900
affixPool:
- [damage_reduction_from_close_enemies, 12]
```

Note: If an itemType is not included in your filters, all items of this type will be discarded as junk! So if you want to take all items of a certain type, add it and leave minPower, affixPool and minAffixCount empty.
### Sigils
Sigils are all ok unless they match any of the blacklisted locations or affixes. See all sigil locations and affixes here: [assets/sigils.json](assets/sigils.json)
```yaml
Sigils:
minTier: 40
maxTier: 100
blacklist:
# locations
- endless_gates
- vault_of_the_forsaken
# affixes
- armor_breakers
- resistance_breakers
```

## Custom configs
D4LF will look for __params.ini__ and for __profiles/*.yaml__ also in C:/Users/WINDOWS_USER/.d4lf. All values in params.ini will overwrite the value from the param.ini in the D4LF folder. In the profiles folder additional custom profiles can be added and used.
Expand Down
40 changes: 20 additions & 20 deletions assets/sigils.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,6 @@
"whispering_vault": "whispering vault",
"revives_allowed": "revives allowed"
},
"positive": {
"quick_killer": "quick killer killing a monster grants attack and move speed. stacking up to.",
"reduce_cooldowns_on_kill": "reduce cooldowns on kill killing a monster reduces your cooldowns by seconds.",
"battle_hardened": "battle hardened gain damage reduction for every health you are missing.",
"poisonous_evade": "poisonous evade using evade leaves a pool of poison behind that damages enemies.",
"nudging_evade": "nudging evade using evade pushes enemies back.",
"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.",
"increased_critical_strike": "increased critical strike your critical strike chance is increased by.",
"increased_healing": "increased healing your healing received is increased by.",
"lightning_damage": "lightning damage you deal more lightning damage.",
"magic_find": "magic find you find more items from enemies.",
"lightning_caller": "lightning caller you occasionally call down lightning strikes that damage nearby enemies.",
"physical_damage": "physical damage you deal more physical damage.",
"poison_damage": "poison damage you deal more poison damage.",
"shadow_damage": "shadow damage you deal more shadow damage.",
"control_impaired_explosions": "control impaired explosions being hit by control impairing effects creates an explosion around you.",
"thorns": "thorns your thorns are increased by."
},
"negative": {
"avenger": "avenger killing a monster enrages monsters near it, making them dealing more damage.",
"blood_blister": "blood blister killing a monster has a chance to spawn a blood blister. after a short time, it explodes, dealing heavy area damage.",
Expand Down Expand Up @@ -105,5 +85,25 @@
"monster_thorns": "monster thorns monsters reflect of nonphysical damage.",
"armor_breakers": "armor breakers monster attacks from a distance reduce your armor by for seconds, stacking up to.",
"unstoppable_monsters": "unstoppable monsters monsters become unstoppable when life drops below."
},
"positive": {
"quick_killer": "quick killer killing a monster grants attack and move speed. stacking up to.",
"reduce_cooldowns_on_kill": "reduce cooldowns on kill killing a monster reduces your cooldowns by seconds.",
"battle_hardened": "battle hardened gain damage reduction for every health you are missing.",
"poisonous_evade": "poisonous evade using evade leaves a pool of poison behind that damages enemies.",
"nudging_evade": "nudging evade using evade pushes enemies back.",
"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.",
"increased_critical_strike": "increased critical strike your critical strike chance is increased by.",
"increased_healing": "increased healing your healing received is increased by.",
"lightning_damage": "lightning damage you deal more lightning damage.",
"magic_find": "magic find you find more items from enemies.",
"lightning_caller": "lightning caller you occasionally call down lightning strikes that damage nearby enemies.",
"physical_damage": "physical damage you deal more physical damage.",
"poison_damage": "poison damage you deal more poison damage.",
"shadow_damage": "shadow damage you deal more shadow damage.",
"control_impaired_explosions": "control impaired explosions being hit by control impairing effects creates an explosion around you.",
"thorns": "thorns your thorns are increased by."
}
}
16 changes: 13 additions & 3 deletions src/item/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ def load_files(self):
for filter_name, filter_data in filter_dict.items():
if "affixPool" in filter_data:
self.check_affix_pool(filter_data["affixPool"], self.affix_dict, filter_name)
if "inherentPool" in filter_data:
self.check_affix_pool(filter_data["inherentPool"], self.affix_dict, filter_name)

if config is not None and "Sigils" in config:
info_str += "Sigils "
Expand Down Expand Up @@ -233,6 +235,7 @@ def should_keep(self, item: Item) -> tuple[bool, bool, list[str], str]:
if item.type is None or item.power is None:
return False, False, [], ""

# Filter Sigils
if item.type == ItemType.Sigil:
if len(self.sigil_filters.items()) == 0:
return True, False, [], ""
Expand All @@ -256,10 +259,17 @@ def should_keep(self, item: Item) -> tuple[bool, bool, list[str], str]:
if not power_ok or not type_ok:
continue
matched_affixes = self._match_affixes(filter_data, item)
if filter_min_affix_count is None or len(matched_affixes) >= filter_min_affix_count:
affix_debug_msg = [name for name in matched_affixes]
affixes_ok = filter_min_affix_count is None or len(matched_affixes) >= filter_min_affix_count
inherent_ok = True
matched_inherent = []
if "inherentPool" in filter_data:
matched_inherent = self._match_affixes(filter_data, item, "inherentPool", match_inherent=True)
inherent_ok = len(matched_inherent) > 0
if affixes_ok and inherent_ok:
all_matched_affixes = matched_affixes + matched_inherent
affix_debug_msg = [name for name in all_matched_affixes]
Logger.info(f"Matched {profile_str}.{filter_name}: {affix_debug_msg}")
return True, True, matched_affixes, f"{profile_str}.{filter_name}"
return True, True, all_matched_affixes, f"{profile_str}.{filter_name}"

if item.aspect:
for profile_str, aspect_filter in self.aspect_filters.items():
Expand Down
54 changes: 37 additions & 17 deletions test/item/filter_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from item.filter import Filter
from item.data.rarity import ItemRarity
from item.data.item_type import ItemType
Expand All @@ -9,26 +10,45 @@
from template_finder import stored_templates


def test_should_keep():
@pytest.mark.parametrize(
"item_descr",
[
Item(
ItemRarity.Common,
ItemType.Sigil,
100,
None,
[
Affix("lightning_damage", 15),
Affix("death_pulse"),
Affix("monster_lightning_damage", 20),
Affix("monster_life", 30),
Affix("potion_breakers", 0.75),
],
[Affix("tormented_ruins"), Affix("revives_allowed", 4)],
),
Item(
ItemRarity.Rare,
ItemType.Pants,
844,
None,
[
Affix("potion_capacity", 3),
Affix("thorns", 873),
Affix("damage_reduction_from_close_enemies", 11),
Affix("imbuement_skill_cooldown_reduction", 5.8),
],
[
Affix("injured_potion_resource", 20),
],
),
],
)
def test_should_keep(item_descr: Item):
Cam().update_window_pos(0, 0, 1920, 1080)
Config().load_data()
stored_templates.cache_clear()

item = Item(
ItemRarity.Common,
ItemType.Sigil,
100,
None,
[
Affix("lightning_damage", 15),
Affix("death_pulse"),
Affix("monster_lightning_damage", 20),
Affix("monster_life", 30),
Affix("potion_breakers", 0.75),
],
[Affix("tormented_ruins"), Affix("revives_allowed", 4)],
)

filter = Filter()
keep, x, matched_affixes, filter_name = filter.should_keep(item)
keep, did_match_affixes, matched_affixes, filter_name = filter.should_keep(item_descr)
print(keep)

0 comments on commit f0e3fed

Please sign in to comment.