Showing preview only (999K chars total). Download the full file or copy to clipboard to get everything.
Repository: aeon0/d4lf
Branch: main
Commit: ae38f65d37ec
Files: 153
Total size: 948.8 KB
Directory structure:
gitextract_9j8i29wl/
├── .clang-format
├── .gitattributes
├── .github/
│ ├── actions/
│ │ └── setup_env/
│ │ └── action.yml
│ └── workflows/
│ ├── ci.yml
│ ├── notify.yml
│ └── release.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE.txt
├── README.md
├── assets/
│ └── lang/
│ └── enUS/
│ ├── How to add to these files.md
│ ├── affixes.json
│ ├── aspects.json
│ ├── corrections.json
│ ├── item_types.json
│ ├── paragon_maxroll_ids.json
│ ├── sigils.json
│ ├── tooltips.json
│ ├── tributes.json
│ └── uniques.json
├── build.py
├── pyproject.toml
├── pytest.ini
├── src/
│ ├── __init__.py
│ ├── autoupdater.py
│ ├── cam.py
│ ├── config/
│ │ ├── __init__.py
│ │ ├── data.py
│ │ ├── helper.py
│ │ ├── loader.py
│ │ ├── profile_models.py
│ │ ├── settings_models.py
│ │ └── ui.py
│ ├── dataloader.py
│ ├── gui/
│ │ ├── __init__.py
│ │ ├── activity_log_widget.py
│ │ ├── collapsible_widget.py
│ │ ├── config_tab.py
│ │ ├── config_window.py
│ │ ├── d4lfitem.py
│ │ ├── dialog.py
│ │ ├── importer/
│ │ │ ├── __init__.py
│ │ │ ├── d4builds.py
│ │ │ ├── diablo_trade.py
│ │ │ ├── gui_common.py
│ │ │ ├── importer_config.py
│ │ │ ├── maxroll.py
│ │ │ ├── mobalytics.py
│ │ │ └── paragon_export.py
│ │ ├── importer_window.py
│ │ ├── open_user_config_button.py
│ │ ├── profile_editor/
│ │ │ ├── __init__.py
│ │ │ ├── affixes_tab.py
│ │ │ ├── aspect_upgrades_tab.py
│ │ │ ├── global_uniques_tab.py
│ │ │ ├── profile_editor.py
│ │ │ ├── sigils_tab.py
│ │ │ └── tributes_tab.py
│ │ ├── profile_editor_window.py
│ │ ├── profile_tab.py
│ │ ├── themes.py
│ │ └── unified_window.py
│ ├── item/
│ │ ├── __init__.py
│ │ ├── data/
│ │ │ ├── __init__.py
│ │ │ ├── affix.py
│ │ │ ├── aspect.py
│ │ │ ├── item_type.py
│ │ │ ├── rarity.py
│ │ │ └── seasonal_attribute.py
│ │ ├── descr/
│ │ │ ├── __init__.py
│ │ │ ├── read_descr_tts.py
│ │ │ ├── text.py
│ │ │ └── texture.py
│ │ ├── filter.py
│ │ ├── find_descr.py
│ │ └── models.py
│ ├── logger.py
│ ├── loot_mover.py
│ ├── main.py
│ ├── overlay.py
│ ├── paragon_overlay.py
│ ├── scripts/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── handler.py
│ │ ├── loot_filter_tts.py
│ │ ├── vision_mode_fast.py
│ │ └── vision_mode_with_highlighting.py
│ ├── startup_messages.py
│ ├── template_finder.py
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── data/
│ │ │ ├── custom_affixes_enUS.json
│ │ │ └── custom_sigils_enUS.json
│ │ └── gen_data.py
│ ├── tts.py
│ ├── ui/
│ │ ├── __init__.py
│ │ ├── char_inventory.py
│ │ ├── inventory_base.py
│ │ ├── menu.py
│ │ ├── stash.py
│ │ └── vendor.py
│ └── utils/
│ ├── __init__.py
│ ├── custom_mouse.py
│ ├── image_operations.py
│ ├── misc.py
│ ├── process_handler.py
│ ├── roi_operations.py
│ └── window.py
├── tests/
│ ├── __init__.py
│ ├── config/
│ │ ├── __init__.py
│ │ ├── data/
│ │ │ ├── __init__.py
│ │ │ ├── sigils.py
│ │ │ └── uniques.py
│ │ ├── helper_test.py
│ │ ├── loader_test.py
│ │ ├── models_test.py
│ │ └── ui_test.py
│ ├── conftest.py
│ ├── gui/
│ │ ├── __init__.py
│ │ └── importer/
│ │ ├── __init__.py
│ │ ├── test_d4builds.py
│ │ ├── test_diablo_trade.py
│ │ ├── test_gui_common.py
│ │ ├── test_maxroll.py
│ │ └── test_mobalytics.py
│ ├── item/
│ │ ├── __init__.py
│ │ ├── descr/
│ │ │ └── __init__.py
│ │ ├── filter/
│ │ │ ├── __init__.py
│ │ │ ├── data/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── affixes.py
│ │ │ │ ├── aspects.py
│ │ │ │ ├── filters.py
│ │ │ │ ├── items.py
│ │ │ │ ├── sigils.py
│ │ │ │ ├── tributes.py
│ │ │ │ └── uniques.py
│ │ │ └── filter_test.py
│ │ ├── read_descr_season6_tts_test.py
│ │ ├── read_descr_season8_tts_test.py
│ │ ├── read_descr_season_11_tts_test.py
│ │ ├── read_descr_season_12_tts_test.py
│ │ ├── read_descr_season_13_tts_test.py
│ │ └── read_descr_tts_test.py
│ ├── template_finder_test.py
│ ├── ui/
│ │ ├── __init__.py
│ │ ├── char_inventory_test.py
│ │ └── chest_test.py
│ └── utils/
│ ├── __init__.py
│ ├── image_operations_test.py
│ └── roi_operations_test.py
└── tts/
├── install_dll.cmd
├── saapi.cpp
├── saapi.h
└── saapi.vcxproj
================================================
FILE CONTENTS
================================================
================================================
FILE: .clang-format
================================================
Language: Cpp
BasedOnStyle: Google
ColumnLimit: 140
IndentWidth: 4
TabWidth: 4
UseTab: Never
================================================
FILE: .gitattributes
================================================
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Denote all files that are truly binary and should not be modified.
*.jpg binary
*.png binary
# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout.
*.md text eol=lf
*.sh text eol=lf
*.yaml text eol=lf
*.yml text eol=lf
================================================
FILE: .github/actions/setup_env/action.yml
================================================
name: Setup env
runs:
using: "composite"
steps:
- name: Setup uv
uses: astral-sh/setup-uv@v8.1.0
with:
activate-environment: true
cache-dependency-glob: |
**/uv.lock
enable-cache: true
ignore-nothing-to-cache: true
python-version: 3.14
- name: Install dependencies
shell: powershell
run: uv sync --frozen --all-groups
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
push:
branches: [main]
concurrency:
group: "${{github.workflow}}-${{github.ref}}"
cancel-in-progress: true
jobs:
tests:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Setup env
uses: ./.github/actions/setup_env
- name: Run prek
uses: j178/prek-action@v2
- name: Pytest
shell: powershell
run: pytest . -m "not selenium" -v -n logical
# - name: Pytest selenium
# shell: powershell
# run: pytest . -m "selenium" -v
================================================
FILE: .github/workflows/notify.yml
================================================
name: Notify
on:
release:
types: [published]
jobs:
github-releases-to-discord:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: SethCohen/github-releases-to-discord@v1.20
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
color: "2105893"
username: "D4LF Release"
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
pull_request:
types: [closed]
workflow_dispatch:
concurrency:
group: release
jobs:
release:
if: |
github.event_name != 'pull_request' ||
(
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'release')
)
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Setup env
uses: ./.github/actions/setup_env
- name: Build & Zip exe
id: build_zip
shell: powershell
run: |
python build.py
$version = python -c "from src import __version__; print(__version__)"
echo "VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
$folderName = "d4lf"
$zipName = "d4lf_v" + $version
Compress-Archive -Path $folderName -DestinationPath "$zipName.zip"
- name: Create Tag
shell: powershell
run: |
git tag "v${{ env.VERSION }}"
git push origin "v${{ env.VERSION }}"
- name: Check if beta
id: check_beta
shell: powershell
run: |
if ($env:VERSION -like "*beta*" -or $env:VERSION -like "*alpha*") {
echo "IS_BETA=true" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
} else {
echo "IS_BETA=false" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
}
- uses: softprops/action-gh-release@v3
with:
files: d4lf_v*.zip
generate_release_notes: true
name: "v${{ env.VERSION }}"
prerelease: ${{ env.IS_BETA == 'true' }}
tag_name: "v${{ env.VERSION }}"
token: "${{ secrets.RELEASE_TOKEN }}"
================================================
FILE: .gitignore
================================================
!config/bnip/.gitkeep
*.bak
*.log
*.pyc
*.pyo
*.spec
*_generated.py
*info_*.png
*info_log_parsed.txt
.coverage
.idea/
.pytest_cache/
.venv
.vs/
*.egg-info/
/tts/saapi
/tts/.tools/
/tts/x64
__pycache__/
build/
config/bnip/*
config/custom.*.ini
config/custom.ini
coverage.xml
custom.ini
custom_filter_affixes.yaml
custom_filter_aspects.yaml
d4lf_*
dist/
generated/
htmlcov/
logs/
main.build/
main.dist/
main.onefile-build/
playground.py
stats/
utils/live-view/
venv
assets/last_update
.codex
AGENTS.md
================================================
FILE: .pre-commit-config.yaml
================================================
default_install_hook_types: [pre-push]
default_language_version:
python: python3.14
minimum_prek_version: '0.3.0'
repos:
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.11.14
hooks:
- id: uv-lock
priority: &group1 4294967294
- repo: https://github.com/executablebooks/mdformat
rev: 1.0.0
hooks:
- id: mdformat
priority: *group1
additional_dependencies:
- mdformat-gfm
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
rev: v2.16.0
hooks:
- id: pretty-format-toml
priority: *group1
args: [--autofix, --indent, "4"]
exclude: ^uv\.lock
- id: pretty-format-yaml
priority: *group1
args: [--autofix, --preserve-quotes, --offset, "2"]
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.5
hooks:
- id: clang-format
priority: *group1
files: \.(cpp|h)$
- repo: builtin
hooks:
- id: check-added-large-files
priority: &read-only 4294967295
- id: check-case-conflict
priority: *read-only
- id: check-executables-have-shebangs
priority: *read-only
- id: check-json
priority: *read-only
- id: check-toml
priority: *read-only
- id: check-yaml
priority: *read-only
- id: end-of-file-fixer
- id: fix-byte-order-marker
exclude: ^tts/sign_dll.ps1
- id: trailing-whitespace
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-ast
priority: *read-only
- id: pretty-format-json
args: [--autofix, --indent=4, --no-ensure-ascii]
priority: *group1
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.13
hooks:
- id: ruff-format
priority: *group1
- id: ruff-check
priority: &group2 4294967293
args: [--fix]
- repo: https://github.com/thetestlabs/py-psscriptanalyzer
rev: v0.3.1
hooks:
- id: py-psscriptanalyzer
priority: *read-only
args: ['--severity', 'Error']
- id: py-psscriptanalyzer-format
priority: *group1
================================================
FILE: LICENSE.txt
================================================
MIT License
Copyright (c) 2026 d4lf contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# 
## Note: D4LF will be updated for Season 13. However, there were a lot of itemization updates and it may take a few weeks for the new features to be complete. You can monitor progress and make suggestions in discord: https://discord.com/channels/1168807680295571456/1499898416572928115
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.

## Features
- Filter items in inventory and stash
- Filter by item type, item power and greater affix count
- Filter by affix and their values, with per-affix greater affix requirements
- Filter uniques by their affix and aspect values
- Filter sigils by blacklisting and whitelisting locations and affixes
- Filter tributes by name or rarity
- Quickly move items from your stash or inventory
- Supported resolutions are all aspect ratios between 16:10 and 21:9
- Paragon Overlay with import from supported build planners (Mobalytics, Maxroll, D4Builds)
## How to Setup
### Installation and quick start guide (New instructions for season 12 that must be followed!)
- Download and extract the latest version (.zip) from the releases: https://github.com/d4lfteam/d4lf/releases
- Find your "Diablo IV" directory. Copy the path and have it in your clipboard:
- In Battle.net, click the gear icon next to the Play button and select "Open in Explorer"
- In Steam, right click the game, select Manage > Browse local files
- D4LF gets item information by reading the screen and using TTS information sent for accessibility. TTS setup takes additional steps, detailed below. For more information on the install_dll.cmd script, see [the TTS section](https://github.com/d4lfteam/d4lf/blob/main/README.md#tts)
- Navigate to your d4lf directory
- Double-click `install_dll.cmd`
- If asked for administrator permissions, provide them.
- When asked for your Diablo 4 path, provide it
- When asked to install a certificate, allow it.
- If everything is successful, proceed with the guide. Otherwise join the [discord](https://discord.gg/YyzaPhAN6T) or post an issue in github.
- Generate a profile of what Diablo 4 items you want to filter for. To do so you have a few options:
- Run d4lf.exe and import a profile using the import window by pasting a build page from popular planner websites
- Create one yourself by looking at the [examples](#how-to-filter--profiles) below
- If created manually, place the profile in the `C:/Users/<WINDOWS_USER>/.d4lf/profiles` folder. The D4LF
importer window has a button to open this folder directly. If imported they are placed there automatically.
- Run d4lf.exe and use the config button to configure the profiles in the general section. Select the '...' next to profiles to activate which
profiles you want to use.
- Ensure all [game settings](#game-settings) are configured properly.
- If you made changes, restart d4lf.exe and launch Diablo 4.
- Use the hotkeys listed in d4lf.exe to run filtering. By default, F11 will run the loot filter and filter your items.
- For most common issues, if something is wrong, you will see an error or warning when you start d4lf.exe. Join our [discord](https://discord.gg/YyzaPhAN6T) for more help.
### Game Settings
- Game Language must be English
- IMPORTANT: Advanced Tooltip Information must be enabled in Options > Gameplay > Gameplay. If you don't do this then item parsing will be very inconsistent and you will receive no warning something is wrong.
- Font scale in Graphics settings must be small or medium
- HDR makes the screen too bright and D4LF is unable to read the state of some items on screen. It must be disabled.
- Use Screen Reader must be enabled in Options > Accessibility
- 3rd Party Screen Reader must be enabled in Options > Accessibility (The voice will go away when DLL is installed, see quick start guide above)
### Common problems
- The tool shows a warning saying "TTS connection has not been made yet." but I've set everything up correctly.
- If you're seeing this error, it means D4LF has found the DLL is in the correct location but the TTS connection is
still not being made. This is most likely due to an issue with your windows user not allowing Diablo to connect to
the third party screen reader. The following steps should resolve it:
- Set Diablo 4 to run as administrator. First, navigate to your Diablo 4 directory.
- Steam User: Right click on the game and choose Properties. In that menu, go to Installed Files and hit Browse.
- Battle.net User: On the game page, click the gear icon and choose Show in Explorer
- Right-click on Diablo IV.exe and go to Properties. In the Compatibility tab, check the box that says "Run this program as an administrator"
- Run Diablo 4 again through Steam/Battle.net and see if that resolved the issue.
- If it did not, set Steam/Battle.net to run as administrator as well and make sure you are running Diablo through Steam. This should resolve the issue.
- The GUI crashes immediately upon opening, with no error message given
- This almost always means there is an issue in your params.ini. Delete the file in `C:/Users/<WINDOWS_USER>/.d4lf/` and then open the GUI and configure
your params.ini through the Settings window in D4LF. Using the GUI for configuration will ensure the file is always accurate.
- Mouse control isn't possible
- Due to your local windows settings, the tool might not be able to control the mouse. Just run the tool as admin
and it should work. If you don't want to run it as admin, you can disable the mouse control in the params.ini
by setting `vision_mode_only` to `true`.
- Paragon overlay does not appear / does nothing
- Ensure Diablo IV is running in **borderless windowed** (exclusive fullscreen may block overlays).
- Ensure your profiles folder contains `*.yaml`/`*.yml` profile files with a top-level `Paragon:` section (default: `C:/Users/<WINDOWS_USER>/.d4lf/profiles`).
- Check/adjust `advanced/settings/toggle_paragon_overlay` (default `f10`) and ensure it is not conflicting with other hotkeys.
### TTS
D4 uses a third-party TTS engine called Tolk. Tolk has a feature that allows custom third-party TTS DLLs to be loaded.
D4 automatically loads the DLL, which actually just sends the text to another application rather than reading it aloud.
This is similar to having a Braille TTS application for D4.
The TTS dll (saapi64.dll) must be signed for Diablo 4 to pick it up. The install_dll.cmd script handles all of this for you. It will:
- Copy the dll file to the Diablo 4 directory
- Download the signtool needed to add a local signature to the dll
- Runs the signtool and signs the dll
If you prefer running it from a terminal, you can run `.\install_dll.cmd`.
For very advanced users that don't want to automatically download signtool.exe, you can run `.\install_dll.cmd -signtool_path "<full path to signtool.exe>"`
### Configs
The config folder in `C:/Users/<WINDOWS_USER>/.d4lf` contains:
- **profiles/\*.yaml**: These files determine what should be filtered. Profiles created by the GUI will be placed here
automatically.
- **params.ini**: Different hotkey settings and number of chest stashes that should be looked at. Management of this
file should be done through the GUI in the config window.
- **profiles/\*.yaml**: Profiles including embedded Paragon data for the integrated overlay (top-level `Paragon:`). Generated/updated by the importer when "Import Paragon" is enabled. Default location: `C:/Users/<WINDOWS_USER>/.d4lf/profiles`
### params.ini (Settings window in GUI)
| [general] | Description |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| profiles | A set of profiles separated by comma. d4lf will look for these yaml files in config/profiles and in C:/Users/WINDOWS_USER/.d4lf/profiles |
| auto_use_temper_manuals | When using the loot filter, should found temper manuals be automatically used? Note: Will not work with stash open. |
| browser | Which browser to use to get builds, please make sure you pick an installed browser: chrome, edge or firefox are currently supported. |
| check_chest_tabs | Which chest tabs will be checked and filtered for items in case chest is open when starting the filter. You need to buy all slots. Counting is done left to right. E.g. 1,2,4 will check tab 1, tab 2, tab 4 |
| do_not_junk_ancestral_legendaries | Do not mark ancestral legendaries as junk. |
| full_dump | When using the import build feature, whether to use the full dump (e.g. contains all filter items) or not |
| handle_cosmetics | How to handle new cosmetics that do not match any filter and are not aspect upgrades. `ignore` will ignore them, `junk` will mark them as junk |
| handle_uniques | How to handle uniques that do not match any filter. This property does not apply to filtered uniques. All mythics are favorited regardless of filter. <br/>- `favorite`: Mark the unique as favorite and vision mode will show it as green (default)<br/>- `ignore`: Do nothing with the unique and vision mode will show it as green<br/>- `junk`: Mark any uniques that don't match any filters as junk and show as red in vision mode |
| ignore_escalation_sigils | When filtering Sigils, should escalation sigils be ignored? |
| keep_aspects | - `all`: Keep all legendary items <br>- `upgrade`: Keep all legendary items that upgrade your codex of power. If the item matches no profile, it will be highlighted in orange <br>- `none`: Keep no legendary items based on aspect (they are still filtered!) <br>- |
| mark_as_favorite | Whether to favorite matched items or not. Defaults to true |
| max_stash_tabs | The maximum number of stash tabs you have available to you if you bought them all. If you own the Lord of Hatred expansion you should choose 7. |
| minimum_overlay_font_size | The minimum font size for the vision overlay, specifically the green text that shows which filter(s) are matching. Note: For small profile names, the font may actually be larger than this size but will never go below this size. |
| move_to_inv_item_type<br/>move_to_stash_item_type | Which types of items to move when using fast move functionality. Will only affect tabs defined in check_chest_tabs. You can select more than one option. <br>- `favorites`: Move favorites only <br>- `junk`: Move junk only <br>- `unmarked`: Only items not marked as favorite or junk <br>- `everything`: Move everything |
| run_vision_mode_on_startup | If the vision mode should automatically start when starting d4lf. Otherwise has to be started manually with the vision button or the hotkey |
| colorblind_mode | Enable a colorblind friendly palette for loot filter and paragon overlays |
| vision_mode_type | Which vision mode you would like to use?. `highlight_matches` does the classic green highlighting of affixes on screen, but is slightly slower. `fast` just puts green text on screen but is very fast and works with controllers. |
| [char] | Description |
| --------- | --------------------------------- |
| inventory | Your hotkey for opening inventory |
| [advanced_options] | Description |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| move_to_inv | Hotkey for moving items from stash to inventory |
| move_to_chest | Hotkey for moving items from inventory to stash |
| run_filter | Hotkey to start/stop filtering items |
| run_filter_drop | Hotkey to start/stop filtering items. Unmatched items are dropped instead of marked as junk |
| run_filter_force_refresh | Hotkey to start/stop filtering items with a force refresh. All item statuses will be reset |
| run_vision_mode | Hotkey to start/stop vision mode |
| force_refresh_only | Hotkey to reset all item statuses without running a filter after |
| exit_key | Hotkey to exit d4lf.exe |
| toggle_paragon_overlay | Hotkey to open/close the Paragon overlay |
| log_lvl | Logging level. Can be any of [debug, info, warning, error, critical] |
| process_name | Process name of the D4 app. Defaults to "Diablo IV.exe". In case of using some remote play this might need to be adapted |
| vision_mode_only | If set to true, only the vision mode will be available. All functionality that clicks the screen is disabled. |
| fast_vision_mode_coordinates | If you are using fast vision mode, provide the location on screen where you want the overlay to appear. For example, you could provide (500, 800) |
### GUI
d4lf.exe is the one-stop shop for all operations, including running the D4LF process and any configuration changes.
If you prefer a standalone console-only experience, you can run d4lf-consoleonly.bat instead which will not open a GUI
as well. It is still recommended you open the GUI for any configurations management.
Except for changing the vision mode, all changes are automatically applied when made. If you make changes to a profile, those will be
automatically picked up and no restart is necessary.
Current functionality:
- Import builds from maxroll/d4builds/mobalytics
- Toggle the integrated Paragon overlay (default hotkey: F10)
- Complete management of your settings through the config tab
- A beta version of a manual profile editor/creator
Each window gives further instructions on how to use it and what kind of input it expects.
## How to filter / Profiles
All profiles define whitelist filters. If no filter included in your profiles matches the item, it will be discarded.
Your config files will be validated on startup and will prevent the program from starting if the structure or syntax is
incorrect. The error message will provide hints about the specific problem.
The following sections will explain each type of filter that you can specify in your profiles. How you define them in
your YAML files is up to you; you can put all of these into just one file or have a dedicated file for each type of
filter, or even split the same type of filter over multiple files. Ultimately, all profiles specified in
your `params.ini` will be used to determine if an item should be kept. If one of the profiles wants to keep the item, it
will be kept regardless of the other profiles. Similarly, if a filter is missing in all profiles (e.g., there is
no `Sigils` section in any profile), all corresponding items (in this case, sigils) will be kept.
### Affix / Unique Aspect Filter Syntax
You have two choices on how to specify aspects or affixes of an item. For both options we recommend importing a profile first and then working from there.
- You can use the Edit Profile window in the GUI, which is the recommended approach
- You can also manually edit your profile.
The instructions below are all about editing the file manually, but the explanations apply to the GUI as well.
<details><summary>Examples</summary>
```yaml
# Filter for attack speed
- { name: attack_speed }
# Filter for attack speed with a threshold value.
# The filter keeps larger rolls when the tooltip range increases and smaller rolls when the range decreases.
- { name: attack_speed, value: 4 }
# Filter for attack speed where the affix is greater than 50% of the potential maximum
- { name: attack_speed, minPercentOfAffix: 50 }
```
</details>
### Affixes
Affixes are defined by the top-level key `Affixes`. It contains a list of filters that you want to apply. Each filter
has a name and can filter for any combination of the following:
- `itemType`: The name of the type or a list of multiple types.
See [assets/lang/enUS/item_types.json](assets/lang/enUS/item_types.json)
- `minPower`: Minimum item power
- `minGreaterAffixCount`: Minimum number of greater affixes expected on the overall item. See [Greater Affix Filtering](#greater-affix-filtering) for more information on filtering GAs.
- `affixPool`: A list of multiple different rulesets to filter for. Each ruleset must be fulfilled or the item is
discarded
- `count`: Define a list of affixes (see [syntax](#affix--unique-aspect-filter-syntax)) and
optionally `minCount`, `maxCount` and `minGreaterAffixCount`
- `minCount`: specifies the minimum number of affixes that must match the item. defaults to amount of specified
affixes
- `maxCount` specifies the maximum number of affixes that must match the item. defaults to amount of specified
affixes
- `inherentPool`: The same rules as for `affixPool` apply, but this is evaluated against the inherent affixes of the
item
- `uniqueAspect`: If you're looking for a specific unique, this is how you define it. It has the following properties:
- `name`: (Required) The name of the unique you are looking for. You can find a list in [uniques.json](assets/lang/enUS/uniques.json)
- `value`: What is the minimum value the aspect must have. You can not have both this and minPercentOfAspect
- `minPercentOfAspect`: Instead of defining a specific value, what percent of the potential maximum value of the aspect should we keep. See [this section](#filtering-on-percent-of-affix-instead-of-value) for more information.
<details><summary>Config Examples</summary>
```yaml
Affixes:
# Search for chest armor and pants that are at least item level 725 and have at least 3 affixes of the affixPool
- NiceArmor:
itemType: [ chest armor, pants ]
minPower: 725
affixPool:
- count:
- { name: dexterity, value: 33 }
- { name: damage_reduction, value: 5 }
- { name: lucky_hit_chance, value: 3 }
- { name: total_armor, value: 9 }
- { name: maximum_life, value: 700 }
minCount: 3
# Search for chest armor that is at least item level 925 and have at least 3 affixes of the affixPool. At least 2 of the matched affixes must be greater affixes
- NiceArmor:
itemType: chest armor
minPower: 925
affixPool:
- count:
- { name: dexterity }
- { name: damage_reduction }
- { name: lucky_hit_chance }
- { name: total_armor }
- { name: maximum_life }
minCount: 3
minGreaterAffixCount: 2
# 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:
- count:
- { name: maximum_evade_charges }
- { name: attacks_reduce_evades_cooldown_by_seconds }
minCount: 1
affixPool:
- count:
- { name: movement_speed, value: 16 }
- { name: cold_resistance }
- { name: lightning_resistance }
minCount: 2
# Search for boots that have at least 2 of the specified affixes AND are a Penitent Greaves
# The Greaves must have at least 19% damage multiplier to chilled enemies (Greaves's range is 15-25)
# Note this would not match non-unique boots that have movement speed and cold resistance, it will only match a Penitent Greaves
- GreatUniqueBoots:
itemType: boots
minPower: 800
affixPool:
- count:
- { name: movement_speed, value: 16 }
- { name: cold_resistance }
- { name: lightning_resistance }
minCount: 2
uniqueAspect:
- name: penitent_greaves
minPercentOfAspect: 50
# Search for boots with movement speed and 1 resistances from a pool of all resistances.
# No need to add maxCount to the resistance group since it isn't possible for an item to have more than one resistance affix
- ResBoots:
itemType: boots
minPower: 800
affixPool:
- count:
- { name: movement_speed, value: 16 }
- count:
- { name: shadow_resistance }
- { name: cold_resistance }
- { name: lightning_resistance }
- { name: fire_resistance }
- { name: poison_resistance }
minCount: 1
# Search for boots with movement speed. At least two of all item affixes must be a greater affix, but we don't care which
- GreaterAffixBoots:
itemType: boots
minPower: 800
minGreaterAffixCount: 2
affixPool:
- count:
- { name: movement_speed, value: 16 }
# Keep all ancestral items, even if they don't match a different filter
- AncestralMatch:
itemType: [amulet, axe, two-handed axe, boots, bow, chest armor, crossbow, dagger, flail, focus, glaive, gloves, helm, pants, mace, two-handed mace, totem, polearm, quarterstaff, ring, scythe, two-handed scythe, shield, staff, sword, two-handed sword, tome, wand]
minPower: 900
```
</details>
Affix names are lower case and spaces are replaced by underscore. You can find the full list of names
in [assets/lang/enUS/affixes.json](assets/lang/enUS/affixes.json).
### Filtering on percent of affix instead of value
You also have the option to filter on the minimum percent of the affix you want instead of a specific value. For example, say you want strength on an item. The potential values for strength are 100-150. If you say the `minPercentOfAffix` for strength is 50 (which means 50%), then strength rolls of 125 and up are kept and rolls below 125 would be discarded.
A greater affix is considered to always match a `minPercentOfAffix`. You do not need to designate larger/smaller for `value` or `minPercentOfAffix`; that is automatically determined from the roll range.
If you put in `minPercentOfAffix` you can not also put `value` for that affix. It must be one or the other.
These rules also apply for `minPercentOfAspect` on the `uniqueAspect` and in `GlobalUniques`.
<details><summary>Config Examples</summary>
```yaml
Affixes:
# Search for chest armor that is at least item level 925 and have at least 3 affixes of the affixPool.
# It must have more than 40 damage_reduction, and armor must be at least 70% of its potential maximum affix value
- NiceArmor:
itemType: chest armor
minPower: 925
affixPool:
- count:
- { name: dexterity }
- { name: damage_reduction, value: 40 }
- { name: lucky_hit_chance }
- { name: armor, minPercentOfAffix: 70 }
- { name: maximum_life }
minCount: 3
```
</details>
### Greater Affix Filtering
D4LF provides two complementary ways to filter items based on Greater Affixes:
#### 1. Item-Level Greater Affix Count (`minGreaterAffixCount`)
This filter requires a minimum total number of Greater Affixes on the entire item, regardless of which affixes they are.
<details><summary>Example</summary>
```yaml
Affixes:
- GreaterAffixBoots:
itemType: boots
minGreaterAffixCount: 2 # Item must have at least 2 Greater Affixes total
affixPool:
- count:
- { name: movement_speed }
- { name: maximum_life }
- { name: strength }
- { name: fire_resistance }
minCount: 3
```
</details>
#### 2. Per-Affix Greater Affix Requirements (`want_greater`)
When using the Profile Editor GUI or when importing affixes using the importer, you can mark/import specific affixes
with a "Greater" checkbox. This is shown as `want_greater` in the profile. This is a list of affixes that you would prefer
to be greater affixes. The `minGreaterAffixCount` value on the item is still respected, so if you have two affixes tagged
as `want_greater` but a `minGreaterAffixCount` of 1, an item with one of those two affixes as GA will be kept. If neither
of those affixes are GA but a different one is, the item will not be kept.
<details><summary>Example</summary>
```yaml
Affixes:
- PerfectBoots:
itemType: boots
affixPool:
- count:
- { name: movement_speed, want_greater: true } # MUST be a Greater Affix
- { name: maximum_life, want_greater: true } # MUST be a Greater Affix
- { name: strength } # Can be normal or Greater
- { name: fire_resistance } # Can be normal or Greater
minCount: 3
minGreaterAffixCount: 2 # Auto-set by GUI if Auto-Sync is checked, or Require Greater Affixes is checked on the importer
```
**This item would match:** Boots with movement_speed (GA), maximum_life (GA), cold_resistance (normal), fire_resistance (normal)\
**Why:** movement_speed and maximum_life are both Greater Affixes as required, and item has 4 affixes (meets minCount of 3)
**This item would NOT match:** Boots with movement_speed (normal), maximum_life (GA), cold_resistance (normal), fire_resistance (normal)\
**Why:** movement_speed is marked as `want_greater: true` but is not a Greater Affix on the item
</details>
#### Common Use Cases
<details><summary>Examples</summary>
**"I want boots with at least 2 Greater Affixes, don't care which ones"**
```yaml
- itemType: boots
minGreaterAffixCount: 2
affixPool:
- count:
- { name: movement_speed }
- { name: maximum_life }
- { name: strength }
- { name: fire_resistance }
minCount: 3
```
**"I want boots where movement_speed MUST be a Greater Affix"**
```yaml
- itemType: boots
minGreaterAffixCount: 1 # The minGreaterAffixCount is important, if it was 0 then movement_speed would not be required to be GA
affixPool:
- count:
- { name: movement_speed, want_greater: true }
- { name: maximum_life }
- { name: strength }
- { name: fire_resistance }
minCount: 3
```
**"I want boots where both movement_speed AND maximum_life MUST be Greater Affixes"**
```yaml
- itemType: boots
minGreaterAffixCount: 2 # minGreaterAffixCount of 2 requires both to be GA
affixPool:
- count:
- { name: movement_speed, want_greater: true }
- { name: maximum_life, want_greater: true }
- { name: strength }
- { name: fire_resistance }
minCount: 3
```
**"I want boots where either movement_speed OR maximum_life are Greater Affixes"**
```yaml
- itemType: boots
minGreaterAffixCount: 1 # minGreaterAffixCount of 1 requires either to be GA
affixPool:
- count:
- { name: movement_speed, want_greater: true }
- { name: maximum_life, want_greater: true }
- { name: strength } # If strength on the item was greater and the top two were not, this would not be matched
- { name: fire_resistance }
minCount: 3
```
</details>
### AspectUpgrades
Legendary Aspects that you want to be notified of receiving upgrades for can be placed in your profile.
They are defined in the top-level key `AspectUpgrades`.
This filter is generally for build-specific aspects that you'd like to be made aware of when you receive an upgrade so you can
upgrade that aspect immediately at the occultist. We notify the user by favoriting the item and showing orange text or
orange highlighting when hovering over the item.
If the item matches any other profile, this filter does nothing. This filter does respect the `mark_as_favorite` config property.
Any aspects that do not match this filter or are not codex upgrades are handled by the `keep_aspects` config property.
<details><summary>Config Examples</summary>
```yaml
AspectUpgrades:
# This would mark Snowveiled Adventurer's Pants as a favorite if it's a codex upgrade. It would ignore the pants otherwise.
- of_singed_extremities
- snowveiled
```
```yaml
# This works exact same as above, it's just a different way to format it
AspectUpgrades: [of_singed_extremities, snowveiled]
```
</details>
Aspect names are lower case and spaces are replaced by underscore. You can find the full list of names
in [assets/lang/enUS/aspects.json](assets/lang/enUS/aspects.json).
### Sigils
Sigils are defined by the top-level key `Sigils`. It contains a list of affix or location names that you want to filter
for. If no Sigil filter is provided, all Sigils will be kept.
<details><summary>Config Examples</summary>
```yaml
Sigils:
blacklist:
# locations
- endless_gates
- vault_of_the_forsaken
# affixes
- armor_breakers
- resistance_breakers
```
If you want to filter for a specific affix or location, you can also use the `whitelist` key. Even if `whitelist` is
present, `blacklist` will be used to discard sigils that match any of the blacklisted affixes or locations.
```yaml
# Only keep sigils for vault_of_the_forsaken without any of the affixes armor_breakers and resistance_breakers
Sigils:
blacklist:
- armor_breakers
- resistance_breakers
whitelist:
- vault_of_the_forsaken
```
To switch that priority, you can add the `priority` key with the value `whitelist`.
```yaml
# This will keep all vault of the forsaken sigils even if they have armor_breakers or resistance_breakers
Sigils:
blacklist:
- armor_breakers
- resistance_breakers
whitelist:
- vault_of_the_forsaken
priority: whitelist
```
You can also create conditional filters based on a single affix or location.
```yaml
# Only keep sigils for iron_hold when it also has shadow_damage
Sigils:
blacklist:
- armor_breakers
- resistance_breakers
whitelist:
- [ iron_hold, shadow_damage ]
```
</details>
Sigil affixes and location names are lower case and spaces are replaced by underscore. You can find the full list of
names in [assets/lang/enUS/sigils.json](assets/lang/enUS/sigils.json).
### Tributes
Tributes are defined by the top-level key `Tributes`. It contains a list of either tribute names or rarities you want
to keep. Any not in the list are not kept. If no Tribute filter is provided, all Tributes will be kept.
Mythic tributes are always kept no matter what.
<details><summary>Config Examples</summary>
```yaml
# Keeps tribute_of_mystique and all legendary and unique tributes
Tributes:
- tribute_of_mystique
- [legendary, unique]
```
If you're exceptionally pressed for time, you can just put the name of the tribute without "tribute_of\_" at the beginning.
```yaml
# Keeps Tribute of Mystique and Tribute of Ascendance (Resolute) and nothing else
Tributes:
- mystique
- ascendance_resolute
```
</details>
Tribute names are lower case and spaces are replaced by underscore. Parentheses are removed. Note that United and
Resolute identifiers are part of the names in [assets/lang/enUS/tributes.json](assets/lang/enUS/tributes.json). You can find the list of item rarities
in [rarity.py](src/item/data/rarity.py)
### GlobalUniques
If you are searching for a specific Unique, use the `uniqueAspect` key in [the Affixes section](#affixes). If you
additionally want to keep other uniques that have particular stats, use the `GlobalUniques` key.
Global unique filters are defined by the top-level key `GlobalUniques`. It contains a list of parameters that you want
to filter for. If no global unique filter is provided or if the item does not match any unique filter (affix or otherwise),
uniques will be handled according to the handle_uniques configuration. All mythics are marked as favorite regardless of
any filter or configuration.
The following global filters are available:
- `minGreaterAffixCount`: Only keep uniques with a specific number of greater affixes
- `minPercentOfAspect`: Only keep uniques whose aspect is above a percentage of the total possible.
For example, if this is set to 80 and an aspect has a range of 100-200, then a value of 180 would be kept but a value
of 150 would be marked as junk. Situations where a smaller value is what is wanted are automatically handled as well.
- `minPower`: The minimum item power of uniques to keep
- `profileAlias`: In vision mode, uniques show as <filename>.<aspect>. For example myuniques.yaml with fists_of_fate aspect defined
would show as myuniques.fists_of_fate. The label for the filename can be configured at the aspect level using the
profileAlias flag (see examples).
<details><summary>Config Examples</summary>
```yaml
# Take all uniques with item power > 900
GlobalUniques:
- minPower: 900
```
```yaml
# Take all uniques with at least 1 greater affix. It would show in logs/vision mode as cool_stuff.<name of unique>
GlobalUniques:
- minGreaterAffixCount: 1
profileAlias: cool_stuff
```
```yaml
# Note that if a unique matches any filter, it is kept. Each - denotes a new filter.
# For example, the below will keep all uniques that have two greater affixes OR an aspect percentage greater than 80
GlobalUniques:
- minGreaterAffixCount: 2
- minPercentOfAspect: 80
```
```yaml
# Conversely, this will match all uniques that have two greater affixes AND an aspect percentage greater than 80
GlobalUniques:
- minGreaterAffixCount: 2
minPercentOfAspect: 80
```
</details>
## Paragon overlay

D4LF can import Paragon boards from supported build planners and show them in-game using the Paragon overlay.
**How to use**
1. Import your build from a supported planner (Mobalytics / Maxroll / D4Builds).
1. Enable **Import Paragon** in the importer. Paragon data will be stored in your profile YAMLs in the profiles folder (default: `~/.d4lf/profiles`).
1. Toggle the Paragon overlay using the hotkey (default **F10**, configurable in *Advanced options*).
1. Follow the on-screen instructions to zoom in and out of the overlay until it is the size you want. Ideally, the golden outline will be the same size as the red lines in the paragon board. The location of the overlay is automatically saved.
**Tips**
- Overlays may not work in exclusive fullscreen; use **borderless windowed** if the overlay does not appear.
- Planner websites can change over time. If an import/export stops working, please report a bug.
## Future Plans
- A video explaining the initial setup
- Evaluate using joystick emulation to further increase speed for users willing to do additional setup
- Finish GUI documentation
- Want something done that's not mentioned here? Leave a suggestion in the [discord](https://discord.gg/YyzaPhAN6T) or use github issues. Or, make the changes yourself and open up a PR!
## Develop
### Setup using uv
If you intend to submit PRs, create your own fork of d4lf and clone that in the steps below.
Before beginning, [install uv](https://docs.astral.sh/uv/getting-started/installation/#winget).
```bash
git clone https://github.com/d4lfteam/d4lf
cd d4lf
uv sync
uv run python -m src.main
```
If you receive an error about missing Visual Studio code, follow the link it provides. Install Visual Studio Build Tools 2022 with the defaults selected and also select "MSVC VS 2022 C++ ..." and "Windows 11 SDK ...". Restart your terminal and try again.
### Formatting & Linting
Just use prek. If it's your first setup, you will need to install the NuGet package provider. Open Windows Powershell and run::
```
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
```
Then run:
```bash
prek install
```
Otherwise just run:
```bash
prek run -a
```
### A note on use of AI for PRs
AI usage is not banned for D4LF, but some things need to be kept in mind:
- You are responsible for any PR you submit.
- It is expected you have tested your code
- It is expected you will fix any bugs resulting from your work
- You need to have an understanding of the changes you're making and why you're making them
- PRs should change as little code as possible, only what needs to be changed for the new feature you are implementing.
- Unless something is being deleted, existing code comments should be maintained
- There should be 1 PR per feature. Try to keep PRs small. The release notes are generated from the PR titles so if you put a lot of items into one PR we can't properly describe it in the release notes.
- Be prepared for a lot of comments on your PR. Everything that's being done needs to be understandable by the maintainer because he has to fix it 3 months later if something goes wrong.
Ultimately, please understand there is only 1 full-time maintainer of D4LF and that maintainer does not use AI. The code needs to remain human readable, and humans are who initially wrote it. If an AI and a human disagree, the human always wins. AIs can be very stupid.
## Credits
- Icon based of: [CarbotAnimations](https://www.youtube.com/carbotanimations/about)
- Some of the OCR code is originally from [@gleed](https://github.com/aliig). Good guy
- Names and textures for matching from [Blizzard](https://www.blizzard.com)
- Thanks to NekrosStratia for the initial idea and help with TTS mode
================================================
FILE: assets/lang/enUS/How to add to these files.md
================================================
These files are all autogenerated from data from d4companion and d4data.
Any manual additions to them will be overwritten the next time that data is updated.
If you want to add data to these files, do the following steps:
1. Download the latest version of d4data: https://github.com/DiabloTools/d4data.git
1. Download the latest version of d4companion: https://github.com/josdemmers/Diablo4Companion
1. Run [gen_data.py](/src/tools/gen_data.py). You provide the paths of the above two downloads. For example,
you might run: `python gen_data.py C:\Users\you\code\d4data C:\Users\you\code\Diablo4Companion`
If you do not see the new data you're expecting to see, you need to add it to the appropriate custom\_\* file. These files store any additional data that we were not able to find in d4data for any reason.
You can find the custom files in [src/tools/data](/src/tools/data). For example, if you need to add a new aspect, you can add it to custom_aspects_enUS.json.
The only exception is corrections.json, which can be modified directly. If you find a bad TTS name for a unique that is where you fix it.
After adding your custom data, run gen_data again and ensure your asset file looks how you expect. Open a PR after.
================================================
FILE: assets/lang/enUS/affixes.json
================================================
{
"abyss_damage": "abyss damage",
"advance_resource_generation": "advance resource generation",
"aegis_cooldown_reduction": "aegis cooldown reduction",
"agility_cooldown_reduction": "agility cooldown reduction",
"agility_damage": "agility damage",
"all_damage_multiplier": "all damage multiplier",
"all_stats": "all stats",
"all_stats_per_ferocity_or_resolve_stack": "all stats per ferocity or resolve stack",
"ancient_damage": "ancient damage",
"anima_of_the_forest_grants_attack_speed": "anima of the forest grants attack speed",
"arbiter_duration": "arbiter duration",
"arbiter_of_justice_cooldown_reduction": "arbiter of justice cooldown reduction",
"archfiend_damage": "archfiend damage",
"armor": "armor",
"armor_and_resistance_to_all_elements_while_you_have_three_or_more_growth_&_decay_witch_powers_equipped_you_gain_maximum_life_and_unhindered": "armor and resistance to all elements while you have three or more growth & decay witch powers equipped you gain maximum life, and unhindered",
"armor_in_arbiter_form": "armor in arbiter form",
"armor_while_in_human_form": "armor while in human form",
"at_level_)": "at level )",
"attack_speed": "attack speed",
"attack_speed_for_seconds_after_casting_a_defensive_skill": "attack speed for seconds after casting a defensive skill",
"attack_speed_for_seconds_after_dodging_an_attack": "attack speed for seconds after dodging an attack",
"attack_speed_while_berserking": "attack speed while berserking",
"attacks_reduce_evades_cooldown_by_seconds": "attacks reduce evades cooldown by seconds",
"attacks_reduce_ultimate_cooldown_by_seconds": "attacks reduce ultimate cooldown by seconds",
"aura_cooldown_reduction": "aura cooldown reduction",
"aura_enhancement_potency": "aura enhancement potency",
"aura_potency": "aura potency",
"ball_lightning_can_be_cast_while_moving": "ball lightning can be cast while moving",
"ball_lightning_projectile_speed": "ball lightning projectile speed",
"barrier_generation": "barrier generation",
"basic_attack_speed": "basic attack speed",
"basic_damage": "basic damage",
"basic_lucky_hit_chance": "basic lucky hit chance",
"basic_resource_generation": "basic resource generation",
"berserking_duration": "berserking duration",
"bleeding_damage": "bleeding damage",
"blight_chill_potency": "blight chill potency",
"blizzard_damage": "blizzard damage",
"block_chance": "block chance",
"blood_attack_speed": "blood attack speed",
"blood_damage": "blood damage",
"blood_howl_cooldown_reduction": "blood howl cooldown reduction",
"blood_howl_grants_stealth_for_seconds": "blood howl grants stealth for seconds",
"blood_mist_cooldown_reduction": "blood mist cooldown reduction",
"blood_orb_healing": "blood orb healing",
"blood_orbs_restore_essence": "blood orbs restore essence",
"blood_surge_drains_times_from_elites": "blood surge drains times from elites",
"blood_wave_cooldown_reduction": "blood wave cooldown reduction",
"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_storm_duration": "bone storm duration",
"boulder_cooldown_reduction": "boulder cooldown reduction",
"boulder_damage": "boulder damage",
"brandish_resource_generation": "brandish resource generation",
"brawling_cooldown_reduction": "brawling cooldown reduction",
"brawling_damage": "brawling damage",
"burning_damage": "burning damage",
"call_of_the_ancients_cooldown_reduction": "call of the ancients cooldown reduction",
"caltrops_cooldown_reduction": "caltrops cooldown reduction",
"casted_hydras_have_heads": "casted hydras have heads",
"casting_blood_wave_fortifies_you_for_maximum_life": "casting blood wave fortifies you for maximum life",
"casting_bone_spear_reduces_blood_waves_cooldown_by_seconds": "casting bone spear reduces blood waves cooldown by seconds",
"casting_justice_skills_restores_primary_resource": "casting justice skills restores primary resource",
"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_valor_skills_restores_primary_resource": "casting valor 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",
"centipede_damage": "centipede damage",
"challenging_shout_cooldown_reduction": "challenging shout cooldown reduction",
"chance_for_arbiter_to_deal_double_damage": "chance for arbiter to deal double damage",
"chance_for_army_of_the_dead_to_deal_double_damage": "chance for army of the dead to deal double damage",
"chance_for_ball_lightning_projectiles_to_cast_twice_is_converted_to_chance_to_cast_a_super_ball_lightning": "chance for ball lightning projectiles to cast twice is converted to chance to cast a super ball lightning",
"chance_for_basic_skills_to_deal_double_damage": "chance for basic skills to deal double damage",
"chance_for_blood_lance_to_deal_double_damage": "chance for blood lance to deal double damage",
"chance_for_bone_storm_to_deal_double_damage": "chance for bone storm to deal double damage",
"chance_for_brandish_to_deal_double_damage": "chance for brandish to deal double damage",
"chance_for_clash_to_deal_double_damage": "chance for clash to deal double damage",
"chance_for_concussive_stomp_to_extra_hit": "chance for concussive stomp to extra hit",
"chance_for_core_skills_to_hit_twice": "chance for core skills to hit twice",
"chance_for_corpse_explosion_to_deal_double_damage": "chance for corpse explosion to deal double damage",
"chance_for_incinerate_to_deal_double_damage": "chance for incinerate to deal double damage",
"chance_for_judgement_to_deal_double_damage": "chance for judgement to deal double damage",
"chance_for_minion_attacks_to_fortify_you_for_maximum_life": "chance for minion attacks to fortify you for maximum life",
"chance_for_payback_to_deal_double_damage": "chance for payback to deal double damage",
"chance_for_pestilent_swarm_to_deal_double_damage": "chance for pestilent swarm to deal double damage",
"chance_for_potency_skills_to_deal_double_damage": "chance for potency skills to deal double damage",
"chance_for_projectiles_to_cast_twice": "chance for projectiles to cast twice",
"chance_for_rapid_fire_projectiles_to_cast_twice": "chance for rapid fire projectiles to cast twice",
"chance_for_ravens_to_deal_double_damage": "chance for ravens to deal double damage",
"chance_for_retribution_to_deal_double_damage": "chance for retribution to deal double damage",
"chance_for_rock_splitter_to_deal_double_damage": "chance for rock splitter to deal double damage",
"chance_for_rushing_claw_to_deal_double_damage": "chance for rushing claw to deal double damage",
"chance_for_sever_to_deal_double_damage": "chance for sever to deal double damage",
"chance_for_shield_bash_to_deal_double_damage": "chance for shield bash to deal double damage",
"chance_for_shield_charge_to_deal_double_damage": "chance for shield charge to deal double damage",
"chance_for_soar_to_deal_double_damage": "chance for soar to deal double damage",
"chance_for_soulrift_to_deal_double_damage": "chance for soulrift to deal double damage",
"chance_for_spear_of_the_heavens_to_deal_double_damage": "chance for spear of the heavens to deal double damage",
"chance_for_the_devourer_to_deal_double_damage": "chance for the devourer to deal double damage",
"chance_for_the_hunter_to_deal_double_damage": "chance for the hunter to deal double damage",
"chance_for_the_protector_to_deal_double_damage": "chance for the protector to deal double damage",
"chance_for_the_seeker_to_deal_double_damage": "chance for the seeker to deal double damage",
"chance_for_thrash_to_deal_double_damage": "chance for thrash to deal double damage",
"chance_for_thunderspike_to_deal_double_damage": "chance for thunderspike to deal double damage",
"chance_for_vortex_to_extra_hit": "chance for vortex to extra hit",
"chance_for_withering_fist_to_deal_double_damage": "chance for withering fist to deal double damage",
"chance_for_zeal_to_deal_double_damage": "chance for zeal to deal double damage",
"chance_to_cluck_thrice": "chance to cluck thrice",
"chance_when_struck_to_fortify_for_life": "chance when struck to fortify for life",
"chance_when_struck_to_gain_life_as_barrier_for_seconds": "chance when struck to gain life as barrier for seconds",
"charge_cooldown_reduction": "charge cooldown reduction",
"charge_damage": "charge damage",
"chill_slow_potency": "chill slow potency",
"clash_resource_generation": "clash resource generation",
"cold_damage": "cold damage",
"cold_damage_multiplier": "cold damage multiplier",
"cold_mage_attack_speed": "cold mage attack speed",
"cold_resistance": "cold resistance",
"companion_cooldown_reduction": "companion cooldown reduction",
"companion_damage": "companion damage",
"concealment_cooldown_reduction": "concealment cooldown reduction",
"condemn_cooldown_reduction": "condemn cooldown reduction",
"conjuration_cooldowns_are_reduced_by_seconds_when_a_frozen_orb_explodes": "conjuration cooldowns are reduced by seconds when a frozen orb explodes",
"conjuration_damage": "conjuration damage",
"consecration_cooldown_reduction": "consecration cooldown reduction",
"cooldown_reduction": "cooldown reduction",
"core_attack_speed": "core attack speed",
"core_damage": "core damage",
"core_resource_cost_reduction": "core resource cost reduction",
"corpse_attack_speed": "corpse attack speed",
"corpse_damage": "corpse damage",
"corpse_explosion_damage": "corpse explosion damage",
"corpse_explosion_fears_and_slows_for_seconds": "corpse explosion fears and slows for seconds",
"corpse_tendrils_damage": "corpse tendrils damage",
"corrupting_damage": "corrupting damage",
"counterattack_charges": "counterattack charges",
"crackling_energy_damage": "crackling energy damage",
"critical_strike_and_vulnerable_damage": "critical strike and vulnerable damage",
"critical_strike_chance": "critical strike chance",
"critical_strike_chance_against_chilled_enemies": "critical strike chance against chilled enemies",
"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_feared_enemies": "critical strike chance against feared enemies",
"critical_strike_chance_against_injured_enemies": "critical strike chance against injured enemies",
"critical_strike_chance_against_stunned_enemies": "critical strike chance against stunned enemies",
"critical_strike_chance_to_each_enhanced_rapid_fire_bonus": "critical strike chance to each enhanced rapid fire bonus",
"critical_strike_damage": "critical strike damage",
"critical_strike_damage_multiplier": "critical strike damage multiplier",
"crowd_control_duration": "crowd control duration",
"crowd_control_duration_lucky_hit_up_to_a_chance_to_heal_life": "crowd control duration lucky hit up to a chance to heal life",
"curse_duration": "curse duration",
"cutthroat_attack_speed": "cutthroat attack speed",
"cutthroat_critical_strike_chance": "cutthroat critical strike chance",
"cutthroat_critical_strike_damage": "cutthroat critical strike damage",
"cutthroat_damage": "cutthroat damage",
"cyclone_armor_cooldown_reduction": "cyclone armor cooldown reduction",
"cyclone_armor_damage": "cyclone armor damage",
"damage": "damage",
"damage_for_seconds_after_dodging_an_attack": "damage for seconds after dodging an attack",
"damage_for_seconds_after_gaining_resolve": "damage for seconds after gaining resolve",
"damage_for_seconds_after_killing_an_elite": "damage for seconds after killing an elite",
"damage_for_seconds_after_picking_up_a_blood_orb": "damage for seconds after picking up a blood orb",
"damage_on_next_attack_after_entering_stealth": "damage on next attack after entering stealth",
"damage_over_time": "damage over time",
"damage_over_time_duration": "damage over time duration",
"damage_over_time_multiplier": "damage over time multiplier",
"damage_per_combo_point_spent": "damage per combo point spent",
"damage_per_overpower_stack": "damage per overpower stack",
"damage_reduction": "damage reduction",
"damage_reduction_for_each_active_ball_lightning": "damage reduction for each active ball lightning",
"damage_reduction_for_your_summons": "damage reduction for your summons",
"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",
"damage_reduction_from_corrupted_enemies": "damage reduction from corrupted enemies",
"damage_reduction_from_distant_enemies": "damage reduction from distant enemies",
"damage_reduction_from_elites": "damage reduction from elites",
"damage_reduction_from_enemies_affected_by_blood_skills": "damage reduction from enemies affected by blood skills",
"damage_reduction_from_enemies_affected_by_curse_skills": "damage reduction from enemies affected by curse skills",
"damage_reduction_from_enemies_affected_by_trap_skills": "damage reduction from enemies affected by trap skills",
"damage_reduction_from_poisoned_enemies": "damage reduction from poisoned enemies",
"damage_reduction_per_crackling_energy_charge": "damage reduction per crackling energy charge",
"damage_reduction_while_fortified": "damage reduction while fortified",
"damage_reduction_while_healthy": "damage reduction while healthy",
"damage_reduction_while_injured": "damage reduction while injured",
"damage_reduction_while_standing_still": "damage reduction while standing still",
"damage_reduction_while_unstoppable": "damage reduction while unstoppable",
"damage_reduction_while_you_have_a_barrier": "damage reduction while you have a barrier",
"damage_to_angels_and_demons": "damage to angels and demons",
"damage_to_bleeding_enemies": "damage to bleeding enemies",
"damage_to_burning_enemies": "damage to burning enemies",
"damage_to_chilled_enemies": "damage to chilled enemies",
"damage_to_close_enemies": "damage to close enemies",
"damage_to_corrupted_enemies": "damage to corrupted enemies",
"damage_to_crowd_controlled_enemies": "damage to crowd controlled enemies",
"damage_to_cursed_enemies": "damage to cursed enemies",
"damage_to_dazed_enemies": "damage to dazed enemies",
"damage_to_distant_enemies": "damage to distant enemies",
"damage_to_elites": "damage to elites",
"damage_to_frozen_enemies": "damage to frozen enemies",
"damage_to_healthy_enemies": "damage to healthy enemies",
"damage_to_immobilized_enemies": "damage to immobilized enemies",
"damage_to_injured_enemies": "damage to injured enemies",
"damage_to_judged_enemies": "damage to judged enemies",
"damage_to_knockeddown_enemies": "damage to knockeddown enemies",
"damage_to_poisoned_enemies": "damage to poisoned enemies",
"damage_to_poultry": "damage to poultry",
"damage_to_slowed_enemies": "damage to slowed enemies",
"damage_to_stunned_enemies": "damage to stunned enemies",
"damage_to_trapped_enemies": "damage to trapped enemies",
"damage_to_weakened_enemies": "damage to weakened enemies",
"damage_when_spending_resolve": "damage when spending resolve",
"damage_when_swapping_weapons": "damage when swapping weapons",
"damage_while_berserking": "damage while berserking",
"damage_while_fortified": "damage while fortified",
"damage_while_healthy": "damage while healthy",
"damage_while_in_arbiter_form": "damage while in arbiter form",
"damage_while_in_human_form": "damage while in human form",
"damage_while_iron_maelstrom_is_active": "damage while iron maelstrom is active",
"damage_while_shadowform_is_active": "damage while shadowform is active",
"damage_while_shapeshifted": "damage while shapeshifted",
"damage_while_war_cry_is_active": "damage while war cry is active",
"damage_while_wrath_of_the_berserker_is_active": "damage while wrath of the berserker is active",
"damage_with_dualwielded_weapons": "damage with dualwielded weapons",
"damage_with_ranged_weapons": "damage with ranged weapons",
"damage_with_twohanded_bludgeoning_weapons": "damage with twohanded bludgeoning weapons",
"damage_with_twohanded_slashing_weapons": "damage with twohanded slashing weapons",
"dark_shroud_cooldown_reduction": "dark shroud cooldown reduction",
"darkness_damage": "darkness damage",
"dash_cooldown_reduction": "dash cooldown reduction",
"dash_damage": "dash damage",
"death_blow_cooldown_reduction": "death blow cooldown reduction",
"death_blow_damage": "death blow damage",
"death_trap_cooldown_reduction": "death trap cooldown reduction",
"debilitating_roar_cooldown_reduction": "debilitating roar cooldown reduction",
"deep_freeze_cooldown_reduction": "deep freeze cooldown reduction",
"defensive_cooldown_reduction": "defensive cooldown reduction",
"defensive_damage": "defensive damage",
"defiance_aura_potency": "defiance aura potency",
"demonform_damage_bonus": "demonform damage bonus",
"demonology_damage": "demonology damage",
"desecrated_ground_damage": "desecrated ground damage",
"dexterity": "dexterity",
"disciple_damage": "disciple damage",
"dodge_chance": "dodge chance",
"dodge_chance_against_close_enemies": "dodge chance against close enemies",
"dodge_chance_against_distant_enemies": "dodge chance against distant enemies",
"dodge_chance_while_channeling_dance_of_knives": "dodge chance while channeling dance of knives",
"drinking_a_potion_grants_movement_speed_for_seconds": "drinking a potion grants movement speed for seconds",
"dust_devil_damage": "dust devil damage",
"eagle_damage": "eagle damage",
"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",
"earth_lucky_hit_chance": "earth lucky hit chance",
"earthen_bulwark_cooldown_reduction": "earthen bulwark cooldown reduction",
"earthquake_damage": "earthquake damage",
"enchantment_damage": "enchantment damage",
"energy_cost_reduction": "energy cost reduction",
"energy_on_kill": "energy on kill",
"energy_regeneration": "energy regeneration",
"energy_when_a_stun_grenade_explodes": "energy when a stun grenade explodes",
"enhanced_rupture_explosion_size": "enhanced rupture explosion size",
"essence_cost_reduction": "essence cost reduction",
"essence_on_hit": "essence on hit",
"essence_on_kill": "essence on kill",
"essence_per_enemy_drained_by_blood_surge": "essence per enemy drained by blood surge",
"essence_regeneration": "essence regeneration",
"evade_cooldown_reduction": "evade cooldown reduction",
"evade_grants_attack_speed_for_seconds": "evade grants attack speed for seconds",
"evade_grants_movement_speed_for_seconds": "evade grants movement speed for seconds",
"evade_grants_unhindered_for_seconds": "evade grants unhindered for seconds",
"evade_leaves_behind_desecrated_ground_for_seconds": "evade leaves behind desecrated ground for seconds",
"faith_on_kill": "faith on kill",
"faith_regeneration": "faith regeneration",
"falling_star_cooldown_reduction": "falling star cooldown reduction",
"familiar_damage": "familiar damage",
"familiar_lucky_hit_chance": "familiar lucky hit chance",
"fanaticism_aura_potency": "fanaticism aura potency",
"feast_every_kills_chains_hook_nearby_enemies": "feast every kills, chains hook nearby enemies",
"feast_every_kills_gain_berserking_for_seconds": "feast every kills, gain berserking for seconds",
"feast_every_kills_release_a_bloodsplosion_for_damage": "feast every kills, release a bloodsplosion for damage",
"feast_every_kills_reset_random_cooldowns": "feast every kills, reset random cooldowns",
"feast_every_kills_restore_of_your_maximum_primary_resource": "feast every kills, restore of your maximum primary resource",
"feast_every_kills_savagely_bite_times_for_damage_and_apply_vulnerable": "feast every kills, savagely bite times for damage and apply vulnerable",
"feast_every_kills_your_next_core_skill_cast_deals_additional_damage": "feast every kills, your next core skill cast deals additional damage",
"ferocity_potency": "ferocity potency",
"fire_and_cold_damage": "fire and cold damage",
"fire_damage": "fire damage",
"fire_damage_multiplier": "fire damage multiplier",
"fire_damage_ranks_of_the_inner_flames_passive": "fire damage ranks of the inner flames passive",
"fire_lucky_hit_chance": "fire lucky hit chance",
"fire_resistance": "fire resistance",
"fireball_attack_speed": "fireball attack speed",
"fireball_projectile_speed": "fireball projectile speed",
"focus_cooldown_reduction": "focus cooldown reduction",
"focus_damage": "focus damage",
"fortify_generation": "fortify generation",
"fortress_cooldown_reduction": "fortress cooldown reduction",
"freeze_duration": "freeze duration",
"frost_critical_strike_chance": "frost critical strike chance",
"frost_damage": "frost damage",
"frost_nova_cooldown_reduction": "frost nova cooldown reduction",
"fury_cost_reduction": "fury cost reduction",
"fury_on_kill": "fury on kill",
"fury_regeneration": "fury regeneration",
"gem_strength_in_this_item": "gem strength in this item",
"gold_drop_rate": "gold drop rate",
"golem_active_cooldown_reduction": "golem active cooldown reduction",
"golem_damage": "golem damage",
"golems_inherit_of_your_thorns": "golems inherit of your thorns",
"gorilla_damage": "gorilla damage",
"grenade_damage": "grenade damage",
"grizzly_rage_cooldown_reduction": "grizzly rage cooldown reduction",
"ground_stomp_cooldown_reduction": "ground stomp cooldown reduction",
"ground_stomp_damage": "ground stomp damage",
"hammer_of_the_ancients_damage_for_seconds_after_an_earthquake_explodes": "hammer of the ancients damage for seconds after an earthquake explodes",
"healing_received": "healing received",
"heavens_fury_cooldown_reduction": "heavens fury cooldown reduction",
"hellfire_damage": "hellfire damage",
"hewed_flesh_grants_maximum_life_as_barrier_for_seconds": "hewed flesh grants maximum life as barrier for seconds",
"holy_bolt_resource_generation": "holy bolt resource generation",
"holy_damage": "holy damage",
"holy_damage_multiplier": "holy damage multiplier",
"holy_light_aura_potency": "holy light aura potency",
"human_damage": "human damage",
"hunger_after_you_cast_a_basic_skill_chance_for_kill_to_your_kill_streak": "hunger after you cast a basic skill, chance for kill to your kill streak",
"hunger_after_you_cast_a_cooldown_kill_to_your_kill_streak": "hunger after you cast a cooldown, kill to your kill streak",
"hunger_after_you_kill_an_enemy_chance_for_kill_to_your_kill_streak": "hunger after you kill an enemy, chance for kill to your kill streak",
"hunger_every_resource_chance_for_kill_to_your_kill_streak": "hunger every resource, chance for kill to your kill streak",
"hunger_increased_chance_for_additional_gold_during_kill_streaks": "hunger increased chance for additional gold during kill streaks",
"hunger_increased_chance_for_additional_salvage_materials_during_your_kill_streaks": "hunger increased chance for additional salvage materials during your kill streaks",
"hunger_increased_chance_for_feast_items_during_your_kill_streaks": "hunger increased chance for feast items during your kill streaks",
"hunger_increased_chance_for_hunger_items_during_kill_streaks": "hunger increased chance for hunger items during kill streaks",
"hunger_increased_chance_for_rampage_items_during_kill_streaks": "hunger increased chance for rampage items during kill streaks",
"hunger_increased_chance_for_runes_during_your_kill_streaks": "hunger increased chance for runes during your kill streaks",
"hunger_increased_experience_from_kill_streaks": "hunger increased experience from kill streaks",
"hunger_increased_reputation_from_kill_streaks": "hunger increased reputation from kill streaks",
"hunger_lucky_hit_up_to_a_chance_for_kill_to_your_kill_streak": "hunger lucky hit up to a chance for kill to your kill streak",
"hurricane_cooldown_reduction": "hurricane cooldown reduction",
"hurricane_damage": "hurricane damage",
"hydra_damage": "hydra damage",
"hydra_lucky_hit_chance": "hydra lucky hit chance",
"hydra_resource_cost_reduction": "hydra resource cost reduction",
"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",
"ice_spikes_freeze_enemies_for_seconds": "ice spikes freeze enemies for seconds",
"imbued_critical_strike_damage": "imbued critical strike damage",
"imbued_damage": "imbued damage",
"imbuement_cooldown_reduction": "imbuement cooldown reduction",
"imbuement_damage": "imbuement damage",
"imbuement_potency": "imbuement potency",
"immobilize_duration": "immobilize duration",
"impairment_reduction": "impairment reduction",
"incarnate_cooldown_reduction": "incarnate cooldown reduction",
"indestructible": "indestructible",
"inferno_cooldown_reduction": "inferno cooldown reduction",
"inner_sight_duration": "inner sight duration",
"intelligence": "intelligence",
"invigorating_strike_energy_regeneration": "invigorating strike energy regeneration",
"iron_maelstrom_cooldown_reduction": "iron maelstrom cooldown reduction",
"iron_maiden_damage": "iron maiden damage",
"iron_skin_cooldown_reduction": "iron skin cooldown reduction",
"item_quality": "item quality",
"jaguar_damage": "jaguar damage",
"judicator_damage": "judicator damage",
"juggernaut_damage": "juggernaut damage",
"justice_cooldown_reduction": "justice cooldown reduction",
"justice_damage": "justice damage",
"kick_cooldown_reduction": "kick cooldown reduction",
"kick_damage": "kick damage",
"lacerate_cooldown_reduction": "lacerate cooldown reduction",
"lacerate_damage": "lacerate damage",
"leap_cooldown_reduction": "leap cooldown reduction",
"leap_damage": "leap damage",
"life_on_hit": "life on hit",
"life_on_kill": "life on kill",
"life_per_seconds": "life per seconds",
"life_regeneration": "life regeneration",
"life_steal": "life steal",
"lightning_bolt_damage": "lightning bolt damage",
"lightning_critical_strike_damage": "lightning critical strike damage",
"lightning_damage": "lightning damage",
"lightning_damage_multiplier": "lightning damage multiplier",
"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",
"lucky_hit_chance": "lucky hit chance",
"lucky_hit_chance_while_you_have_a_barrier": "lucky hit chance while you have a barrier",
"lucky_hit_critical_strikes_have_up_to_a_chance_to_daze_for_seconds": "lucky hit critical strikes have up to a chance to daze for seconds",
"lucky_hit_critical_strikes_have_up_to_a_chance_to_immobilize_for_seconds": "lucky hit critical strikes have up to a chance to immobilize for seconds",
"lucky_hit_critical_strikes_have_up_to_a_chance_to_slow_for_seconds": "lucky hit critical strikes have up to a chance to slow for seconds",
"lucky_hit_critical_strikes_have_up_to_a_chance_to_stun_for_seconds": "lucky hit critical strikes have up to a chance to stun for seconds",
"lucky_hit_up_to_a_bleeding_damage_over_seconds": "lucky hit up to a bleeding damage over seconds",
"lucky_hit_up_to_a_chance_to_apply_a_random_crowd_control_effect_for_seconds": "lucky hit up to a chance to apply a random crowd control effect for seconds",
"lucky_hit_up_to_a_chance_to_become_berserking": "lucky hit up to a chance to become berserking",
"lucky_hit_up_to_a_chance_to_chill_for_seconds": "lucky hit up to a chance to chill for seconds",
"lucky_hit_up_to_a_chance_to_daze_for_seconds": "lucky hit up to a chance to daze for seconds",
"lucky_hit_up_to_a_chance_to_deal_cold_damage": "lucky hit up to a chance to deal cold damage",
"lucky_hit_up_to_a_chance_to_deal_fire_damage": "lucky hit up to a chance to deal fire damage",
"lucky_hit_up_to_a_chance_to_deal_holy_damage": "lucky hit up to a chance to deal holy damage",
"lucky_hit_up_to_a_chance_to_deal_lightning_damage": "lucky hit up to a chance to deal lightning damage",
"lucky_hit_up_to_a_chance_to_deal_physical_damage": "lucky hit up to a chance to deal physical damage",
"lucky_hit_up_to_a_chance_to_deal_poison_damage": "lucky hit up to a chance to deal poison damage",
"lucky_hit_up_to_a_chance_to_deal_shadow_damage": "lucky hit up to a chance to deal shadow damage",
"lucky_hit_up_to_a_chance_to_execute_injured_nonelites": "lucky hit up to a chance to execute injured nonelites",
"lucky_hit_up_to_a_chance_to_fear_for_seconds": "lucky hit up to a chance to fear for seconds",
"lucky_hit_up_to_a_chance_to_freeze_for_seconds": "lucky hit up to a chance to freeze for seconds",
"lucky_hit_up_to_a_chance_to_gain_a_stack_of_frenzy": "lucky hit up to a chance to gain a stack of frenzy",
"lucky_hit_up_to_a_chance_to_gain_damage_for_seconds": "lucky hit up to a chance to gain damage for seconds",
"lucky_hit_up_to_a_chance_to_heal_life": "lucky hit up to a chance to heal life",
"lucky_hit_up_to_a_chance_to_immobilize_for_seconds": "lucky hit up to a chance to immobilize for seconds",
"lucky_hit_up_to_a_chance_to_knockback_for_seconds": "lucky hit up to a chance to knockback for seconds",
"lucky_hit_up_to_a_chance_to_make_enemies_vulnerable_for_seconds": "lucky hit up to a chance to make enemies vulnerable for seconds",
"lucky_hit_up_to_a_chance_to_restore_primary_resource": "lucky hit up to a chance to restore primary resource",
"lucky_hit_up_to_a_chance_to_slow_for_seconds": "lucky hit up to a chance to slow for seconds",
"lucky_hit_up_to_a_chance_to_stun_for_seconds": "lucky hit up to a chance to stun for seconds",
"lucky_hit_up_to_a_chance_to_taunt_for_seconds": "lucky hit up to a chance to taunt for seconds",
"lucky_hit_up_to_a_chance_to_weaken_for_seconds": "lucky hit up to a chance to weaken for seconds",
"lucky_hit_up_to_a_damage_for_seconds": "lucky hit up to a damage for seconds",
"lunging_strike_healing": "lunging strike healing",
"macabre_damage": "macabre damage",
"main_hand_weapon_damage": "main hand weapon damage",
"mana_cost_reduction": "mana cost reduction",
"mana_on_kill": "mana on kill",
"mana_regeneration": "mana regeneration",
"marksman_attack_speed_per_precison_stack": "marksman attack speed per precison stack",
"marksman_critical_strike_chance": "marksman critical strike chance",
"marksman_critical_strike_damage": "marksman critical strike damage",
"marksman_damage": "marksman damage",
"mastery_damage": "mastery damage",
"maximum_energy": "maximum energy",
"maximum_essence": "maximum essence",
"maximum_evade_charges": "maximum evade charges",
"maximum_fury": "maximum fury",
"maximum_life": "maximum life",
"maximum_mana": "maximum mana",
"maximum_poison_traps": "maximum poison traps",
"maximum_resolve_stacks": "maximum resolve stacks",
"maximum_resource": "maximum resource",
"maximum_spirit": "maximum spirit",
"maximum_summon_life": "maximum summon life",
"maximum_vigor": "maximum vigor",
"meteor_size": "meteor size",
"minions_inherit_of_your_thorns": "minions inherit of your thorns",
"mobility_cooldown_reduction": "mobility cooldown reduction",
"mobility_damage": "mobility damage",
"mobility_skills_grant_movement_speed_for_seconds": "mobility skills grant movement speed for seconds",
"movement_speed": "movement speed",
"movement_speed_for_seconds_after_killing_an_elite": "movement speed for seconds after killing an elite",
"movement_speed_for_seconds_after_killing_an_enemy": "movement speed for seconds after killing an enemy",
"movement_speed_for_seconds_after_picking_up_crackling_energy": "movement speed for seconds after picking up crackling energy",
"movement_speed_while_berserking": "movement speed while berserking",
"movement_speed_while_cataclysm_is_active": "movement speed while cataclysm is active",
"movement_speed_while_hurricane_is_active": "movement speed while hurricane is active",
"movement_speed_while_in_human_form": "movement speed while in human form",
"movement_speed_while_shapeshifted_into_a_werewolf": "movement speed while shapeshifted into a werewolf",
"movement_speed_while_the_inner_sight_gauge_is_full": "movement speed while the inner sight gauge is full",
"mystic_circle_potency": "mystic circle potency",
"nature_magic_cooldown_reduction": "nature magic cooldown reduction",
"nature_magic_skill_cooldown_reduction": "nature magic skill cooldown reduction",
"nonphysical_damage": "nonphysical damage",
"occult_damage": "occult damage",
"overpower_chance": "overpower chance",
"overpower_critical_damage": "overpower critical damage",
"pestilent_swarm_damage": "pestilent swarm damage",
"petrify_cooldown_reduction": "petrify cooldown reduction",
"physical_critical_strike_chance_against_elites": "physical critical strike chance against elites",
"physical_damage": "physical damage",
"physical_damage_multiplier": "physical damage multiplier",
"physical_resistance": "physical resistance",
"pickup_radius": "pickup radius",
"poison_creeper_cooldown_reduction": "poison creeper cooldown reduction",
"poison_creeper_damage": "poison creeper damage",
"poison_damage": "poison damage",
"poison_damage_multiplier": "poison damage multiplier",
"poison_damage_over_time_duration": "poison damage over time duration",
"poison_resistance": "poison resistance",
"poison_trap_cooldown_reduction": "poison trap cooldown reduction",
"poisoning_damage": "poisoning damage",
"potency_cooldown_reduction": "potency cooldown reduction",
"potency_damage": "potency damage",
"potion_capacity": "potion capacity",
"potion_drop_rate": "potion drop rate",
"potion_healing": "potion healing",
"primary_centipede_spirit_hall_damage": "primary centipede spirit hall damage",
"primary_eagle_spirit_hall_damage": "primary eagle spirit hall damage",
"primary_gorilla_spirit_hall_damage": "primary gorilla spirit hall damage",
"primary_jaguar_spirit_hall_damage": "primary jaguar spirit hall damage",
"puncture_resource_generation": "puncture resource generation",
"purify_cooldown_reduction": "purify cooldown reduction",
"pyromancy_attack_speed": "pyromancy attack speed",
"pyromancy_critical_strike_damage": "pyromancy critical strike damage",
"pyromancy_damage": "pyromancy damage",
"rabies_cooldown_reduction": "rabies cooldown reduction",
"rabies_damage": "rabies damage",
"rain_of_arrows_cooldown_reduction": "rain of arrows cooldown reduction",
"rain_of_arrows_damage": "rain of arrows damage",
"rain_of_arrows_skill_cooldown_reduction": "rain of arrows skill cooldown reduction",
"rampage_attack_speed_per_kill_streak_tier": "rampage attack speed per kill streak tier",
"rampage_cooldown_reduction_per_kill_streak_tier": "rampage cooldown reduction per kill streak tier",
"rampage_critical_strike_chance_per_kill_streak_tier": "rampage critical strike chance per kill streak tier",
"rampage_dexterity_per_kill_streak_tier": "rampage dexterity per kill streak tier",
"rampage_intelligence_per_kill_streak_tier": "rampage intelligence per kill streak tier",
"rampage_life_on_hit_per_kill_streak_tier": "rampage life on hit per kill streak tier",
"rampage_lucky_hit_chance_per_kill_streak_tier": "rampage lucky hit chance per kill streak tier",
"rampage_maximum_life_per_kill_streak_tier": "rampage maximum life per kill streak tier",
"rampage_movement_speed_per_kill_streak_tier": "rampage movement speed per kill streak tier",
"rampage_resource_cost_reduction_per_kill_streak_tier": "rampage resource cost reduction per kill streak tier",
"rampage_strength_per_kill_streak_tier": "rampage strength per kill streak tier",
"rampage_willpower_per_kill_streak_tier": "rampage willpower per kill streak tier",
"rank_of_all_agility_skills": "rank of all agility skills",
"ranks_of_the_aggressive_resistance_passive": "ranks of the aggressive resistance passive",
"ranks_of_the_concussive_passive": "ranks of the concussive passive",
"ranks_of_the_heightened_senses_passive": "ranks of the heightened senses passive",
"ranks_of_the_hewed_flesh_passive": "ranks of the hewed flesh passive",
"ravager_on_kill_duration_extension": "ravager on kill duration extension",
"ravens_attack_speed": "ravens attack speed",
"ravens_cooldown_reduction": "ravens cooldown reduction",
"ravens_damage": "ravens damage",
"razor_wings_charges": "razor wings charges",
"resistance_to_all_elements": "resistance to all elements",
"resolve_generated": "resolve generated",
"resource_cost_reduction": "resource cost reduction",
"resource_generation": "resource generation",
"resource_generation_and_maximum": "resource generation and maximum",
"resource_generation_while_wielding_a_scythe": "resource generation while wielding a scythe",
"resource_generation_while_wielding_a_shield": "resource generation while wielding a shield",
"resource_generation_with_dualwielded_weapons": "resource generation with dualwielded weapons",
"resource_generation_with_polearms": "resource generation with polearms",
"resource_generation_with_twohanded_bludgeoning_weapons": "resource generation with twohanded bludgeoning weapons",
"resource_generation_with_twohanded_slashing_weapons": "resource generation with twohanded slashing weapons",
"resource_generation_with_twohanded_weapons": "resource generation with twohanded weapons",
"resource_on_hit": "resource on hit",
"resource_regeneration": "resource regeneration",
"rock_splitter_resource_generation": "rock splitter resource generation",
"rupture_cooldown_reduction": "rupture cooldown reduction",
"rupture_damage": "rupture damage",
"rushing_claw_charges": "rushing claw charges",
"scourge_poisoning_duration": "scourge poisoning duration",
"sever_size": "sever size",
"shade_damage": "shade damage",
"shadow_clone_cooldown_reduction": "shadow clone cooldown reduction",
"shadow_clone_damage": "shadow clone damage",
"shadow_clones_execute_injured_nonelite_enemies": "shadow clones execute injured nonelite enemies",
"shadow_damage": "shadow damage",
"shadow_damage_multiplier": "shadow damage multiplier",
"shadow_lucky_hit_chance": "shadow lucky hit chance",
"shadow_resistance": "shadow resistance",
"shadow_step_cooldown_reduction": "shadow step cooldown reduction",
"shadow_step_damage": "shadow step damage",
"shapeshifting_attack_speed": "shapeshifting attack speed",
"shield_charge_cooldown_reduction": "shield charge cooldown reduction",
"shock_critical_strike_chance": "shock critical strike chance",
"shock_critical_strike_damage": "shock critical strike damage",
"shock_damage": "shock damage",
"shout_cooldown_reduction": "shout cooldown reduction",
"shred_critical_strike_chance": "shred critical strike chance",
"shrine_buff_duration": "shrine buff duration",
"sigil_damage": "sigil damage",
"sigil_duration": "sigil duration",
"skeletal_mages_inherit_of_your_thorns": "skeletal mages inherit of your thorns",
"skeletal_warriors_inherit_of_your_thorns": "skeletal warriors inherit of your thorns",
"skeleton_mage_damage": "skeleton mage damage",
"slow_duration_reduction": "slow duration reduction",
"smoke_grenade_cooldown_reduction": "smoke grenade cooldown reduction",
"smoke_grenade_damage": "smoke grenade damage",
"soar_cooldown_reduction": "soar cooldown reduction",
"soar_deals_up_to_damage_based_on_distance_traveled": "soar deals up to damage based on distance traveled",
"soar_grants_maximum_life_as_barrier_for_seconds": "soar grants maximum life as barrier for seconds",
"spirit_cost_reduction": "spirit cost reduction",
"spirit_on_kill": "spirit on kill",
"spirit_regeneration": "spirit regeneration",
"steel_grasp_cooldown_reduction": "steel grasp cooldown reduction",
"steel_grasp_damage": "steel grasp damage",
"steel_grasp_stuns_for_seconds": "steel grasp stuns for seconds",
"storm_cooldown_reduction": "storm cooldown reduction",
"storm_critical_strike_chance": "storm critical strike chance",
"storm_damage": "storm damage",
"storm_feather_potency": "storm feather potency",
"storm_strike_chains_to_targets": "storm strike chains to targets",
"strength": "strength",
"stun_duration": "stun duration",
"stun_grenade_damage": "stun grenade damage",
"stun_grenade_size": "stun grenade size",
"subterfuge_cooldown_reduction": "subterfuge cooldown reduction",
"summon_attack_speed": "summon attack speed",
"summon_damage": "summon damage",
"summon_movement_speed": "summon movement speed",
"teleport_cooldown_reduction": "teleport cooldown reduction",
"teleport_damage": "teleport damage",
"the_devourer_cooldown_reduction": "the devourer cooldown reduction",
"the_hunter_cooldown_reduction": "the hunter cooldown reduction",
"the_protector_cooldown_reduction": "the protector cooldown reduction",
"the_seeker_charges": "the seeker charges",
"the_seeker_cooldown_reduction": "the seeker cooldown reduction",
"thorns": "thorns",
"thorns_while_fortified": "thorns while fortified",
"thrash_resource_generation": "thrash resource generation",
"thunderspike_resource_generation": "thunderspike resource generation",
"to_abyss_skills": "to abyss skills",
"to_advance": "to advance",
"to_aegis": "to aegis",
"to_agility_skills": "to agility skills",
"to_all_skills": "to all skills",
"to_ancient_skills": "to ancient skills",
"to_arc_lash": "to arc lash",
"to_archfiend_skills": "to archfiend skills",
"to_armored_hide": "to armored hide",
"to_arrow_storm_skills": "to arrow storm skills",
"to_aura_skills": "to aura skills",
"to_ball_lightning": "to ball lightning",
"to_barrage": "to barrage",
"to_bash": "to bash",
"to_basic_skills": "to basic skills",
"to_blade_shift": "to blade shift",
"to_blazing_scream": "to blazing scream",
"to_blessed_hammer": "to blessed hammer",
"to_blessed_shield": "to blessed shield",
"to_blight": "to blight",
"to_blizzard": "to blizzard",
"to_blood_howl": "to blood howl",
"to_blood_lance": "to blood lance",
"to_blood_mist": "to blood mist",
"to_blood_skills": "to blood skills",
"to_blood_surge": "to blood surge",
"to_bombardment": "to bombardment",
"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",
"to_boulder": "to boulder",
"to_brandish": "to brandish",
"to_brawling_skills": "to brawling skills",
"to_caltrops": "to caltrops",
"to_centipede_skills": "to centipede skills",
"to_chain_lightning": "to chain lightning",
"to_challenging_shout": "to challenging shout",
"to_charge": "to charge",
"to_charged_bolts": "to charged bolts",
"to_clash": "to clash",
"to_claw": "to claw",
"to_cold_imbuement": "to cold imbuement",
"to_combat_skills": "to combat skills",
"to_command_abodian": "to command abodian",
"to_command_aegrom": "to command aegrom",
"to_command_fallen": "to command fallen",
"to_command_laalish": "to command laalish",
"to_command_valloch": "to command valloch",
"to_companion_skills": "to companion skills",
"to_concealment": "to concealment",
"to_concussive_stomp": "to concussive stomp",
"to_condemn": "to condemn",
"to_conjuration_skills": "to conjuration skills",
"to_consecration": "to consecration",
"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_counterattack": "to counterattack",
"to_crushing_hand": "to crushing hand",
"to_curse_skills": "to curse skills",
"to_cutthroat_skills": "to cutthroat skills",
"to_cyclone_armor": "to cyclone armor",
"to_dance_of_knives": "to dance of knives",
"to_dark_prison": "to dark prison",
"to_dark_shroud": "to dark shroud",
"to_darkness_skills": "to darkness skills",
"to_dash": "to dash",
"to_death_blow": "to death blow",
"to_deaths_reach": "to deaths reach",
"to_debilitating_roar": "to debilitating roar",
"to_decompose": "to decompose",
"to_decrepify": "to decrepify",
"to_defensive_skills": "to defensive skills",
"to_defiance_aura": "to defiance aura",
"to_demonology_skills": "to demonology skills",
"to_disciple_skills": "to disciple skills",
"to_divine_lance": "to divine lance",
"to_doom": "to doom",
"to_double_swing": "to double swing",
"to_dread_claws": "to dread claws",
"to_dust_devil_skills": "to dust devil skills",
"to_eagle_skills": "to eagle skills",
"to_earth_skills": "to earth skills",
"to_earth_spike": "to earth spike",
"to_earthen_bulwark": "to earthen bulwark",
"to_earthquake_skills": "to earthquake skills",
"to_falling_star": "to falling star",
"to_familiar": "to familiar",
"to_fanaticism_aura": "to fanaticism aura",
"to_fire_bolt": "to fire bolt",
"to_fireball": "to fireball",
"to_firewall": "to firewall",
"to_flame_shield": "to flame shield",
"to_flay": "to flay",
"to_flurry": "to flurry",
"to_focus_skills": "to focus skills",
"to_forceful_arrow": "to forceful arrow",
"to_frenzy": "to frenzy",
"to_frost_bolt": "to frost bolt",
"to_frost_nova": "to frost nova",
"to_frost_skills": "to frost skills",
"to_frozen_orb": "to frozen orb",
"to_golem": "to golem",
"to_gorilla_skills": "to gorilla skills",
"to_grenade_skills": "to grenade skills",
"to_ground_stomp": "to ground stomp",
"to_hammer_of_the_ancients": "to hammer of the ancients",
"to_heartseeker": "to heartseeker",
"to_hell_fracture": "to hell fracture",
"to_hellfire_skills": "to hellfire skills",
"to_hellion_sting": "to hellion sting",
"to_hemorrhage": "to hemorrhage",
"to_holy_bolt": "to holy bolt",
"to_holy_light_aura": "to holy light aura",
"to_human_skills": "to human skills",
"to_hurricane": "to hurricane",
"to_hydra": "to hydra",
"to_ice_armor": "to ice armor",
"to_ice_blades": "to ice blades",
"to_ice_shards": "to ice shards",
"to_imbuement_skills": "to imbuement skills",
"to_incinerate": "to incinerate",
"to_infernal_breath": "to infernal breath",
"to_invigorating_strike": "to invigorating strike",
"to_iron_maiden": "to iron maiden",
"to_iron_shrapnel_skills": "to iron shrapnel skills",
"to_iron_skin": "to iron skin",
"to_jaguar_skills": "to jaguar skills",
"to_judicator_skills": "to judicator skills",
"to_juggernaut_skills": "to juggernaut skills",
"to_justice_skills": "to justice skills",
"to_kick": "to kick",
"to_landslide": "to landslide",
"to_leap": "to leap",
"to_lightning_spear": "to lightning spear",
"to_lightning_storm": "to lightning storm",
"to_lunging_strike": "to lunging strike",
"to_macabre_skills": "to macabre skills",
"to_marksman_and_cutthroat_skills": "to marksman and cutthroat skills",
"to_marksman_skills": "to marksman skills",
"to_martial_skills": "to martial skills",
"to_mastery_skills": "to mastery skills",
"to_maul": "to maul",
"to_meteor": "to meteor",
"to_mighty_throw": "to mighty throw",
"to_minion_skills": "to minion skills",
"to_mobility_skills": "to mobility skills",
"to_molten_bomb": "to molten bomb",
"to_nature_magic_skills": "to nature magic skills",
"to_nether_step": "to nether step",
"to_occult_skills": "to occult skills",
"to_payback": "to payback",
"to_penetrating_shot": "to penetrating shot",
"to_poison_creeper": "to poison creeper",
"to_poison_imbuement": "to poison imbuement",
"to_poison_trap": "to poison trap",
"to_potency_skills": "to potency skills",
"to_prime_bone_storms_damage_reduction": "to prime bone storms damage reduction",
"to_profane_sentinel": "to profane sentinel",
"to_pulverize": "to pulverize",
"to_puncture": "to puncture",
"to_purify": "to purify",
"to_pyromancy_skills": "to pyromancy skills",
"to_quill_volley": "to quill volley",
"to_rabies": "to rabies",
"to_rake": "to rake",
"to_rally": "to rally",
"to_rallying_cry": "to rallying cry",
"to_rampage": "to rampage",
"to_rapid_fire": "to rapid fire",
"to_ravager": "to ravager",
"to_ravens": "to ravens",
"to_razor_wings": "to razor wings",
"to_reap": "to reap",
"to_rend": "to rend",
"to_rock_splitter": "to rock splitter",
"to_rupture": "to rupture",
"to_rushing_claw": "to rushing claw",
"to_scourge": "to scourge",
"to_sever": "to sever",
"to_shade_skills": "to shade skills",
"to_shadow_imbuement": "to shadow imbuement",
"to_shadow_step": "to shadow step",
"to_shapeshifting_skills": "to shapeshifting skills",
"to_shield_bash": "to shield bash",
"to_shield_charge": "to shield charge",
"to_shock_skills": "to shock skills",
"to_shred": "to shred",
"to_sigil_of_chaos": "to sigil of chaos",
"to_sigil_of_subversion": "to sigil of subversion",
"to_sigil_of_summons": "to sigil of summons",
"to_sigil_skills": "to sigil skills",
"to_skeletal_mage_mastery": "to skeletal mage mastery",
"to_skeleton_mage": "to skeleton mage",
"to_skeleton_warrior": "to skeleton warrior",
"to_slashing_skills": "to slashing skills",
"to_smoke_grenade": "to smoke grenade",
"to_soar": "to soar",
"to_soul_shard_skills": "to soul shard skills",
"to_soulrift": "to soulrift",
"to_spark": "to spark",
"to_spear_of_the_heavens": "to spear of the heavens",
"to_steel_grasp": "to steel grasp",
"to_steel_grasp_cold_imbuement_frost_hurricane_or_skeletal_mage_mastery": "to steel grasp, cold imbuement, frost, hurricane, or skeletal mage mastery",
"to_stinger": "to stinger",
"to_stone_burst": "to stone burst",
"to_storm_skills": "to storm skills",
"to_storm_strike": "to storm strike",
"to_subterfuge_skills": "to subterfuge skills",
"to_teleport": "to teleport",
"to_the_pack_leader_spirit_boons_lucky_hit_chance": "to the pack leader spirit boons lucky hit chance",
"to_thrash": "to thrash",
"to_thunderspike": "to thunderspike",
"to_tornado": "to tornado",
"to_tortured_wretch": "to tortured wretch",
"to_touch_of_death": "to touch of death",
"to_toxic_skin": "to toxic skin",
"to_trample": "to trample",
"to_trap_skills": "to trap skills",
"to_twisting_blades": "to twisting blades",
"to_tyrants_grasp": "to tyrants grasp",
"to_ultimate_skills": "to ultimate skills",
"to_umbral_chains": "to umbral chains",
"to_upheaval": "to upheaval",
"to_upheaval_cutthroat_pyromancy_earth_or_blood": "to upheaval, cutthroat, pyromancy, earth, or blood",
"to_valor_skills": "to valor skills",
"to_versatile_skills": "to versatile skills",
"to_vortex": "to vortex",
"to_wall_of_agony": "to wall of agony",
"to_war_cry": "to war cry",
"to_weapon_mastery_skills": "to weapon mastery skills",
"to_werebear_skills": "to werebear skills",
"to_werewolf_skills": "to werewolf skills",
"to_whirlwind": "to whirlwind",
"to_wind_shear": "to wind shear",
"to_withering_fist": "to withering fist",
"to_wolves": "to wolves",
"to_wrath_skills": "to wrath skills",
"to_zeal": "to zeal",
"to_zealot_skills": "to zealot skills",
"total_armor": "total armor",
"total_armor_while_in_werebear_form": "total armor while in werebear form",
"total_armor_while_in_werewolf_form": "total armor while in werewolf form",
"total_bonus_experience": "total bonus experience",
"trample_cooldown_reduction": "trample cooldown reduction",
"trample_damage": "trample damage",
"trap_cooldown_reduction": "trap cooldown reduction",
"trap_damage": "trap damage",
"traps_arm_seconds_faster": "traps arm seconds faster",
"twisting_blades_returns_faster": "twisting blades returns faster",
"ultimate_cooldown_reduction": "ultimate cooldown reduction",
"ultimate_damage": "ultimate damage",
"unstable_currents_cooldown_reduction": "unstable currents cooldown reduction",
"upheaval_overpowers_stun_for_seconds": "upheaval overpowers stun for seconds",
"valor_cooldown_reduction": "valor cooldown reduction",
"versatile_damage": "versatile damage",
"vigor_cost_reduction": "vigor cost reduction",
"vigor_on_kill": "vigor on kill",
"vigor_regeneration": "vigor regeneration",
"vigor_when_resolve_is_lost": "vigor when resolve is lost",
"vulnerable_damage": "vulnerable damage",
"vulnerable_damage_multiplier": "vulnerable damage multiplier",
"war_cry_cooldown_reduction": "war cry cooldown reduction",
"weapon_damage": "weapon damage",
"weapon_mastery_attack_speed": "weapon mastery attack speed",
"weapon_mastery_cooldown_reduction": "weapon mastery cooldown reduction",
"weapon_mastery_damage": "weapon mastery damage",
"werebear_damage": "werebear damage",
"werewolf_attack_speed": "werewolf attack speed",
"werewolf_critical_strike_chance": "werewolf critical strike chance",
"werewolf_critical_strike_damage": "werewolf critical strike damage",
"werewolf_damage": "werewolf damage",
"while_injured_your_potion_also_grants_maximum_life_as_barrier": "while injured, your potion also grants maximum life as barrier",
"while_injured_your_potion_also_grants_movement_speed_for_seconds": "while injured, your potion also grants movement speed for seconds",
"while_injured_your_potion_also_restores_resource": "while injured, your potion also restores resource",
"willpower": "willpower",
"wing_strike_damage": "wing strike damage",
"withering_fist_resource_generation": "withering fist resource generation",
"wolves_attack_speed": "wolves attack speed",
"wolves_cooldown_reduction": "wolves cooldown reduction",
"wolves_damage": "wolves damage",
"wrath_every_kills": "wrath every kills",
"wrath_of_the_berserker_cooldown_reduction": "wrath of the berserker cooldown reduction",
"wrath_regeneration": "wrath regeneration",
"your_trap_skills_are_also_considered_core_skills": "your trap skills are also considered core skills",
"zealot_critical_strike_chance": "zealot critical strike chance",
"zealot_critical_strike_damage": "zealot critical strike damage",
"zealot_damage": "zealot damage",
"zenith_cooldown_reduction": "zenith cooldown reduction"
}
================================================
FILE: assets/lang/enUS/aspects.json
================================================
[
"accelerating",
"aggressive",
"agile",
"aphotic",
"apostles",
"archdruids",
"assimilation",
"balanced",
"ballistic",
"bane-link",
"battle-mad",
"battle-mad",
"battle_casters",
"battle_casters",
"battle_fervors",
"bear_clan_berserkers",
"bladedancers",
"blast-trappers",
"blast-trappers",
"blasting",
"blood_boiling",
"blood_boiling",
"blood_getters",
"bold_chieftains",
"bone_breakers",
"brawlers",
"breakneck_bandits",
"bristleback",
"bruisers",
"brutal",
"bulwarks",
"bulwarks",
"cadaverous",
"charged",
"cheats",
"clandestine",
"coldbringers",
"coldclip",
"conceited",
"conjuration_masters",
"crashstone",
"craven",
"cremators",
"crushing",
"cut_to_the_bone",
"deadeyes",
"death_wish",
"demonic",
"devilish",
"duelists",
"duelists",
"dust_devils",
"earthstrikers",
"earthstrikers",
"edgemasters",
"elementalists",
"eluding",
"embattled",
"encased",
"encased",
"energizing",
"enfeebling",
"enshrouding",
"envenomed",
"escape_artists",
"everliving",
"everliving",
"executioners",
"exploiters",
"fastblood",
"fell_soothsayers",
"ferocious",
"firestarter",
"flamethrowers",
"flamewalkers",
"flash_fire",
"frostbitten",
"frostblitz",
"galvanic",
"galvanized_slashers",
"ghostwalker",
"glacial",
"gorefeast",
"gravitational",
"great_storm",
"grenadiers",
"heavy_hitting",
"hectic",
"hellbent_commander",
"high_velocity",
"high_velocity",
"hulking",
"icy_alchemists",
"icy_alchemists",
"impairing",
"incendiary",
"infiltrators",
"insatiable",
"insidious",
"inspiring_leader",
"iron_blood",
"irrepressible",
"jolting",
"joltkeepers",
"juggernauts",
"lightning_dancers",
"lightning_rod",
"lightning_rod",
"lingering",
"lord_of_bloods",
"lord_of_bloods",
"luckbringer",
"mage-lords",
"mage-lords",
"malicious",
"mangled",
"manglers",
"menacing",
"methodical",
"mighty_storms",
"mired_sharpshooters",
"misanthropic",
"moonrage",
"natures_reach",
"necrotic_carapace",
"needleflare",
"nefarious",
"neurotoxic",
"nightstalkers",
"obstinate",
"of_abundant_energy",
"of_accursed_touch",
"of_adaptability",
"of_aftermath",
"of_akarats_blessing",
"of_alacrity",
"of_alchemical_advantage",
"of_amplified_damage",
"of_ancestral_charge",
"of_ancestral_echoes",
"of_ancestral_force",
"of_ancient_flame",
"of_ancient_flame",
"of_anemia",
"of_angelic_masterwork",
"of_anger_management",
"of_anger_management",
"of_anticline_burst",
"of_apogeic_furor",
"of_apogeic_furor",
"of_apprehension",
"of_arcane_ward",
"of_armageddon",
"of_armageddon",
"of_arrogance",
"of_arrow_storms",
"of_artful_initiative",
"of_ascension",
"of_assistance",
"of_audacity",
"of_authority",
"of_avoidance",
"of_barbed_roses",
"of_berserk_fury",
"of_berserk_ripping",
"of_binding_embers",
"of_binding_morass",
"of_biting_cold",
"of_biting_cold",
"of_bitter_infection",
"of_booming_voice",
"of_bristling_vengeance",
"of_bul-kathos",
"of_burning_rage",
"of_bursting_bones",
"of_bursting_venoms",
"of_calamity",
"of_cauterization",
"of_celestial_strife",
"of_channeling",
"of_charged_flash",
"of_chastisement",
"of_coagulation",
"of_coalesced_blood",
"of_cold_judgement",
"of_combined_strikes",
"of_combustion",
"of_compound_fracture",
"of_concentration",
"of_concussive_blend",
"of_concussive_strikes",
"of_conflagration",
"of_contamination",
"of_contemplation",
"of_corruption",
"of_creeping_cadaver",
"of_creeping_death",
"of_crippling_darkness",
"of_dazzling_light",
"of_death_chill",
"of_deaths_defense",
"of_debilitating_darkness",
"of_debilitating_darkness",
"of_debilitating_toxins",
"of_decay",
"of_dedication",
"of_deeper_shadows",
"of_deflection",
"of_delayed_extinction",
"of_deluge",
"of_diabolical_armor",
"of_disobedience",
"of_dominance",
"of_dominance",
"of_earthquakes",
"of_efficiency",
"of_electrified_claws",
"of_elemental_acuity",
"of_elemental_attunement",
"of_elemental_constellation",
"of_elemental_constellation",
"of_elemental_fate",
"of_elusive_menace",
"of_elusive_menace",
"of_empowered_feathers",
"of_encircling_blades",
"of_encroaching_wrath",
"of_endless_fury",
"of_endless_talons",
"of_endurance",
"of_engulfing_flames",
"of_entrapment",
"of_excellence",
"of_excellence",
"of_exhilaration",
"of_exorcism",
"of_explosive_verve",
"of_explosive_verve",
"of_exposed_flesh",
"of_falling_feathers",
"of_falling_feathers",
"of_fathomless_dark",
"of_fevered_mauling",
"of_fiendish_oppression",
"of_fierce_winds",
"of_finality",
"of_firm_decree",
"of_fleet_wings",
"of_forest_power",
"of_fortune",
"of_forward_momentum",
"of_frenzied_onslaught",
"of_frosty_strides",
"of_frozen_memories",
"of_frozen_memories",
"of_frozen_orbit",
"of_furious_impulse",
"of_giant_strides",
"of_gloom",
"of_glynns_anvil",
"of_gore_quills",
"of_grasping_whirlwind",
"of_grim_prognosis",
"of_guttural_yell",
"of_hales_salve",
"of_hardened_bones",
"of_haste",
"of_heavenly_strength",
"of_herculean_spectacle",
"of_heresy",
"of_hewed_flesh",
"of_hewed_flesh",
"of_hit_and_run",
"of_holy_cadence",
"of_holy_punishment",
"of_human_ingenuity",
"of_ignition",
"of_imitated_imbuement",
"of_immolation",
"of_impending_deluge",
"of_impetus",
"of_incendiary_fissures",
"of_inevitable_fate",
"of_infestation",
"of_inner_calm",
"of_innervation",
"of_interdiction",
"of_interdiction",
"of_intricacy",
"of_invigorating_will",
"of_iron_rain",
"of_iron_rain",
"of_jacques_fervor",
"of_kinetic_suppression",
"of_lageras_sovereignty",
"of_lapas_scripture",
"of_lava",
"of_layered_wards",
"of_lethal_dusk",
"of_limitless_rage",
"of_loyalty",
"of_malevolence",
"of_malice",
"of_mending_obscurity",
"of_mending_stone",
"of_merciless_cold",
"of_metamorphosis",
"of_might",
"of_militance",
"of_minds_awakening",
"of_misfortune",
"of_momentum",
"of_mutilation",
"of_natural_balance",
"of_natural_defenses",
"of_natural_instincts",
"of_natural_selection",
"of_nebulous_brews",
"of_noxious_ice",
"of_numbing_wrath",
"of_overheating",
"of_overwhelming_currents",
"of_overwhelming_currents",
"of_peril",
"of_perpetual_stomping",
"of_pestilence",
"of_pestilent_points",
"of_piercing_cold",
"of_piercing_cold",
"of_piercing_static",
"of_piercing_static",
"of_pilgrims_progress",
"of_plains_power",
"of_poisonous_clouds",
"of_poisonous_clouds",
"of_potent_blood",
"of_potent_exchange",
"of_prolific_fury",
"of_proselytizing",
"of_putrefaction",
"of_quickening_fog",
"of_quicksand",
"of_rallying_reversal",
"of_rapid_ossification",
"of_rathmas_chosen",
"of_reactive_armor",
"of_reanimation",
"of_recalling_feathers",
"of_recalling_feathers",
"of_redirected_force",
"of_refutation",
"of_retaliation",
"of_retribution",
"of_retribution",
"of_righteous_rage",
"of_ritual_synthesis",
"of_salvation",
"of_scorching_heat",
"of_scorn",
"of_searing_impact",
"of_searing_wards",
"of_serration",
"of_shared_misery",
"of_shattered_stars",
"of_shattering_steel",
"of_shelter",
"of_shielding_bones",
"of_shielding_bones",
"of_shredding_blades",
"of_shredding_blades",
"of_simple_reprisal",
"of_singed_extremities",
"of_siphoned_victuals",
"of_siphoning_strikes",
"of_sky_power",
"of_slaughter",
"of_sly_steps",
"of_soil_power",
"of_spiked_armor",
"of_splintering_energy",
"of_splintering_energy",
"of_splintering_shards",
"of_stolen_vigor",
"of_sundered_ground",
"of_supremacy",
"of_surprise",
"of_swift_spirit",
"of_synergy",
"of_synergy",
"of_target_practice",
"of_tempering_blows",
"of_temporal_incisions",
"of_tenacity",
"of_tenuous_agility",
"of_tenuous_survival",
"of_terror",
"of_the_agile_wolf",
"of_the_arbiters_zephyr",
"of_the_bounding_conduit",
"of_the_calm_breeze",
"of_the_calm_breeze",
"of_the_changelings_debt",
"of_the_crowded_sage",
"of_the_cursed_aura",
"of_the_damned",
"of_the_dark_dance",
"of_the_dark_howl",
"of_the_deflecting_barrier",
"of_the_dire_whirlwind",
"of_the_disciple",
"of_the_disciple",
"of_the_embalmer",
"of_the_embalmer",
"of_the_enchanter",
"of_the_expectant",
"of_the_firebird",
"of_the_flaming_rampage",
"of_the_followed_path",
"of_the_fortress",
"of_the_frozen_tundra",
"of_the_frozen_wake",
"of_the_frozen_wake",
"of_the_golden_hour",
"of_the_great_feast",
"of_the_indomitable",
"of_the_iron_warrior",
"of_the_judicator",
"of_the_juggernauts_covenant",
"of_the_lights_mending",
"of_the_lights_mending",
"of_the_long_shadow",
"of_the_moonrise",
"of_the_northern_guard",
"of_the_orange_herald",
"of_the_orange_herald",
"of_the_pack_alpha",
"of_the_protector",
"of_the_prudent_heart",
"of_the_rabid_beast",
"of_the_relentless_armsmaster",
"of_the_rushing_wilds",
"of_the_shapeshifter",
"of_the_solitary_shadow",
"of_the_stampede",
"of_the_umbral",
"of_the_unbroken_tether",
"of_the_unholy_confederate",
"of_the_unleashed_beast",
"of_the_unsatiated",
"of_the_untarnished_blaze",
"of_the_unwavering",
"of_the_ursine_horror",
"of_the_valintyr",
"of_the_void",
"of_the_warpath",
"of_the_wildrage",
"of_the_wildrage",
"of_the_zealots_covenant",
"of_thickened_blood",
"of_torment",
"of_transfusion",
"of_true_sight",
"of_turbulence",
"of_tyraels_jurisdiction",
"of_ultimate_shadow",
"of_uncanny_treachery",
"of_unnatural_movement",
"of_unstable_imbuements",
"of_unstoppable_force",
"of_untimely_death",
"of_unyielding_hits",
"of_utmost_glory",
"of_valiance",
"of_verdant_restoration",
"of_vocalized_empowerment",
"of_volatile_shadows",
"of_voracious_rage",
"of_walloping",
"of_warmth",
"of_watkins_law",
"of_wild_claws",
"of_wolfs_rain",
"ominous",
"opportunists",
"osseous_gale",
"overcharged",
"overcharged",
"overheating",
"overwhelming",
"perforators",
"powershifting",
"prepared_assailants",
"prodigys",
"progenitors",
"protecting",
"pyroclastic",
"raid_leaders",
"raiders",
"rangers",
"rapid",
"ravenous",
"raw_might",
"raw_might",
"reapers",
"rebounding",
"recharging",
"recharging",
"rejuvenating",
"relentless_berserkers",
"remorseless",
"requiem",
"resistant_assailants",
"revelators",
"rip_and_tear",
"rotting",
"ruthless",
"sacrificial",
"sadistic",
"sapping",
"scornful",
"seismic-shift",
"serpentine",
"shadow-soaked",
"shadowslicer",
"shard_of_dawn",
"shattered",
"shattered",
"shepherds",
"shifters",
"shivering",
"sickfoots",
"skinwalkers",
"skullbreakers",
"slaking",
"smiting",
"snap_frozen",
"snowguards",
"snowveiled",
"snowveiled",
"spirit_bond",
"splintering",
"squires",
"stable",
"starlight",
"starving_ravagers",
"steadfast_berserkers",
"steadfast_berserkers",
"sticker-thought",
"stoneworkers",
"storm_splitters",
"storm_swell",
"stormchasers",
"stormcrows",
"subterranean",
"sunderfrost",
"the_penitents",
"tidal",
"tides_of_blood",
"tormentors",
"toxic_alchemists",
"trappers",
"tricksters",
"tricksters",
"umbrous",
"undying",
"unrelenting",
"unyielding_commanders",
"vanguards",
"vanquishing",
"vehement_brawlers",
"vengeful",
"veteran_brawlers",
"vigorous",
"virtuous",
"virulent",
"vulpines",
"wanton_rupture",
"weapon_masters",
"wildbolt",
"wind_striker",
"windlasher",
"winter_touch",
"writhing",
"wywards"
]
================================================
FILE: assets/lang/enUS/corrections.json
================================================
{
"bad_tts_uniques": {
"bane_ofahjad-den": "bane_of_ahjad-den",
"galvanicazurite": "galvanic_azurite",
"grandfather": "the_grandfather",
"kilt_ofblackwing": "kilt_of_blackwing",
"mjᅢヨlnic_ryng": "mjölnic_ryng",
"sunstainedwar-crozier": "sunstained_war-crozier"
},
"error_map": {
" arbarian": " barbarian",
"(arbarian": "(barbarian",
"@arbarian": "(barbarian",
"garbarian": "barbarian",
"gorcerer": "sorcerer",
"mruid": "(druid",
"omuid": "(druid",
"seythe": "scythe",
"thoms": "thorns",
"tier s": "tier 5",
"tier1": "tier 1",
"tier2": "tier 2",
"tier3": "tier 3",
"tier4": "tier 4",
"tier5": "tier 5",
"tier6": "tier 6",
"tier7": "tier 7",
"tier8": "tier 8",
"tier9": "tier 9",
"tmlelligence": "intelligence",
"tomado": "tornado",
"ttem": "item",
"two- handed": "two-handed",
"two-handed!": "two-handed",
"two-handed.": "two-handed"
},
"filter_after_keyword": [
" cts ",
"account",
"compare",
"dearest will",
"empty socket",
"granted",
"pacts",
"requires lev",
"requires level",
"requires world",
"scroll down",
"scroll up",
"sell value",
"when equipped"
],
"filter_words": [
"account bound",
"barbarian",
"by your clas",
"by your class",
"druid",
"dungeon affixe",
"imprinted",
"monster level",
"necromancer",
"not useable",
"only)",
"operties lost",
"properties lost",
"requires world tier 3",
"requires world tier 4",
"revives allowed",
"rogue",
"sorcerer",
"sorceress"
]
}
================================================
FILE: assets/lang/enUS/item_types.json
================================================
{
"Amulet": "amulet",
"Axe": "axe",
"Axe2H": "two-handed axe",
"Boots": "boots",
"Bow": "bow",
"ChestArmor": "chest armor",
"Crossbow2H": "crossbow",
"Dagger": "dagger",
"Elixir": "elixir",
"Flail": "flail",
"Focus": "focus",
"Glaive": "glaive",
"Gloves": "gloves",
"Helm": "helm",
"Incense": "custom type incense",
"Legs": "pants",
"Mace": "mace",
"Mace2H": "two-handed mace",
"Material": "custom type material",
"OffHandTotem": "totem",
"Polearm": "polearm",
"Quarterstaff": "quarterstaff",
"Ring": "ring",
"Scythe": "scythe",
"Scythe2H": "two-handed scythe",
"Shield": "shield",
"Sigil": "custom type sigil",
"Staff": "staff",
"Sword": "sword",
"Sword2H": "two-handed sword",
"TemperManual": "temper manual",
"Tome": "tome",
"Wand": "wand"
}
================================================
FILE: assets/lang/enUS/paragon_maxroll_ids.json
================================================
{
"boards": {
"Paragon_Barb_00": "Start",
"Paragon_Barb_01": "Hemorrhage",
"Paragon_Barb_02": "Blood Rage",
"Paragon_Barb_03": "Carnage",
"Paragon_Barb_04": "Decimator",
"Paragon_Barb_05": "Bone Breaker",
"Paragon_Barb_06": "Flawless Technique",
"Paragon_Barb_07": "Warbringer",
"Paragon_Barb_08": "Weapons Master",
"Paragon_Barb_10": "Force of Nature",
"Paragon_Druid_00": "Start",
"Paragon_Druid_01": "Thunderstruck",
"Paragon_Druid_02": "Earthen Devastation",
"Paragon_Druid_03": "Survival Instincts",
"Paragon_Druid_04": "Lust for Carnage",
"Paragon_Druid_05": "Heightened Malice",
"Paragon_Druid_06": "Inner Beast",
"Paragon_Druid_07": "Constricting Tendrils",
"Paragon_Druid_08": "Ancestral Guidance",
"Paragon_Druid_10": "Untamed",
"Paragon_Necro_00": "Start",
"Paragon_Necro_01": "Cult Leader",
"Paragon_Necro_02": "Hulking Monstrosity",
"Paragon_Necro_03": "Flesh-eater",
"Paragon_Necro_04": "Scent of Death",
"Paragon_Necro_05": "Bone Graft",
"Paragon_Necro_06": "Blood Begets Blood",
"Paragon_Necro_07": "Bloodbath",
"Paragon_Necro_08": "Wither",
"Paragon_Necro_10": "Frailty",
"Paragon_Paladin_00": "Start",
"Paragon_Paladin_01": "Castle",
"Paragon_Paladin_02": "Shield Bearer",
"Paragon_Paladin_03": "Fervent",
"Paragon_Paladin_04": "Preacher",
"Paragon_Paladin_05": "Divinity",
"Paragon_Paladin_06": "Relentless",
"Paragon_Paladin_07": "Sentencing",
"Paragon_Paladin_08": "Endure",
"Paragon_Paladin_09": "Beacon",
"Paragon_Rogue_00": "Start",
"Paragon_Rogue_01": "Eldritch Bounty",
"Paragon_Rogue_02": "Tricks of the Trade",
"Paragon_Rogue_03": "Cheap Shot",
"Paragon_Rogue_04": "Deadly Ambush",
"Paragon_Rogue_05": "Leyrana's Instinct",
"Paragon_Rogue_06": "No Witnesses",
"Paragon_Rogue_07": "Exploit Weakness",
"Paragon_Rogue_08": "Cunning Stratagem",
"Paragon_Rogue_10": "Danse Macabre",
"Paragon_Sorc_00": "Start",
"Paragon_Sorc_01": "Searing Heat",
"Paragon_Sorc_02": "Frigid Fate",
"Paragon_Sorc_03": "Static Surge",
"Paragon_Sorc_04": "Elemental Summoner",
"Paragon_Sorc_05": "Burning Instinct",
"Paragon_Sorc_06": "Icefall",
"Paragon_Sorc_07": "Ceaseless Conduit",
"Paragon_Sorc_08": "Enchantment Master",
"Paragon_Sorc_10": "Fundamental Release",
"Paragon_Spirit_0": "Start",
"Paragon_Spirit_01": "In-Fighter",
"Paragon_Spirit_02": "Spiney Skin",
"Paragon_Spirit_03": "Viscous Shield",
"Paragon_Spirit_04": "Bitter Medicine",
"Paragon_Spirit_05": "Revealing",
"Paragon_Spirit_06": "Drive",
"Paragon_Spirit_07": "Convergence",
"Paragon_Spirit_08": "Sapping"
},
"glyphs": {
"Rare_001_Intelligence_Main": "Enchanter",
"Rare_002_Intelligence_Main": "Unleash",
"Rare_003_Intelligence_Main": "Elementalist",
"Rare_004_Intelligence_Main": "Adept",
"Rare_005_Intelligence_Main": "Conjurer",
"Rare_006_Intelligence_Main": "Charged",
"Rare_007_Willpower_Side": "Torch",
"Rare_008_Willpower_Side": "Pyromaniac",
"Rare_009_Willpower_Side": "Cryopathy",
"Rare_010_Dexterity_Main": "Tactician",
"Rare_011_Intelligence_Side": "Guzzler",
"Rare_011_Willpower_Side": "Imbiber",
"Rare_012_Intelligence_Side": "Protector",
"Rare_012_Willpower_Side": "Reinforced",
"Rare_013_Dexterity_Side": "Poise",
"Rare_014_Dexterity_Side": "Territorial",
"Rare_014_Strength_Main": "Turf",
"Rare_014_Strength_Side": "Turf",
"Rare_015_Dexterity_Side": "Flamefeeder",
"Rare_016_Dexterity_Side": "Exploit",
"Rare_016_Intelligence_Side": "Exploit",
"Rare_016_Strength_Side": "Exploit",
"Rare_017_Dexterity_Side": "Winter",
"Rare_018_Dexterity_Side": "Electrocute",
"Rare_019_Dexterity_Side": "Destruction",
"Rare_020_Dexterity_Side": "Control",
"Rare_020_Intelligence_Main": "Control",
"Rare_020_Intelligence_Side": "Control",
"Rare_021_Strength_Main": "Ambidextrous",
"Rare_022_Strength_Main": "Might",
"Rare_023_Strength_Main": "Cleaver",
"Rare_024_Strength_Main": "Seething",
"Rare_025_Strength_Main": "Crusher",
"Rare_026_Strength_Main": "Executioner",
"Rare_027_Strength_Main": "Ire",
"Rare_028_Strength_Main": "Marshal",
"Rare_029_Dexterity_Side": "Bloodfeeder",
"Rare_030_Dexterity_Side": "Wrath",
"Rare_031_Dexterity_Side": "Weapon Master",
"Rare_032_Dexterity_Side": "Mortal Draw",
"Rare_033_Intelligence_Side": "Revenge",
"Rare_033_Willpower_Side": "Revenge",
"Rare_033_Willpower_Side_Necro": "Revenge",
"Rare_034_Intelligence_Side": "Undaunted",
"Rare_034_Willpower_Side": "Undaunted",
"Rare_035_Intelligence_Side": "Dominate",
"Rare_035_Willpower_Side": "Dominate",
"Rare_035_Willpower_Side_Necro": "Dominate",
"Rare_036_Willpower_Side": "Disembowel",
"Rare_037_Willpower_Side": "Brawl",
"Rare_038_Intelligence_Main": "Corporeal",
"Rare_039_Willpower_Main": "Fang and Claw",
"Rare_040_Willpower_Main": "Earth and Sky",
"Rare_041_Intelligence_Side": "Wilds",
"Rare_042_Willpower_Main": "Werebear",
"Rare_043_Willpower_Main": "Werewolf",
"Rare_044_Willpower_Main": "Human",
"Rare_045_Intelligence_Side": "Bane",
"Rare_045_Strength_Side": "Bane",
"Rare_046_Dexterity_Side": "Abyssal",
"Rare_046_Intelligence_Side": "Keeper",
"Rare_047_Dexterity_Side": "Fulminate",
"Rare_047_Intelligence_Side": "Fulminate",
"Rare_048_Dexterity_Side": "Tracker",
"Rare_048_Intelligence_Side": "Tracker",
"Rare_049_Dexterity_Side": "Outmatch",
"Rare_049_Strength_Main": "Outmatch",
"Rare_049_Strength_Side": "Outmatch",
"Rare_050_Dexterity_Main": "Spirit",
"Rare_050_Dexterity_Side": "Spirit",
"Rare_050_Willpower_Side": "Spirit",
"Rare_051_Dexterity_Side": "Shapeshifter",
"Rare_052_Dexterity_Main": "Versatility",
"Rare_053_Dexterity_Main": "Closer",
"Rare_054_Dexterity_Main": "Ranger",
"Rare_055_Dexterity_Main": "Chip",
"Rare_055_Dexterity_Side": "Chip",
"Rare_055_Willpower_Side": "Chip",
"Rare_056_Dexterity_Main": "Frostfeeder",
"Rare_057_Dexterity_Main": "Fluidity",
"Rare_058_Intelligence_Side": "Infusion",
"Rare_059_Dexterity_Main": "Devious",
"Rare_060_Dexterity_Side": "Warrior",
"Rare_061_Intelligence_Side": "Combat",
"Rare_062_Dexterity_Side": "Gravekeeper",
"Rare_063_Intelligence_Side": "Canny",
"Rare_064_Intelligence_Side": "Efficacy",
"Rare_065_Intelligence_Side": "Snare",
"Rare_066_Dexterity_Side": "Essence",
"Rare_067_Strength_Side": "Pride",
"Rare_068_Strength_Side": "Ambush",
"Rare_069_Intelligence_Main": "Sacrificial",
"Rare_070_Intelligence_Main": "Blood-drinker",
"Rare_071_Intelligence_Main": "Deadraiser",
"Rare_072_Intelligence_Main": "Mage",
"Rare_073_Intelligence_Main": "Amplify",
"Rare_074_Willpower_Side": "Golem",
"Rare_075_Willpower_Side": "Scourge",
"Rare_076_Strength_Main": "Diminish",
"Rare_076_Strength_Side": "Diminish",
"Rare_077_Willpower_Side": "Warding",
"Rare_078_Willpower_Side": "Darkness",
"Rare_079_Dexterity_Side": "Exploit",
"Rare_080_Strength_Main": "Twister",
"Rare_081_Strength_Main": "Rumble",
"Rare_082_Dexterity_Main": "Explosive",
"Rare_083_Intelligence_Side": "Nightstalker",
"Rare_084_Intelligence_Main": "Stalagmite",
"Rare_085_Dexterity_Side": "Invocation",
"Rare_086_Dexterity_Side": "Tectonic",
"Rare_087_Willpower_Main": "Electrocution",
"Rare_088_Intelligence_Main": "Exhumation",
"Rare_089_Willpower_Side": "Desecration",
"Rare_090_Dexterity_Main": "Menagerist",
"Rare_091_Strength_Side": "Hone",
"Rare_092_Intelligence_Side": "Consumption",
"Rare_093_Dexterity_Main": "Fitness",
"Rare_094_Intelligence_Side": "Ritual",
"Rare_095_Dexterity_Main": "Jagged Plume",
"Rare_096_Strength_Side": "Innate",
"Rare_097_Dexterity_Main": "Wildfire",
"Rare_098_Strength_Side": "Colossal",
"Rare_100_Dexterity_Main": "Talon",
"Rare_101_Strength_Side": "Hubris",
"Rare_102_Dexterity_Main": "Fester",
"Rare_103_Strength_Main": "Sentinel",
"Rare_104_Dexterity_Side": "Honed",
"Rare_105_Strength_Main": "Law",
"Rare_106_Willpower_Side": "Arbiter ",
"Rare_107_Strength_Main": "Resplendence",
"Rare_108_Intelligence_Side": "Judicator",
"Rare_109_Dexterity_Side": "Feverous",
"Rare_110_Strength_Main": "Apostle",
"Rare_Dex_Generic": "Headhunter",
"Rare_Int_Generic": "Eliminator",
"Rare_Str_Generic": "Challenger",
"Rare_Will_Generic": "Headhunter"
}
}
================================================
FILE: assets/lang/enUS/sigils.json
================================================
{
"dungeons": {
"abandoned_mineworks": "abandoned mineworks",
"akkhans_grasp": "akkhans grasp",
"aldurwood": "aldurwood",
"ancient_reservoir": "ancient reservoir",
"ancients_lament": "ancients lament",
"anicas_claim": "anicas claim",
"basaltic_ascent": "basaltic ascent",
"bastion_of_faith": "bastion of faith",
"beast_graveyard": "beast graveyard",
"belfry_zakara": "belfry zakara",
"betrayed_tomb": "betrayed tomb",
"betrayers_row": "betrayers row",
"bewitched_grotto": "bewitched grotto",
"black_asylum": "black asylum",
"blind_burrows": "blind burrows",
"bloodsoaked_crag": "bloodsoaked crag",
"broken_bulwark": "broken bulwark",
"buried_halls": "buried halls",
"caldera_gate": "caldera gate",
"calibels_mine": "calibels mine",
"carrion_fields": "carrion fields",
"cataclysm": "cataclysm",
"cavern_of_the_sea_hag": "cavern of the sea hag",
"caves_of_kutokue": "caves of kutokue",
"champions_demise": "champions demise",
"charnel_house": "charnel house",
"collapsed_vault": "collapsed vault",
"conclave": "conclave",
"corrupted_grotto": "corrupted grotto",
"crumbling_hekma": "crumbling hekma",
"crusaders_cathedral": "crusaders cathedral",
"cultist_refuge": "cultist refuge",
"dark_ravine": "dark ravine",
"dark_refuge": "dark refuge",
"dead_mans_dredge": "dead mans dredge",
"defiled_catacomb": "defiled catacomb",
"demons_wake": "demons wake",
"derelict_lodge": "derelict lodge",
"deserted_underpass": "deserted underpass",
"domhainne_tunnels": "domhainne tunnels",
"earthen_wound": "earthen wound",
"endless_gates": "endless gates",
"faceless_shrine": "faceless shrine",
"fading_echo": "fading echo",
"farai_cliffs": "farai cliffs",
"feeding_grounds": "feeding grounds",
"ferals_den": "ferals den",
"fetid_mausoleum": "fetid mausoleum",
"flooded_depths": "flooded depths",
"forbidden_city": "forbidden city",
"forge_of_malice": "forge of malice",
"forgotten_depths": "forgotten depths",
"forgotten_remains": "forgotten remains",
"forgotten_ruins": "forgotten ruins",
"forsaken_quarry": "forsaken quarry",
"garan_hold": "garan hold",
"ghoa_ruins": "ghoa ruins",
"grim_haven": "grim haven",
"grinning_labyrinth": "grinning labyrinth",
"guulrahn_canals": "guulrahn canals",
"guulrahn_slums": "guulrahn slums",
"hakans_refuge": "hakans refuge",
"hallowed_ossuary": "hallowed ossuary",
"hallowed_stones": "hallowed stones",
"halls_of_the_damned": "halls of the damned",
"haunted_refuge": "haunted refuge",
"heart_of_the_mountain": "heart of the mountain",
"heathens_keep": "heathens keep",
"heretics_asylum": "heretics asylum",
"hidden_firstborn_ruins": "hidden firstborn ruins",
"hierophant_pyre": "hierophant pyre",
"hive": "hive",
"hoarfrost_demise": "hoarfrost demise",
"howling_warren": "howling warren",
"immortal_emanation": "immortal emanation",
"inferno": "inferno",
"iron_cenotaph": "iron cenotaph",
"iron_hold": "iron hold",
"jalals_vigil": "jalals vigil",
"komdor_temple": "komdor temple",
"kor_dragan_barracks": "kor dragan barracks",
"kor_valar_ramparts": "kor valar ramparts",
"leviathans_maw": "leviathans maw",
"lights_refuge": "lights refuge",
"lights_watch": "lights watch",
"lost_archives": "lost archives",
"lost_keep": "lost keep",
"lubans_rest": "lubans rest",
"maddux_watch": "maddux watch",
"mariners_refuge": "mariners refuge",
"maugans_works": "maugans works",
"maulwood": "maulwood",
"mercys_reach": "mercys reach",
"mournfield": "mournfield",
"murmuring_spiral": "murmuring spiral",
"nostrava_deepwood": "nostrava deepwood",
"oblivion": "oblivion",
"oldstones": "oldstones",
"onyx_hold": "onyx hold",
"pallid_delve": "pallid delve",
"path_of_the_blind": "path of the blind",
"penitent_cairns": "penitent cairns",
"prison_of_caldeum": "prison of caldeum",
"putrescent_larder": "putrescent larder",
"putrid_aquifer": "putrid aquifer",
"raethwind_wilds": "raethwind wilds",
"razaks_descent": "razaks descent",
"refuge_of_the_lost": "refuge of the lost",
"remnants_of_rage": "remnants of rage",
"renegades_retreat": "renegades retreat",
"rimescar_cavern": "rimescar cavern",
"ruined_wild": "ruined wild",
"ruins_of_eridu": "ruins of eridu",
"sanguine_chapel": "sanguine chapel",
"sarats_lair": "sarats lair",
"scorched_tunnels": "scorched tunnels",
"scoriaceous_path": "scoriaceous path",
"sealed_archives": "sealed archives",
"seaside_descent": "seaside descent",
"seers_reach": "seers reach",
"seething_underpass": "seething underpass",
"sepulcher_of_the_forsworn": "sepulcher of the forsworn",
"serpents_lair": "serpents lair",
"shadowed_plunge": "shadowed plunge",
"shifting_city": "shifting city",
"shivta_ruins": "shivta ruins",
"sirocco_caverns": "sirocco caverns",
"skatsimi_fane": "skatsimi fane",
"sleepless_hollow": "sleepless hollow",
"steadfast_barracks": "steadfast barracks",
"stockades": "stockades",
"submerged_ruins": "submerged ruins",
"sunken_library": "sunken library",
"sunken_ruins": "sunken ruins",
"the_aegoye": "the aegoye",
"the_hinterland": "the hinterland",
"the_swallowed_temple": "the swallowed temple",
"tomb_of_hallows": "tomb of hallows",
"tomb_of_the_saints": "tomb of the saints",
"tormented_forest": "tormented forest",
"tormented_ruins": "tormented ruins",
"twisted_hollow": "twisted hollow",
"ularian_sepulcher": "ularian sepulcher",
"uldurs_cave": "uldurs cave",
"underroot": "underroot",
"vault_of_the_forsaken": "vault of the forsaken",
"vile_hive": "vile hive",
"whispering_pines": "whispering pines",
"whispering_vault": "whispering vault",
"witchwater": "witchwater",
"wretched_delve": "wretched delve",
"yshari_sanctum": "yshari sanctum",
"zenith": "zenith"
},
"major": {
"anguished_souls_elites": "anguished souls elites elite monsters have the \"anguished souls\" affix and deal more damage.",
"astaroths_armageddon": "astaroths armageddon meteors will constantly rain from the sky.",
"astaroths_loyal_steed": "astaroths loyal steed astaroth is immune to damage upon reaching each health threshold until the amalgam of rage is killed.",
"avengers": "avengers killing a monster enrages monsters near it after a short delay, making them deal more damage.",
"berserker_elites": "berserker elites elite monsters have the \"berserker\" affix and deal more damage.",
"blood_blisters": "blood blisters killing a monster has a chance to spawn a blood blister. after a short delay, it explodes, dealing heavy area damage.",
"cannibal_horde": "cannibal horde foul cannibals have defiled this place and additionally gain extra life.",
"cultist_horde": "cultist horde corrupted fanatics worship within this place and additionally gain extra life.",
"dark_omen": "dark omen this place is not right. someone, or something, stalks you here...",
"deathly_shadows": "deathly shadows killing a monster has a chance to unleash a volatile pulse after a short delay, dealing heavy area damage.",
"demon_horde": "demon horde vile hellspawn have conquered this place and additionally gain extra life.",
"drifting_shades": "drifting shades drifting shades chase players. on contact, they explode for heavy damage and create a nightmare field that dazes victims.",
"elemental_totems": "elemental totems while in combat, random elemental totems appear that buff nearby enemies until destroyed.",
"empowered_elites_cold_enchanted": "empowered elites cold enchanted elites always have the \"cold enchanted\" affix.",
"empowered_elites_shadow_enchanted": "empowered elites shadow enchanted elites always have the \"shadow enchanted\" affix.",
"empowered_elites_teleporter": "empowered elites teleporter elites always have the \"teleporter\" affix.",
"executioner_elites": "executioner elites elite monsters have the \"executioner\" affix and deal more damage.",
"explosive_elites": "explosive elites elite monsters have the \"explosive\" affix and deal more damage.",
"frenzied_elites": "frenzied elites elite monsters have the \"frenzied\" affix and deal more damage.",
"hellfire": "hellfire monsters are afflicted with hellfire, which transfers to players when killed. hellfire increases damage, but deals damage overtime for each stack. using a potion helps alleviate hellfire with a fiery explosion.",
"illusory_elites": "illusory elites elite monsters have the \"illusory\" affix and deal more damage.",
"infested_elites": "infested elites elite monsters have the \"infested\" affix and deal more damage.",
"insect_horde": "insect horde pestilent bugs have infested this place and additionally gain extra life.",
"khazra_horde": "khazra horde braying goatmen have inhabited this place and additionally gain extra life.",
"lambs_to_slaughter": "lambs to slaughter lure captured enemies to the shrine to sacrifice them for additional rewards.",
"leaping_amalgam": "leaping amalgam the amalgam of rage will now continue its pounce attacks after astaroth dismounts.",
"lightning_storm": "lightning storm lightning gathers above the player. get into the protection dome to avoid severe outcomes.",
"meteor_storm": "meteor storm astaroth now has more meteor attacks.",
"nightmare_portal": "nightmare portal while in combat, nightmare portals open randomly near players, pouring out dangerous monsters.",
"poison_enchanted_elites": "poison enchanted elites elite monsters have the \"poison enchanted\" affix and deal more damage.",
"profane_aegis": "profane aegis monsters gain of their maximum life as a barrier.",
"rage_of_the_pack": "rage of the pack the amalgam of rage now summons elite werewolves.",
"raging": "raging monsters here are reckless, receiving and dealing more damage.",
"saw_blades_elites": "saw blades elites elite monsters have the \"saw blades\" affix and deal more damage.",
"shadow_enchanted_elites": "shadow enchanted elites elite monsters have the \"shadow enchanted\" affix and deal more damage.",
"shock_lance_elites": "shock lance elites elite monsters have the \"shock lance\" affix and deal more damage.",
"sinful_torment_elites": "sinful torment elites elite monsters have the \"sinful torment\" affix and deal more damage.",
"splitter_elites": "splitter elites elite monsters have the \"splitter\" affix and deal more damage.",
"stormbanes_wrath": "stormbanes wrath a dark monolith chases players, pulsing for heavy damage when nearby.",
"suppressor_elites": "suppressor elites elite monsters have the \"suppressor\" affix and deal more damage.",
"teleport_residue": "teleport residue astaroth now leaves behind a pool of persistent damage when teleporting.",
"the_beast_in_ice": "the beast in ice face a more challenging beast in ice at the end of this frozen cave.",
"undead_horde": "undead horde desecrated undead have risen within this place and additionally gain extra life.",
"vampiric_elites": "vampiric elites elite monsters have the \"vampiric\" affix and deal more damage.",
"veiled_elites": "veiled elites elite monsters have the \"veiled\" affix and deal more damage.",
"volcanic": "volcanic while in combat, gouts of flame periodically erupt near players.",
"waller_elites": "waller elites elite monsters have the \"waller\" affix and deal more damage."
},
"minor": {
"armor_breakers": "armor breakers monster attacks from a distance reduce your armor by for seconds, stacking up to .",
"astaroth_blood_blisters": "astaroth blood blisters astaroth summons blood blisters that explode if not destroyed.",
"astaroth_deadly_pulse": "astaroth deadly pulse astaroth occasionally releases a deadly shadow pulse.",
"astaroth_life_steal": "astaroth life steal astaroth gains life steal.",
"backstabbers": "backstabbers close monster attacks from behind cause you to become vulnerable.",
"barrier_breakers": "barrier breakers monsters deal more damage to barriers.",
"bleeding_strikes": "bleeding strikes monsters deal an additional of their physical damage dealt as bleeding damage over seconds.",
"burning_strikes": "burning strikes monsters deal an additional of their physical damage dealt as burning damage over seconds.",
"chilling_wind_elites": "chilling wind elites elite monsters have the \"chilling wind\" affix and deal more damage.",
"cold_strikes": "cold strikes monsters deal an additional of their physical damage dealt as cold damage.",
"corrupting_strikes": "corrupting strikes monsters deal an additional of their physical damage dealt as corrupting damage over seconds.",
"dodge_breakers": "dodge breakers monster attacks from a distance reduce dodge chance by for seconds, stacking up to .",
"fire_strikes": "fire strikes monsters deal an additional of their physical damage dealt as fire damage.",
"fire_traps": "fire traps additional fire traps appear in this dungeon.",
"frostbiting_strikes": "frostbiting strikes monsters deal an additional of their physical damage dealt as frostbiting damage over seconds.",
"hellbound_elites": "hellbound elites elite monsters have the \"hellbound\" affix and deal more damage.",
"hunters": "hunters monsters have unnatural speed here, moving and attacking faster.",
"lesser_aegis": "lesser aegis some monsters here gain of their maximum life as a barrier.",
"lightning_strikes": "lightning strikes monsters deal an additional of their physical damage dealt as lightning damage.",
"melee_defenders": "melee defenders monsters take less damage from close targets",
"monster_barrier": "monster barrier monsters gain of their maximum life as a barrier.",
"monster_bleed_resist": "monster bleed resist monsters take less bleeding damage.",
"monster_burning_resist": "monster burning resist monsters take less burning damage.",
"monster_cold_resist": "monster cold resist monsters take less cold damage.",
"monster_critical_resist": "monster critical resist monster attacks reduce the damage of your critical strikes for seconds by , stacking up to .",
"monster_crowd_control_resist": "monster crowd control resist crowd control duration vs monsters is reduced by .",
"monster_fire_resist": "monster fire resist monsters take less fire damage.",
"monster_life": "monster life monsters gain extra life.",
"monster_life_steal": "monster life steal nonboss monsters gain life steal.",
"monster_overpower_resist": "monster overpower resist monster attacks reduce the damage of your next overpower attack by . stacking up to .",
"monster_physical_resist": "monster physical resist monsters take less physical damage.",
"monster_poison_resist": "monster poison resist monsters take less poison damage.",
"monster_regen": "monster regen nonboss monsters regen maximum life per second.",
"monster_shadow_damage_over_time_resist": "monster shadow damage over time resist monsters take reduced damage from your shadow damage over time effects.",
"monster_shadow_resist": "monster shadow resist monsters take less shadow damage.",
"monster_thorns": "monster thorns monsters reflect of damage.",
"monster_vulnerable_resist": "monster vulnerable resist duration of vulnerable effects vs monsters is reduced by .",
"monsters_lightning_resist": "monsters lightning resist monsters take less lightning damage.",
"poison_strikes": "poison strikes monsters deal an additional of their physical damage dealt as poison damage.",
"poisoning_strikes": "poisoning strikes monsters deal an additional of their physical damage dealt as poisoning damage over seconds.",
"potion_breakers": "potion breakers monster attacks from a distance increase the cooldown of your next potion by seconds, up to seconds.",
"ranged_defenders": "ranged defenders monsters take less damage from distant targets.",
"resistance_breakers": "resistance breakers monster attacks from a distance reduce resistance to all elements by for seconds, stacking up to .",
"resource_burn": "resource burn after you spend resource, you burn an additional of the resource spent for every time you were hit by a distant enemy.",
"shadow_strikes": "shadow strikes monsters deal an additional of their physical damage dealt as shadow damage.",
"slowing_projectiles": "slowing projectiles monster attacks from a distance have a chance to slow targets.",
"sparking_strikes": "sparking strikes monsters deal an additional of their physical damage dealt as sparking damage over seconds.",
"summoner_elites": "summoner elites elite monsters have the \"summoner\" affix and deal more damage.",
"suppressor_elites": "suppressor elites elite monsters have the \"suppressor\" affix and deal more damage.",
"unstable_experiments": "unstable experiments monsters may explode on death, dealing heavy area damage around themselves after a delay.",
"unstoppable_monsters": "unstoppable monsters monsters become unstoppable when life drops below ."
},
"positive": {
"amethyst_reserve": "amethyst reserve many amethyst chests have been stashed here.",
"ancestors_favor": "ancestors favor extra lunar shrines and more glyph experience earned at the end of this dungeon.",
"ancestral_awakening": "ancestral awakening you earn more glyph experience at the end of this dungeon.",
"andariels_offering": "andariels offering astaroths rewards include pincushioned dolls, required to open andariels hoard.",
"artillery_shrines": "artillery shrines artillery shrines will appear throughout this place.",
"astral_prophecy": "astral prophecy astaroths rewards include a valuable horadric jewel.",
"battle_hardened": "battle hardened gain damage reduction for every health you are missing.",
"belials_apparitions": "belials apparitions elite apparitions have invaded this dungeon.",
"belials_offering": "belials offering astaroths rewards include betrayers husks, required to open belials hoard.",
"blast_wave_shrines": "blast wave shrines blast wave shrines will appear throughout this place.",
"bonus_chests_armor": "bonus chests armor armor chests spawn at an extremely high rate.",
"bonus_chests_boss_materials": "bonus chests boss materials summoning material chests spawn at an extremely high rate.",
"bonus_chests_elixirs": "bonus chests elixirs elixir chests spawn at an extremely high rate.",
"bonus_chests_herbs": "bonus chests herbs herb chests spawn at an extremely high rate.",
"bonus_chests_jewelry": "bonus chests jewelry jewelry chests spawn at an extremely high rate.",
"bonus_chests_runes": "bonus chests runes rune chests spawn at an extremely high rate.",
"bonus_chests_sigil_powder": "bonus chests sigil powder sigil powder chests spawn at an extremely high rate.",
"bonus_chests_weapons": "bonus chests weapons weapon chests spawn at an extremely high rate.",
"channeling_shrines": "channeling shrines channeling shrines will appear throughout this place.",
"chaos_rifts": "chaos rifts have opened in this place.",
"conduit_shrines": "conduit shrines conduit shrines will appear throughout this place.",
"control_impaired_explosions": "control impaired explosions being hit by control impairing effects creates an explosion around you.",
"diamond_reserve": "diamond reserve many diamond chests have been stashed here.",
"dungeon_delve": "dungeon delve completing the dungeon grants extra experience and rewards.",
"duriels_offering": "duriels offering astaroths rewards include shards of agony, required to open duriels hoard.",
"emerald_reserve": "emerald reserve many emerald chests have been stashed here.",
"equipment_delve": "equipment delve find horadric artifacts to earn an increasingly higher quality cache of gear.",
"extra_resplendent_chests": "extra resplendent chests extra resplendent chests will appear in the dungeon.",
"extra_shrines": "extra shrines extra shrines will appear in the dungeon.",
"fire_damage": "fire damage you deal more fire damage.",
"forgotten_altar": "forgotten altar this world will always spawn a forgotten altar.",
"forgotten_wisdom": "forgotten wisdom enemies hoard knowledge here, granting extra experience.",
"frost_damage": "frost damage you deal more frost damage.",
"gem_reserve": "gem reserve many gem fragment chests have been stashed here.",
"gold_find": "gold find you find more gold.",
"gold_reserve": "gold reserve many gold chests have been stashed here.",
"greater_lair_keys": "greater lair keys astaroths rewards include greater lair keys.",
"grigoires_offering": "grigoires offering astaroths rewards include living steel, required to open grigoires hoard.",
"grim_secrets": "grim secrets killing astaroth grants a large amount of horadric knowledge.",
"guaranteed_treasure_goblins": "guaranteed treasure goblins treasure goblins will appear in the dungeon.",
"harbinger_of_hatreds_offering": "harbinger of hatreds offering astaroths rewards include abhorrent hearts, required to open the harbinger of hatreds hoard.",
"hell_touched": "hell touched extra infernal shrines and more glyph experience earned at the end of this dungeon.",
"hidden_armory": "hidden armory exceptional items are kept here, granting elite monsters a powerful loot affix.",
"hidden_legendary_vendor": "hidden legendary vendor a secret vendor appears somewhere in the dungeon...",
"horadric_phials": "horadric phials monsters in this dungeon drop more horadric phials.",
"horadric_strongroom": "horadric strongroom this place will always contain a horadric strongroom.",
"increased_critical_strike": "increased critical strike your critical strike chance is increased by .",
"increased_healing": "increased healing your healing received is increased by .",
"infernal_warp_reserve": "infernal warp reserve many infernal warp chests have been stashed here.",
"lair_keys": "lair keys astaroths rewards include lair keys.",
"legendary_spoils": "legendary spoils astaroths rewards include a guaranteed ancestral item.",
"lethal_shrines": "lethal shrines lethal shrines will appear throughout this place.",
"lightning_caller": "lightning caller you occasionally call down lightning strikes that damage nearby enemies.",
"lightning_damage": "lightning damage you deal more lightning damage.",
"lord_zirs_offering": "lord zirs offering astaroths rewards include exquisite blood, required to open lord zirs hoard.",
"magic_find": "magic find you find more items from enemies.",
"materials_reserve": "materials reserve many materials chests have been stashed here.",
"mythic_prankster": "mythic prankster the resplendent treasures in this place have attracted a fancy, yet familiar nemesis...",
"nudging_evade": "nudging evade using evade pushes enemies back.",
"obducite_mine": "obducite mine monsters drop additional obducite in this place.",
"obols_reserve": "obols reserve many obols chests have been stashed here.",
"physical_damage": "physical damage you deal more physical damage.",
"poison_damage": "poison damage you deal more poison damage",
"poisonous_evade": "poisonous evade using evade leaves a damaging pool of poison under the first enemy you evade through or at your destination.",
"protection_shrines": "protection shrines protection shrines will appear throughout this place.",
"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.",
"revered_site": "revered site monsters grant additional divine favor in this place.",
"riches_of_gold": "riches of gold astaroths rewards include a large amount of gold.",
"riches_of_keys": "riches of keys astaroths rewards include valuable dungeon keys.",
"riches_of_materials": "riches of materials astaroths rewards include a large amount of crafting materials.",
"riches_of_phials": "riches of phials astaroths rewards include a large amount of horadric phials.",
"ruby_reserve": "ruby reserve many ruby chests have been stashed here.",
"sapphire_reserve": "sapphire reserve many sapphire chests have been stashed here.",
"shadow_damage": "shadow damage you deal more shadow damage.",
"skull_reserve": "skull reserve many skull chests have been stashed here.",
"thorns": "thorns after attacking an enemy, your thorns are increased by for seconds, up to at max stacks.",
"topaz_reserve": "topaz reserve many topaz chests have been stashed here.",
"treasure_breach": "treasure breach a puzzling number of treasure goblins have overrun this place.",
"unique_spoils": "unique spoils astaroths rewards include more unique items.",
"urivars_offering": "urivars offering astaroths rewards include judicators masks, required to open urivars hoard.",
"varshans_offering": "varshans offering astaroths rewards include malignant hearts, required to open varshans hoard.",
"vile_splendor": "vile splendor opulent excess is enjoyed here, granting elite monsters the \"gilded\" affix."
}
}
================================================
FILE: assets/lang/enUS/tooltips.json
================================================
{
"ItemPower": "item power"
}
================================================
FILE: assets/lang/enUS/tributes.json
================================================
{
"ancestral_tribute_of_armaments": "ancestral tribute of armaments",
"greater_tribute_of_armaments": "greater tribute of armaments",
"greater_tribute_of_harmony": "greater tribute of harmony",
"greater_tribute_of_ingenuity": "greater tribute of ingenuity",
"greater_tribute_of_refinement": "greater tribute of refinement",
"greater_tribute_of_the_horadrim": "greater tribute of the horadrim",
"lesser_tribute": "lesser tribute",
"lesser_tribute_of_harmony": "lesser tribute of harmony",
"lesser_tribute_of_ingenuity": "lesser tribute of ingenuity",
"lesser_tribute_of_the_horadrim": "lesser tribute of the horadrim",
"major_tribute_of_andariel": "major tribute of andariel",
"minor_tribute_of_andariel": "minor tribute of andariel",
"mythic_tribute_of_armaments": "mythic tribute of armaments",
"tribute_of_andariel": "tribute of andariel",
"tribute_of_armaments": "tribute of armaments",
"tribute_of_ascendance_resolute": "tribute of ascendance (resolute)",
"tribute_of_growth": "tribute of growth",
"tribute_of_harmony": "tribute of harmony",
"tribute_of_heritage": "tribute of heritage",
"tribute_of_ingenuity": "tribute of ingenuity",
"tribute_of_radiance_resolute": "tribute of radiance (resolute)",
"tribute_of_refinement": "tribute of refinement",
"tribute_of_the_horadrim": "tribute of the horadrim",
"tribute_of_titans": "tribute of titans"
}
================================================
FILE: assets/lang/enUS/uniques.json
================================================
{
"100000_steps": {
"num_inherents": 0
},
"accord_of_the_wilds": {
"num_inherents": 0
},
"aegroms_schism": {
"num_inherents": 0
},
"ahavarion_spear_of_lycander": {
"num_inherents": 0
},
"airidahs_inexorable_will": {
"num_inherents": 0
},
"anathema_of_the_primes": {
"num_inherents": 0
},
"ancients_oath": {
"num_inherents": 0
},
"andariels_visage": {
"num_inherents": 0
},
"arcadia": {
"num_inherents": 0
},
"argent_veil": {
"num_inherents": 0
},
"arreats_bearing": {
"num_inherents": 0
},
"ashearas_khanjar": {
"num_inherents": 0
},
"assassins_stride": {
"num_inherents": 0
},
"autumnal_crown": {
"num_inherents": 0
},
"axial_conduit": {
"num_inherents": 0
},
"azurewrath": {
"num_inherents": 0
},
"balazans_maxtlatl": {
"num_inherents": 0
},
"band_of_first_breath": {
"num_inherents": 0
},
"bands_of_ichorous_rose": {
"num_inherents": 0
},
"bane_of_ahjad-den": {
"num_inherents": 0
},
"banished_lords_talisman": {
"num_inherents": 0
},
"bastion_of_sir_matthias": {
"num_inherents": 2
},
"battle_trance": {
"num_inherents": 0
},
"beastfall_boots": {
"num_inherents": 0
},
"bindings_of_attrition": {
"num_inherents": 0
},
"black_river": {
"num_inherents": 0
},
"blood-mad_idol": {
"num_inherents": 0
},
"blood_artisans_cuirass": {
"num_inherents": 0
},
"blood_moon_breeches": {
"num_inherents": 0
},
"blood_wake": {
"num_inherents": 0
},
"bloodless_scream": {
"num_inherents": 0
},
"blue_rose": {
"num_inherents": 0
},
"bridle_of_torbaalos": {
"num_inherents": 0
},
"cage_of_madness": {
"num_inherents": 0
},
"cassias_grace": {
"num_inherents": 0
},
"cathedrals_song": {
"num_inherents": 2
},
"chainscourged_mail": {
"num_inherents": 0
},
"cluckeye": {
"num_inherents": 0
},
"cluckonomicon": {
"num_inherents": 0
},
"condemnation": {
"num_inherents": 0
},
"coop_de_grâce": {
"num_inherents": 0
},
"cowl_of_malefic_torment": {
"num_inherents": 0
},
"cowl_of_the_nameless": {
"num_inherents": 0
},
"craze_of_the_dead_god": {
"num_inherents": 0
},
"crown_of_lucion": {
"num_inherents": 0
},
"cruors_embrace": {
"num_inherents": 0
},
"dark_howl": {
"num_inherents": 0
},
"dark_stalkers_medallion": {
"num_inherents": 0
},
"dawnfire": {
"num_inherents": 0
},
"deathgrip": {
"num_inherents": 0
},
"deathless_visage": {
"num_inherents": 0
},
"deathmask_of_nirmitruq": {
"num_inherents": 0
},
"deaths_pavane": {
"num_inherents": 0
},
"deathspeakers_pendant": {
"num_inherents": 0
},
"desperate_march": {
"num_inherents": 0
},
"dirge_of_airidah": {
"num_inherents": 0
},
"dirge_of_odium": {
"num_inherents": 0
},
"dolmen_stone": {
"num_inherents": 0
},
"doombringer": {
"num_inherents": 0
},
"drognans_anguish": {
"num_inherents": 0
},
"eaglehorn": {
"num_inherents": 0
},
"earthbreaker": {
"num_inherents": 0
},
"ebonpiercer": {
"num_inherents": 0
},
"echo_of_kwatli": {
"num_inherents": 0
},
"eggcecutioner": {
"num_inherents": 0
},
"eggis": {
"num_inherents": 2
},
"eldruin_sword_of_justice": {
"num_inherents": 1
},
"elegy": {
"num_inherents": 0
},
"emberfury": {
"num_inherents": 0
},
"emblem_of_staalbreak": {
"num_inherents": 0
},
"endurant_faith": {
"num_inherents": 0
},
"esadoras_overflowing_cameo": {
"num_inherents": 0
},
"esus_heirloom": {
"num_inherents": 0
},
"etnas_lost_dagger": {
"num_inherents": 0
},
"eye_of_baal": {
"num_inherents": 0
},
"eyes_in_the_dark": {
"num_inherents": 0
},
"fang_of_the_vipermagi": {
"num_inherents": 0
},
"fields_of_crimson": {
"num_inherents": 0
},
"fist_of_the_iron_rose": {
"num_inherents": 0
},
"fists_of_fate": {
"num_inherents": 0
},
"flamescar": {
"num_inherents": 0
},
"flameweaver": {
"num_inherents": 0
},
"fleshrender": {
"num_inherents": 0
},
"fleshwrit_carapace": {
"num_inherents": 0
},
"flickerstep": {
"num_inherents": 0
},
"footfalls_of_the_waning_world": {
"num_inherents": 0
},
"fractured_runestone": {
"num_inherents": 0
},
"fractured_winterglass": {
"num_inherents": 0
},
"frostburn": {
"num_inherents": 0
},
"fury_of_the_wilds": {
"num_inherents": 0
},
"galvanic_azurite": {
"num_inherents": 0
},
"gate_of_the_red_dawn": {
"num_inherents": 2
},
"gathlens_birthright": {
"num_inherents": 0
},
"gauntlets_of_sheol": {
"num_inherents": 0
},
"gift_of_frost": {
"num_inherents": 0
},
"gladiators_triumph": {
"num_inherents": 0
},
"gloves_of_the_illuminator": {
"num_inherents": 0
},
"godslayer_crown": {
"num_inherents": 0
},
"gohrs_devastating_grips": {
"num_inherents": 0
},
"gospel_of_the_devotee": {
"num_inherents": 0
},
"grasp_of_shadow": {
"num_inherents": 0
},
"gravewalkers_hand": {
"num_inherents": 0
},
"greatstaff_of_the_crone": {
"num_inherents": 0
},
"greaves_of_the_empty_tomb": {
"num_inherents": 0
},
"greenwalkers_oath": {
"num_inherents": 0
},
"greenwalkers_signet": {
"num_inherents": 0
},
"griswolds_opus": {
"num_inherents": 0
},
"hail_of_verglas": {
"num_inherents": 0
},
"hand_of_apotheosis": {
"num_inherents": 0
},
"hands_of_the_worldbreaker": {
"num_inherents": 0
},
"hangmans_hand": {
"num_inherents": 0
},
"harlequin_crest": {
"num_inherents": 0
},
"harmony_of_ebewaka": {
"num_inherents": 0
},
"heart_of_azgar": {
"num_inherents": 0
},
"hecaton_chasm": {
"num_inherents": 0
},
"heir_of_perdition": {
"num_inherents": 0
},
"hellbrand_signet": {
"num_inherents": 0
},
"hellhammer": {
"num_inherents": 0
},
"hellhounds_sabatons": {
"num_inherents": 0
},
"herald_of_zakarum": {
"num_inherents": 3
},
"heralds_morningstar": {
"num_inherents": 0
},
"hesha_e_kesungi": {
"num_inherents": 0
},
"hooves_of_the_mountain_god": {
"num_inherents": 0
},
"howl_from_below": {
"num_inherents": 0
},
"hunters_zenith": {
"num_inherents": 0
},
"iceheart_brais": {
"num_inherents": 0
},
"ifehs_dire_totem": {
"num_inherents": 0
},
"indiras_memory": {
"num_inherents": 0
},
"infernal_homunculus": {
"num_inherents": 0
},
"insatiable_fury": {
"num_inherents": 0
},
"jacinth_shell": {
"num_inherents": 0
},
"judgment_of_auriel": {
"num_inherents": 0
},
"judicants_glaivehelm": {
"num_inherents": 0
},
"kabraxis_will": {
"num_inherents": 0
},
"kessimes_legacy": {
"num_inherents": 0
},
"khamsin_steppewalkers": {
"num_inherents": 0
},
"kilt_of_blackwing": {
"num_inherents": 0
},
"levin_grasp": {
"num_inherents": 0
},
"lidless_wall": {
"num_inherents": 2
},
"lights_rebuke": {
"num_inherents": 0
},
"litany_of_sable": {
"num_inherents": 0
},
"locrans_talisman": {
"num_inherents": 0
},
"loyaltys_mantle": {
"num_inherents": 0
},
"lurid_pact": {
"num_inherents": 0
},
"mace_of_king_leoric": {
"num_inherents": 0
},
"mad_wolfs_glee": {
"num_inherents": 0
},
"malefic_crescent": {
"num_inherents": 0
},
"mantle_of_mountains_fury": {
"num_inherents": 0
},
"mantle_of_the_grey": {
"num_inherents": 0
},
"march_of_the_stalwart_soul": {
"num_inherents": 0
},
"mark_of_the_old_wolf": {
"num_inherents": 0
},
"melted_heart_of_selig": {
"num_inherents": 0
},
"might_of_qual-kehk": {
"num_inherents": 0
},
"might_of_the_ursine": {
"num_inherents": 0
},
"misericorde": {
"num_inherents": 0
},
"mjölnic_ryng": {
"num_inherents": 0
},
"molochs_beating_flame": {
"num_inherents": 0
},
"molten_band": {
"num_inherents": 0
},
"morlu_fleshward": {
"num_inherents": 0
},
"mothers_embrace": {
"num_inherents": 0
},
"mutilator_plate": {
"num_inherents": 0
},
"nails_of_the_gore-crowned": {
"num_inherents": 0
},
"nesekem_the_herald": {
"num_inherents": 0
},
"night_terror": {
"num_inherents": 0
},
"nomads_longing_heart": {
"num_inherents": 0
},
"okuns_catalyst": {
"num_inherents": 1
},
"omen_of_pain": {
"num_inherents": 0
},
"onyx_soul": {
"num_inherents": 0
},
"ophidian_iris": {
"num_inherents": 0
},
"orphan_maker": {
"num_inherents": 0
},
"orsivane": {
"num_inherents": 0
},
"overkill": {
"num_inherents": 0
},
"pact_of_bone": {
"num_inherents": 0
},
"paingorgers_gauntlets": {
"num_inherents": 0
},
"path_of_the_emissary": {
"num_inherents": 0
},
"path_of_tragoul": {
"num_inherents": 0
},
"peacemongers_signet": {
"num_inherents": 0
},
"penitent_greaves": {
"num_inherents": 0
},
"pitfighters_gull": {
"num_inherents": 0
},
"protean_heart": {
"num_inherents": 0
},
"protection_of_the_prime": {
"num_inherents": 0
},
"purified_lightbringer": {
"num_inherents": 0
},
"rage_of_harrogath": {
"num_inherents": 0
},
"raiment_of_the_infinite": {
"num_inherents": 0
},
"raiment_of_the_sea": {
"num_inherents": 0
},
"rakanoths_wake": {
"num_inherents": 0
},
"ramaladnis_magnum_opus": {
"num_inherents": 0
},
"razorplate": {
"num_inherents": 0
},
"red_blessing": {
"num_inherents": 0
},
"red_sermon": {
"num_inherents": 0
},
"rictus_of_terror": {
"num_inherents": 0
},
"rimeblood": {
"num_inherents": 0
},
"ring_of_mendeln": {
"num_inherents": 0
},
"ring_of_red_furor": {
"num_inherents": 0
},
"ring_of_starless_skies": {
"num_inherents": 0
},
"ring_of_the_midday_hunt": {
"num_inherents": 0
},
"ring_of_the_midnight_sun": {
"num_inherents": 0
},
"ring_of_the_ravenous": {
"num_inherents": 0
},
"ring_of_the_sacrilegious_soul": {
"num_inherents": 0
},
"ring_of_writhing_moon": {
"num_inherents": 0
},
"rod_of_kepeleke": {
"num_inherents": 0
},
"rotting_lightbringer": {
"num_inherents": 0
},
"rustbitten_dirk": {
"num_inherents": 0
},
"saboteurs_signet": {
"num_inherents": 0
},
"sabre_of_tsasgal": {
"num_inherents": 0
},
"sanctis_of_kethamar": {
"num_inherents": 0
},
"sanguivor_blade_of_zir": {
"num_inherents": 0
},
"sashes_of_the_wretched": {
"num_inherents": 0
},
"scepter_of_the_three": {
"num_inherents": 0
},
"scorn_of_the_earth": {
"num_inherents": 0
},
"scoundrels_kiss": {
"num_inherents": 0
},
"scoundrels_leathers": {
"num_inherents": 0
},
"scourge_of_duriel": {
"num_inherents": 0
},
"sea_lords_fine_gloves": {
"num_inherents": 0
},
"seal_of_the_ophanim": {
"num_inherents": 0
},
"seal_of_the_second_trumpet": {
"num_inherents": 0
},
"seed_of_horazon": {
"num_inherents": 0
},
"sepazontec": {
"num_inherents": 0
},
"shanars_resonance": {
"num_inherents": 0
},
"shard_of_verathiel": {
"num_inherents": 0
},
"shattered_vow": {
"num_inherents": 0
},
"shroud_of_false_death": {
"num_inherents": 0
},
"shroud_of_khanduras": {
"num_inherents": 0
},
"shrouded_gift": {
"num_inherents": 0
},
"sidhe_bindings": {
"num_inherents": 0
},
"signet_of_pelghain": {
"num_inherents": 0
},
"sire_of_sin": {
"num_inherents": 0
},
"skyhunter": {
"num_inherents": 0
},
"sliver_of_hate": {
"num_inherents": 0
},
"soulbrand": {
"num_inherents": 0
},
"spine_of_tathamet": {
"num_inherents": 0
},
"staff_of_endless_rage": {
"num_inherents": 0
},
"staff_of_lam_esen": {
"num_inherents": 0
},
"staff_of_zerae": {
"num_inherents": 0
},
"starfall_coronet": {
"num_inherents": 0
},
"stone_of_vehemen": {
"num_inherents": 0
},
"storms_companion": {
"num_inherents": 0
},
"strike_of_stormhorn": {
"num_inherents": 0
},
"sunbirds_gorget": {
"num_inherents": 0
},
"sunbrand": {
"num_inherents": 0
},
"sundered_night": {
"num_inherents": 0
},
"sunstained_war-crozier": {
"num_inherents": 0
},
"supplication": {
"num_inherents": 0
},
"tal_rashas_iridescent_loop": {
"num_inherents": 0
},
"tassets_of_the_dawning_sky": {
"num_inherents": 0
},
"temerity": {
"num_inherents": 0
},
"tempest_roar": {
"num_inherents": 0
},
"the_basilisk": {
"num_inherents": 0
},
"the_blade_of_sight_aflame": {
"num_inherents": 0
},
"the_butchers_cleaver": {
"num_inherents": 0
},
"the_eightfold_idol": {
"num_inherents": 0
},
"the_fecund_seal": {
"num_inherents": 0
},
"the_gloom_ward": {
"num_inherents": 2
},
"the_grandfather": {
"num_inherents": 1
},
"the_hand_of_naz": {
"num_inherents": 0
},
"the_hemat_stone": {
"num_inherents": 0
},
"the_maestro": {
"num_inherents": 0
},
"the_mortacrux": {
"num_inherents": 0
},
"the_oculus": {
"num_inherents": 0
},
"the_open_eye_of_gorgorra": {
"num_inherents": 0
},
"the_relentless_heart": {
"num_inherents": 0
},
"the_third_blade": {
"num_inherents": 0
},
"the_umbracrux": {
"num_inherents": 0
},
"the_undercrown": {
"num_inherents": 0
},
"the_unmaker": {
"num_inherents": 0
},
"thousand-eye_reaver": {
"num_inherents": 0
},
"thrice-woven_nightmare": {
"num_inherents": 0
},
"thundergods_blessing": {
"num_inherents": 0
},
"tibaults_will": {
"num_inherents": 0
},
"tuskhelm_of_joritz_the_mighty": {
"num_inherents": 0
},
"twin_strikes": {
"num_inherents": 0
},
"tyraels_might": {
"num_inherents": 1
},
"ugly_bastard_helm": {
"num_inherents": 0
},
"unbroken_chain": {
"num_inherents": 0
},
"unsung_ascetics_wraps": {
"num_inherents": 0
},
"vasilys_prayer": {
"num_inherents": 0
},
"vengeful_sinew": {
"num_inherents": 0
},
"vision_of_the_firestorm": {
"num_inherents": 0
},
"vox_omnium": {
"num_inherents": 0
},
"ward_of_the_white_dove": {
"num_inherents": 2
},
"waxing_gibbous": {
"num_inherents": 0
},
"wendigo_brand": {
"num_inherents": 0
},
"widows_web": {
"num_inherents": 0
},
"wildheart_hunger": {
"num_inherents": 0
},
"will_of_rathma": {
"num_inherents": 0
},
"will_of_stone": {
"num_inherents": 0
},
"windforce": {
"num_inherents": 0
},
"word_of_hakan": {
"num_inherents": 0
},
"wound_drinker": {
"num_inherents": 0
},
"wreath_of_auric_laurel": {
"num_inherents": 0
},
"writhing_band_of_trickery": {
"num_inherents": 0
},
"wushe_nak_pa": {
"num_inherents": 0
},
"wyrdskin": {
"num_inherents": 0
},
"xfals_corroded_signet": {
"num_inherents": 0
},
"yens_blessing": {
"num_inherents": 0
}
}
================================================
FILE: build.py
================================================
import os
import shutil
from pathlib import Path
from src import __version__
EXE_NAME = "d4lf.exe"
def build(release_dir: Path):
installer_cmd = (
f"pyinstaller --clean --onefile --icon=assets/logo.ico --distpath {release_dir} --paths src src\\main.py"
)
os.system(installer_cmd)
(release_dir / "main.exe").rename(release_dir / EXE_NAME)
def clean_up():
if (build_dir := Path("build")).exists():
shutil.rmtree(build_dir)
for p in Path.cwd().glob("*.spec"):
p.unlink()
def copy_additional_resources(release_dir: Path):
(release_dir / "tts").mkdir()
shutil.copy("README.md", release_dir)
shutil.copy("tts/saapi64.dll", release_dir)
shutil.copytree("assets", release_dir / "assets")
shutil.copy("tts/install_dll.cmd", release_dir)
def create_batch_for_consoleonly(release_dir: Path, exe_name: str):
batch_file_path = release_dir / "d4lf-consoleonly.bat"
with Path(batch_file_path).open("w", encoding="utf-8") as f:
f.write("@echo off\n")
f.write('cd /d "%~dp0"\n')
f.write(f'start "" {exe_name} --consoleonly\n')
def create_batch_for_autoupdater(release_dir: Path, exe_name: str):
batch_file_path = release_dir / "autoupdater.bat"
Path(batch_file_path).write_text(
f"""@echo off
cd /d "%~dp0"
echo Starting D4LF auto update preprocessing
start /WAIT {exe_name} --autoupdate
if %errorlevel% == 1 (
echo Process did not complete successfully, check logs for more information.
) else if %errorlevel% == 2 (
echo D4Lf is already up to date!
) else (
echo Killing all existing d4lf processes to perform update
taskkill /f /im d4lf.exe
timeout /t 1 /nobreak
echo Updating files
robocopy "./temp_update/d4lf" "." /MIR /XF "autoupdater.bat" /XD "temp_update" "logs"
echo Running postprocessing to verify update and clean up files
start /WAIT {exe_name} --autoupdatepost
)""",
encoding="utf-8",
)
if __name__ == "__main__":
os.chdir(Path(__file__).parent)
print(f"Building version: {__version__}")
release_dir = Path("d4lf")
if release_dir.exists():
shutil.rmtree(release_dir.absolute())
release_dir.mkdir(exist_ok=True, parents=True)
clean_up()
build(release_dir=release_dir)
copy_additional_resources(release_dir)
create_batch_for_consoleonly(release_dir=release_dir, exe_name=EXE_NAME)
create_batch_for_autoupdater(release_dir=release_dir, exe_name=EXE_NAME)
clean_up()
================================================
FILE: pyproject.toml
================================================
[build-system]
build-backend = "hatchling.build"
requires = ["hatchling"]
[dependency-groups]
dev = [
"prek",
"pyinstaller",
"pytest",
"pytest-env",
"pytest-mock",
"pytest-pythonpath",
"pytest-xdist",
"typing-extensions"
]
[project]
dependencies = [
"beautifultable",
"colorama",
"httpx",
"jsonpath",
"keyboard",
"lxml",
"mouse",
"mss",
"natsort",
"numpy",
"opencv-python",
"pillow",
"psutil",
"pydantic",
"pydantic-numpy",
"pydantic-yaml",
"pyqt6",
"python-jsonpath",
"pytweening",
"pywin32; sys_platform == 'win32'",
"pyyaml",
"rapidfuzz",
"ruamel-yaml",
"ruff",
"selenium",
"seleniumbase",
"tk",
"webdriver-manager"
]
dynamic = ["version"]
name = "d4lf"
requires-python = ">=3.14"
[tool.hatch.build.targets.wheel]
packages = ["src"]
[tool.hatch.version]
path = "src/__init__.py"
[tool.ruff]
indent-width = 4
line-length = 120
preview = true
[tool.ruff.format]
docstring-code-format = true
docstring-code-line-length = "dynamic"
indent-style = "space"
line-ending = "auto"
quote-style = "double"
skip-magic-trailing-comma = true
[tool.ruff.lint]
ignore = [
"ANN001", # fix more in the future, this is typing
"ANN002", # fix more in the future, this is typing
"ANN003", # fix more in the future, this is typing
"ANN201", # fix more in the future, this is typing
"ANN202", # fix more in the future, this is typing
"ANN204", # fix more in the future, this is typing
"ANN205", # fix more in the future, this is typing
"ANN401", # fix more in the future, this is typing
"ARG001", # fix more in the future
"ARG002", # fix more in the future
"BLE001", # fix more in the future
"C901", # fix more in the future
"COM812",
"CPY",
"D100",
"D101",
"D102",
"D103",
"D104",
"D105",
"D106",
"D107",
"DOC201", # fix more in the future, this is doc
"DOC501", # fix more in the future, this is docs
"DOC502", # fix more in the future, this is docs
"E501",
"ERA001", # fix more in the future
"FBT001",
"FBT002",
"FBT003", # fix more in the future
"FIX002", # fix more in the future
"FIX004", # fix more in the future
"FURB101", # fix more in the future
"FURB118", # fix more in the future
"FURB171", # fix more in the future
"G004",
"LOG014",
"N801", # fix more in the future
"N802", # fix more in the future
"N803", # fix more in the future
"N805", # fix more in the future
"N806", # fix more in the future
"N812", # fix more in the future
"N815", # fix more in the future
"N818", # fix more in the future
"NPY002", # fix more in the future
"PLC0206", # fix more in the future
"PLR0904", # fix more in the future
"PLR0911", # fix more in the future
"PLR0912", # fix more in the future
"PLR0913",
"PLR0914", # fix more in the future
"PLR0915", # fix more in the future
"PLR0916", # fix more in the future
"PLR0917",
"PLR1702", # fix more in the future
"PLR1704", # fix more in the future
"PLR2004", # fix more in the future
"PLR6104", # fix more in the future
"PLR6201", # fix more in the future
"PLR6301", # fix more in the future, staticmethods
"PLW0108", # fix more in the future
"PLW0602", # fix more in the future
"PLW0603", # fix more in the future
"PLW1641", # fix more in the future
"PTH122", # fix more in the future
"RUF001", # fix more in the future
"RUF005", # fix more in the future
"RUF012", # fix more in the future
"RUF043", # fix more in the future
"RUF045", # fix more in the future
"RUF059", # fix more in the future
"RUF067", # fix more in the future
"S101", # fix more in the future
"S311", # fix more in the future
"S404",
"S506", # fix more in the future
"S603",
"S605", # fix more in the future
"S606", # fix more in the future
"S607",
"SLF001", # fix more in the future
"T201", # fix more in the future
"TD002", # fix more in the future
"TD003", # fix more in the future
"TD004", # fix more in the future
"TRY002", # fix more in the future
"TRY003", # fix more in the future
"TRY004", # fix more in the future
"TRY400", # fix more in the future
"UP047" # fix more in the future
]
select = ["ALL"]
[tool.ruff.lint.isort]
split-on-trailing-comma = false
[tool.ruff.lint.per-file-ignores]
"tests/*" = [
"FBT003",
"PLC2701",
"PLR2004",
"S101",
"S311",
"SLF001"
]
[tool.ruff.lint.pydocstyle]
convention = "google"
================================================
FILE: pytest.ini
================================================
[pytest]
addopts = --strict-markers
markers =
requests: mark a test using requests
selenium: mark a test using selenium
pythonpath = src
testpaths = tests
================================================
FILE: src/__init__.py
================================================
import concurrent.futures
TP = concurrent.futures.ThreadPoolExecutor()
__version__ = "9.1.3"
================================================
FILE: src/autoupdater.py
================================================
import logging
import shutil
import sys
import time
import zipfile
from pathlib import Path
import requests
import src.logger
from src import __version__
LOGGER = logging.getLogger(__name__)
# This autoupdater was almost entirely provided by iAmPilcrow
class D4LFUpdater:
def __init__(self):
self.repo_owner = "d4lfteam"
self.repo_name = "d4lf"
self.api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/releases/latest"
self.changes_base_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/compare/"
self.current_dir = Path.cwd()
self.temp_dir = self.current_dir / "temp_update"
self.version_file = self.temp_dir / "version"
@staticmethod
def normalize_version(version):
"""Ensure version has 'v' prefix."""
if version and not version.startswith("v"):
return f"v{version.strip()}"
return version
def get_latest_release(self, silent=False):
"""Fetch latest release info from GitHub API."""
if not silent:
LOGGER.info("Checking for latest release...")
try:
response = requests.get(self.api_url, timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
LOGGER.error(f"Error fetching release info: {e}")
return None
def print_changes_between_releases(self, current_version, latest_version):
try:
url = self.changes_base_url + current_version + "..." + latest_version
response = requests.get(url, timeout=10)
response.raise_for_status()
LOGGER.info("Changes since last update:")
for commit in response.json()["commits"]:
LOGGER.info(f"- {commit['commit']['message']}")
except requests.exceptions.RequestException as e:
LOGGER.error(f"Error fetching changes since last update: {e}")
@staticmethod
def download_file(url, filename):
"""Download file with progress indication."""
LOGGER.info(f"Downloading {filename}...")
try:
response = requests.get(url, stream=True, timeout=30)
response.raise_for_status()
total_size = int(response.headers.get("content-length", 0))
downloaded = 0
with Path(filename).open("wb") as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded += len(chunk)
if total_size > 0:
percent = (downloaded / total_size) * 100
print(f"\rProgress: {percent:.1f}%", end="")
print("\n")
except requests.exceptions.RequestException as e:
LOGGER.error(f"\nError downloading file: {e}")
return False
LOGGER.info("Download complete!")
return True
def extract_release(self, zip_path, latest_version):
"""Extract zip so batch process can copy files."""
LOGGER.info("Extracting files...")
try:
# Extract zip
with zipfile.ZipFile(zip_path, "r") as zip_ref:
zip_ref.extractall(self.temp_dir)
# Also create an update file with information post processing will need
# with Path(self.update_file).open("w") as f:
# update_data = {"version": latest_version, "zip_path": zip_path}
# json.dump(update_data, f)
Path(self.version_file).write_text(latest_version, encoding="utf-8")
except Exception as e:
LOGGER.error(f"Error during extraction: {e}")
return False
LOGGER.info("Files extracted successfully!")
return True
@staticmethod
def _get_major_version_number(version: str) -> int:
return int(version.replace("v", "").split(".")[0])
def preprocess(self):
"""Main update process.
This will:
- Check if update is needed
- Download new release
- Extract files to a temp directory
Additional updating and cleanup will be handled by the post process
"""
self._print_header()
# Get current installed version
current_version = self.normalize_version(__version__)
LOGGER.info(f"Current installed version: {current_version}")
# Get latest release info
release_data = self.get_latest_release()
if not release_data:
LOGGER.warning("Unable to find latest release on github, can't automatically update.")
return False
latest_version = self.normalize_version(release_data.get("tag_name"))
LOGGER.info(f"Latest release tag: {latest_version}")
# Check if update needed
if current_version == latest_version:
LOGGER.info("✓ You're already on the latest version!")
input("\nPress Enter to exit...")
sys.exit(2)
LOGGER.info(f"→ Update available: {current_version} → {latest_version}")
self.print_changes_between_releases(current_version, latest_version)
# Check if it's an update to a major version and warn of the consequences
if self._get_major_version_number(latest_version) > self._get_major_version_number(current_version):
LOGGER.warning(
"You are upgrading a major version. This means your existing profiles might no longer work and will need to be reimported or recreated. Do you want to proceed?"
)
proceed = input("Enter yes or y to proceed, all other inputs will cancel: ")
if proceed.lower() not in ["yes", "y"]:
LOGGER.info("Cancelling update.")
return False
# Find the d4lf zip asset
assets = release_data.get("assets", [])
zip_asset = None
for asset in assets:
if asset["name"].startswith("d4lf_") and asset["name"].endswith(".zip"):
zip_asset = asset
break
if not zip_asset:
LOGGER.error("Could not find d4lf zip file in release assets.")
return False
# Create temp directory
self.temp_dir.mkdir(exist_ok=True)
download_url = zip_asset["browser_download_url"]
zip_filename = self.temp_dir / zip_asset["name"]
LOGGER.info("")
# Download
if not self.download_file(download_url, zip_filename):
return False
# Extract the zip
if not self.extract_release(zip_filename, latest_version):
return False
LOGGER.info("=" * 50)
LOGGER.info("✓ Preprocessing is done, shutting down to allow update to happen. A new window will open shortly.")
LOGGER.info("=" * 50)
return True
def postprocess(self):
"""Post process will handle the cleanup.
It will:
- Delete the temporary files that were extracted
- Verify the version is truly updated
"""
self._print_header()
with self.version_file.open("r") as f:
updated_to_version = f.read().strip()
if not updated_to_version:
LOGGER.error(
"Pre-processing update data was missing! Try to update manually by downloading the newest D4LF release."
)
return False
current_version = self.normalize_version(__version__)
if updated_to_version != current_version:
LOGGER.error(
f"Current version is {current_version} but we attempted to update to {updated_to_version}. Check logs for errors and update manually."
)
return False
LOGGER.info("Cleaning up temporary files")
if self.temp_dir.exists():
shutil.rmtree(self.temp_dir, ignore_errors=True)
LOGGER.info("Temporary files are removed")
LOGGER.info("=" * 50)
LOGGER.info(f"✓ Successfully updated to {updated_to_version}!")
LOGGER.info("="
gitextract_9j8i29wl/
├── .clang-format
├── .gitattributes
├── .github/
│ ├── actions/
│ │ └── setup_env/
│ │ └── action.yml
│ └── workflows/
│ ├── ci.yml
│ ├── notify.yml
│ └── release.yml
├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE.txt
├── README.md
├── assets/
│ └── lang/
│ └── enUS/
│ ├── How to add to these files.md
│ ├── affixes.json
│ ├── aspects.json
│ ├── corrections.json
│ ├── item_types.json
│ ├── paragon_maxroll_ids.json
│ ├── sigils.json
│ ├── tooltips.json
│ ├── tributes.json
│ └── uniques.json
├── build.py
├── pyproject.toml
├── pytest.ini
├── src/
│ ├── __init__.py
│ ├── autoupdater.py
│ ├── cam.py
│ ├── config/
│ │ ├── __init__.py
│ │ ├── data.py
│ │ ├── helper.py
│ │ ├── loader.py
│ │ ├── profile_models.py
│ │ ├── settings_models.py
│ │ └── ui.py
│ ├── dataloader.py
│ ├── gui/
│ │ ├── __init__.py
│ │ ├── activity_log_widget.py
│ │ ├── collapsible_widget.py
│ │ ├── config_tab.py
│ │ ├── config_window.py
│ │ ├── d4lfitem.py
│ │ ├── dialog.py
│ │ ├── importer/
│ │ │ ├── __init__.py
│ │ │ ├── d4builds.py
│ │ │ ├── diablo_trade.py
│ │ │ ├── gui_common.py
│ │ │ ├── importer_config.py
│ │ │ ├── maxroll.py
│ │ │ ├── mobalytics.py
│ │ │ └── paragon_export.py
│ │ ├── importer_window.py
│ │ ├── open_user_config_button.py
│ │ ├── profile_editor/
│ │ │ ├── __init__.py
│ │ │ ├── affixes_tab.py
│ │ │ ├── aspect_upgrades_tab.py
│ │ │ ├── global_uniques_tab.py
│ │ │ ├── profile_editor.py
│ │ │ ├── sigils_tab.py
│ │ │ └── tributes_tab.py
│ │ ├── profile_editor_window.py
│ │ ├── profile_tab.py
│ │ ├── themes.py
│ │ └── unified_window.py
│ ├── item/
│ │ ├── __init__.py
│ │ ├── data/
│ │ │ ├── __init__.py
│ │ │ ├── affix.py
│ │ │ ├── aspect.py
│ │ │ ├── item_type.py
│ │ │ ├── rarity.py
│ │ │ └── seasonal_attribute.py
│ │ ├── descr/
│ │ │ ├── __init__.py
│ │ │ ├── read_descr_tts.py
│ │ │ ├── text.py
│ │ │ └── texture.py
│ │ ├── filter.py
│ │ ├── find_descr.py
│ │ └── models.py
│ ├── logger.py
│ ├── loot_mover.py
│ ├── main.py
│ ├── overlay.py
│ ├── paragon_overlay.py
│ ├── scripts/
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── handler.py
│ │ ├── loot_filter_tts.py
│ │ ├── vision_mode_fast.py
│ │ └── vision_mode_with_highlighting.py
│ ├── startup_messages.py
│ ├── template_finder.py
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── data/
│ │ │ ├── custom_affixes_enUS.json
│ │ │ └── custom_sigils_enUS.json
│ │ └── gen_data.py
│ ├── tts.py
│ ├── ui/
│ │ ├── __init__.py
│ │ ├── char_inventory.py
│ │ ├── inventory_base.py
│ │ ├── menu.py
│ │ ├── stash.py
│ │ └── vendor.py
│ └── utils/
│ ├── __init__.py
│ ├── custom_mouse.py
│ ├── image_operations.py
│ ├── misc.py
│ ├── process_handler.py
│ ├── roi_operations.py
│ └── window.py
├── tests/
│ ├── __init__.py
│ ├── config/
│ │ ├── __init__.py
│ │ ├── data/
│ │ │ ├── __init__.py
│ │ │ ├── sigils.py
│ │ │ └── uniques.py
│ │ ├── helper_test.py
│ │ ├── loader_test.py
│ │ ├── models_test.py
│ │ └── ui_test.py
│ ├── conftest.py
│ ├── gui/
│ │ ├── __init__.py
│ │ └── importer/
│ │ ├── __init__.py
│ │ ├── test_d4builds.py
│ │ ├── test_diablo_trade.py
│ │ ├── test_gui_common.py
│ │ ├── test_maxroll.py
│ │ └── test_mobalytics.py
│ ├── item/
│ │ ├── __init__.py
│ │ ├── descr/
│ │ │ └── __init__.py
│ │ ├── filter/
│ │ │ ├── __init__.py
│ │ │ ├── data/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── affixes.py
│ │ │ │ ├── aspects.py
│ │ │ │ ├── filters.py
│ │ │ │ ├── items.py
│ │ │ │ ├── sigils.py
│ │ │ │ ├── tributes.py
│ │ │ │ └── uniques.py
│ │ │ └── filter_test.py
│ │ ├── read_descr_season6_tts_test.py
│ │ ├── read_descr_season8_tts_test.py
│ │ ├── read_descr_season_11_tts_test.py
│ │ ├── read_descr_season_12_tts_test.py
│ │ ├── read_descr_season_13_tts_test.py
│ │ └── read_descr_tts_test.py
│ ├── template_finder_test.py
│ ├── ui/
│ │ ├── __init__.py
│ │ ├── char_inventory_test.py
│ │ └── chest_test.py
│ └── utils/
│ ├── __init__.py
│ ├── image_operations_test.py
│ └── roi_operations_test.py
└── tts/
├── install_dll.cmd
├── saapi.cpp
├── saapi.h
└── saapi.vcxproj
SYMBOL INDEX (1038 symbols across 101 files)
FILE: build.py
function build (line 10) | def build(release_dir: Path):
function clean_up (line 18) | def clean_up():
function copy_additional_resources (line 25) | def copy_additional_resources(release_dir: Path):
function create_batch_for_consoleonly (line 33) | def create_batch_for_consoleonly(release_dir: Path, exe_name: str):
function create_batch_for_autoupdater (line 41) | def create_batch_for_autoupdater(release_dir: Path, exe_name: str):
FILE: src/autoupdater.py
class D4LFUpdater (line 17) | class D4LFUpdater:
method __init__ (line 18) | def __init__(self):
method normalize_version (line 28) | def normalize_version(version):
method get_latest_release (line 34) | def get_latest_release(self, silent=False):
method print_changes_between_releases (line 46) | def print_changes_between_releases(self, current_version, latest_versi...
method download_file (line 59) | def download_file(url, filename):
method extract_release (line 84) | def extract_release(self, zip_path, latest_version):
method _get_major_version_number (line 105) | def _get_major_version_number(version: str) -> int:
method preprocess (line 108) | def preprocess(self):
method postprocess (line 184) | def postprocess(self):
method _print_header (line 219) | def _print_header():
function start_auto_update (line 226) | def start_auto_update(postprocess=False):
function notify_if_update (line 241) | def notify_if_update():
function _should_check_for_update (line 264) | def _should_check_for_update(check_interval_hours=4):
FILE: src/cam.py
class Cam (line 18) | class Cam:
method __new__ (line 30) | def __new__(cls):
method update_window_pos (line 35) | def update_window_pos(self, offset_x: int, offset_y: int, width: int, ...
method is_offset_set (line 59) | def is_offset_set(self):
method grab (line 62) | def grab(self, force_new: bool = False) -> np.ndarray:
method monitor_to_window (line 88) | def monitor_to_window(self, monitor_coord: np.ndarray) -> np.ndarray:
method window_to_monitor (line 92) | def window_to_monitor(self, window_coord: np.ndarray) -> np.ndarray:
method abs_window_to_window (line 97) | def abs_window_to_window(self, abs_window_coord: np.ndarray) -> np.nda...
method window_to_abs_window (line 101) | def window_to_abs_window(self, window_coord: np.ndarray) -> np.ndarray:
method abs_window_to_monitor (line 105) | def abs_window_to_monitor(self, abs_window_coord: np.ndarray) -> np.nd...
FILE: src/config/__init__.py
function get_base_dir (line 5) | def get_base_dir(bundled: bool = False) -> Path:
FILE: src/config/data.py
class Template (line 71) | class Template:
function load_templates (line 80) | def load_templates() -> dict[str, Template]:
FILE: src/config/helper.py
function check_greater_than_zero (line 8) | def check_greater_than_zero(v: int) -> int:
function validate_percent (line 15) | def validate_percent(v: int) -> int:
function validate_hotkey (line 23) | def validate_hotkey(k: str) -> str:
function singleton (line 28) | def singleton(cls):
function str_to_int_list (line 41) | def str_to_int_list(s: str) -> list[int]:
FILE: src/config/loader.py
class IniConfigLoader (line 23) | class IniConfigLoader:
method __init__ (line 26) | def __init__(self) -> None:
method _config_path (line 42) | def _config_path(self) -> Path:
method _get_config_signature (line 45) | def _get_config_signature(self) -> tuple[int, int] | None:
method _section_models (line 53) | def _section_models(self) -> dict[str, Any]:
method _model_for_section (line 56) | def _model_for_section(self, section: str) -> Any | None:
method _capture_state_snapshot (line 59) | def _capture_state_snapshot(self) -> dict[str, Any]:
method _changed_keys (line 66) | def _changed_keys(self, previous_snapshot: dict[str, Any], current_sna...
method _write_parser (line 73) | def _write_parser(self) -> None:
method _remove_defunct_model_keys (line 81) | def _remove_defunct_model_keys(self) -> bool:
method _log_defunct_model_key (line 102) | def _log_defunct_model_key(self, section: str, key: str) -> None:
method consume_deferred_cleanup_log_records (line 118) | def consume_deferred_cleanup_log_records(self) -> list[logging.LogReco...
method _format_value_for_log (line 125) | def _format_value_for_log(self, value: Any) -> str:
method _log_changed_values (line 130) | def _log_changed_values(self, changed_keys: set[str]) -> None:
method _notify_listeners (line 142) | def _notify_listeners(self, changed_keys: set[str]) -> None:
method register_change_listener (line 154) | def register_change_listener(self, listener: ConfigChangeListener) -> ...
method unregister_change_listener (line 159) | def unregister_change_listener(self, listener: ConfigChangeListener) -...
method register_listener (line 163) | def register_listener(self, listener: ConfigChangeListener) -> None:
method unregister_listener (line 167) | def unregister_listener(self, listener: ConfigChangeListener) -> None:
method load (line 171) | def load(self, clear: bool = False, notify: bool = True) -> None:
method reload_if_changed (line 209) | def reload_if_changed(self) -> bool:
method advanced_options (line 220) | def advanced_options(self) -> AdvancedOptionsModel:
method char (line 225) | def char(self) -> CharModel:
method general (line 230) | def general(self) -> GeneralModel:
method user_dir (line 235) | def user_dir(self) -> Path:
method config_revision (line 239) | def config_revision(self) -> int:
method save_value (line 243) | def save_value(self, section: str, key: str, value: Any) -> None:
FILE: src/config/profile_models.py
function _parse_item_type_or_rarities (line 16) | def _parse_item_type_or_rarities(data: str | list[str]) -> list[str]:
class AffixAspectFilterModel (line 22) | class AffixAspectFilterModel(BaseModel):
method parse_data (line 29) | def parse_data(cls, data: str | list[str] | list[str | float] | dict[s...
class AffixFilterModel (line 48) | class AffixFilterModel(AffixAspectFilterModel):
method name_must_exist (line 54) | def name_must_exist(cls, name: str) -> str:
method percent_validator (line 65) | def percent_validator(cls, v: int) -> int:
method value_and_percent_are_mutually_exclusive (line 69) | def value_and_percent_are_mutually_exclusive(self) -> AffixFilterModel:
class AffixFilterCountModel (line 76) | class AffixFilterCountModel(BaseModel):
method count_validator (line 84) | def count_validator(cls, v: int) -> int:
method model_validator (line 88) | def model_validator(self) -> AffixFilterCountModel:
class AspectUniqueFilterModel (line 105) | class AspectUniqueFilterModel(AffixAspectFilterModel):
method name_must_exist (line 110) | def name_must_exist(cls, name: str) -> str:
method percent_validator (line 124) | def percent_validator(cls, v: int) -> int:
method value_and_percent_are_mutually_exclusive (line 128) | def value_and_percent_are_mutually_exclusive(self) -> AspectUniqueFilt...
class GlobalUniqueModel (line 135) | class GlobalUniqueModel(BaseModel):
method check_min_power (line 144) | def check_min_power(cls, v: int) -> int:
method count_validator (line 149) | def count_validator(cls, v: int) -> int:
method percent_validator (line 157) | def percent_validator(cls, v: int) -> int:
class ItemFilterModel (line 161) | class ItemFilterModel(BaseModel):
method check_min_power (line 172) | def check_min_power(cls, v: int) -> int:
method min_greater_affix_in_range (line 177) | def min_greater_affix_in_range(cls, v: int) -> int:
method parse_item_type (line 185) | def parse_item_type(cls, data: str | list[str]) -> list[str]:
class SigilPriority (line 192) | class SigilPriority(enum.StrEnum):
class SigilConditionModel (line 197) | class SigilConditionModel(BaseModel):
method parse_data (line 204) | def parse_data(cls, data: str | list[str] | list[str | float] | dict[s...
method name_must_exist (line 224) | def name_must_exist(cls, names_in: str | list[str]) -> str | list[str]:
class SigilFilterModel (line 236) | class SigilFilterModel(BaseModel):
method data_integrity (line 243) | def data_integrity(self) -> SigilFilterModel:
class TributeFilterModel (line 251) | class TributeFilterModel(BaseModel):
method name_must_exist (line 258) | def name_must_exist(cls, name: str) -> str:
method parse_data (line 279) | def parse_data(cls, data: str | list[str] | dict[str, str | list[str]]...
method parse_rarities (line 296) | def parse_rarities(cls, data: str | list[str]) -> list[str]:
class ProfileModel (line 300) | class ProfileModel(BaseModel):
method aspects_must_exist (line 311) | def aspects_must_exist(self) -> ProfileModel:
FILE: src/config/settings_models.py
class AspectFilterType (line 20) | class AspectFilterType(enum.StrEnum):
class BrowserType (line 26) | class BrowserType(enum.StrEnum):
class CosmeticFilterType (line 32) | class CosmeticFilterType(enum.StrEnum):
class ItemRefreshType (line 37) | class ItemRefreshType(enum.StrEnum):
class LogLevels (line 43) | class LogLevels(enum.StrEnum):
class MoveItemsType (line 51) | class MoveItemsType(enum.StrEnum):
class JunkRaresType (line 58) | class JunkRaresType(enum.StrEnum):
class ThemeType (line 64) | class ThemeType(enum.StrEnum):
class UnfilteredUniquesType (line 69) | class UnfilteredUniquesType(enum.StrEnum):
class VisionModeType (line 75) | class VisionModeType(enum.StrEnum):
class _IniBaseModel (line 80) | class _IniBaseModel(BaseModel):
class AdvancedOptionsModel (line 84) | class AdvancedOptionsModel(_IniBaseModel):
method key_must_be_unique (line 146) | def key_must_be_unique(self) -> AdvancedOptionsModel:
method key_must_exist (line 175) | def key_must_exist(cls, k: str) -> str:
method convert_fast_vision_mode_coordinates (line 180) | def convert_fast_vision_mode_coordinates(cls, v: str) -> tuple[int, in...
class CharModel (line 200) | class CharModel(_IniBaseModel):
method key_must_exist (line 207) | def key_must_exist(cls, k: str) -> str:
class ColorsModel (line 211) | class ColorsModel(_IniBaseModel):
class GeneralModel (line 217) | class GeneralModel(_IniBaseModel):
method check_chest_tabs_index (line 288) | def check_chest_tabs_index(cls, v: str) -> list[int]:
method check_max_stash_tabs (line 298) | def check_max_stash_tabs(cls, v: int) -> int:
method check_profiles_is_list (line 306) | def check_profiles_is_list(cls, v: str) -> list[str]:
method language_must_exist (line 316) | def language_must_exist(cls, v: str) -> str:
method font_size_in_range (line 324) | def font_size_in_range(cls, v: int) -> int:
method convert_move_item_type (line 332) | def convert_move_item_type(cls, v: str) -> list[type[MoveItemsType[Any...
class HSVRangeModel (line 341) | class HSVRangeModel(_IniBaseModel):
method __getitem__ (line 345) | def __getitem__(self, index):
method check_interval_sanity (line 355) | def check_interval_sanity(self) -> HSVRangeModel:
method values_in_range (line 369) | def values_in_range(cls, v: np.ndarray) -> np.ndarray:
class UiOffsetsModel (line 382) | class UiOffsetsModel(_IniBaseModel):
class UiPosModel (line 392) | class UiPosModel(_IniBaseModel):
class UiRoiModel (line 397) | class UiRoiModel(NumpyModel):
FILE: src/config/ui.py
class _ResTransformer (line 13) | class _ResTransformer:
method __init__ (line 14) | def __init__(self, resolution: str):
method _resize_image (line 20) | def _resize_image(self, src: np.ndarray) -> np.ndarray:
method _transform (line 24) | def _transform(self, value: int) -> int:
method _transform_array (line 27) | def _transform_array(self, value: np.ndarray, scale_only=False) -> np....
method _transform_list_of_tuples (line 47) | def _transform_list_of_tuples(self, value: list[tuple[int, int]]) -> l...
method _transform_templates (line 50) | def _transform_templates(self, templates: dict[str, Template]) -> dict...
method _transform_tuples (line 65) | def _transform_tuples(self, value: tuple[int, int]) -> tuple[int, int]:
method fromUHD (line 69) | def fromUHD(self) -> tuple[UiOffsetsModel, UiPosModel, UiRoiModel, dic...
class ResManager (line 100) | class ResManager:
method __init__ (line 101) | def __init__(self):
method offsets (line 109) | def offsets(self) -> UiOffsetsModel:
method pos (line 113) | def pos(self) -> UiPosModel:
method resolution (line 117) | def resolution(self) -> tuple[int, ...]:
method roi (line 121) | def roi(self) -> UiRoiModel:
method templates (line 125) | def templates(self) -> dict[str, Template]:
method set_resolution (line 128) | def set_resolution(self, res: str):
FILE: src/dataloader.py
class Dataloader (line 15) | class Dataloader:
method __new__ (line 32) | def __new__(cls):
method load_data (line 41) | def load_data(self):
FILE: src/gui/activity_log_widget.py
class ActivityLogWidget (line 8) | class ActivityLogWidget(QWidget):
method __init__ (line 9) | def __init__(self, parent=None):
method _open_user_dir (line 96) | def _open_user_dir(self) -> None:
FILE: src/gui/collapsible_widget.py
class Header (line 6) | class Header(QWidget):
method __init__ (line 9) | def __init__(self, name, content_widget):
method mousePressEvent (line 58) | def mousePressEvent(self, *args):
method expand (line 63) | def expand(self):
method collapse (line 71) | def collapse(self):
method set_name (line 76) | def set_name(self, name):
class Container (line 81) | class Container(QWidget):
method __init__ (line 84) | def __init__(self, name, color_background=False):
method contentWidget (line 119) | def contentWidget(self):
method first_expansion (line 126) | def first_expansion(self):
FILE: src/gui/config_tab.py
function _validate_and_save_changes (line 40) | def _validate_and_save_changes(
class ConfigTab (line 77) | class ConfigTab(QWidget):
method __init__ (line 78) | def __init__(self, theme_changed_callback=None):
method _finish_init (line 120) | def _finish_init(self):
method _prompt_restart_for_vision_mode_change (line 123) | def _prompt_restart_for_vision_mode_change(self) -> None:
method _restart_application (line 135) | def _restart_application(self) -> None:
method _generate_params_section (line 156) | def _generate_params_section(self, model: BaseModel, section_readable_...
method _generate_parameter_value_widget (line 185) | def _generate_parameter_value_widget(
method show_tab (line 260) | def show_tab(self):
method reset_button_click (line 265) | def reset_button_click(self):
method _reset_values_for_model (line 281) | def _reset_values_for_model(self, model, section_config_header):
method _setup_reset_button (line 300) | def _setup_reset_button(self) -> QPushButton:
class IgnoreScrollWheelComboBox (line 306) | class IgnoreScrollWheelComboBox(QComboBox):
method __init__ (line 307) | def __init__(self):
method wheelEvent (line 311) | def wheelEvent(self, event):
method reset_values (line 317) | def reset_values(self, value):
class QChestTabWidget (line 323) | class QChestTabWidget(QWidget):
method __init__ (line 324) | def __init__(self, model, section_header, config_key, chest_tab_config...
method reset_values (line 342) | def reset_values(self, chest_tab_config: list[int]):
method _save_changes_on_box_change (line 346) | def _save_changes_on_box_change(self, model, section_header, config_key):
class QMoveItemsWidget (line 351) | class QMoveItemsWidget(QWidget):
method __init__ (line 352) | def __init__(self, model, section_header, config_key, move_selections:...
method reset_values (line 375) | def reset_values(self, move_selections: list[MoveItemsType]):
method _launch_picker (line 378) | def _launch_picker(self, model, section_header, config_key, move_selec...
class QMoveItemsPicker (line 389) | class QMoveItemsPicker(QDialog):
method __init__ (line 390) | def __init__(self, parent, move_selections):
method get_selected_move_types (line 423) | def get_selected_move_types(self) -> list[MoveItemsType]:
class QProfilesWidget (line 439) | class QProfilesWidget(QWidget):
method __init__ (line 440) | def __init__(self, model, section_header, config_key, current_profiles):
method reset_values (line 463) | def reset_values(self, current_profiles):
method _launch_picker (line 466) | def _launch_picker(self, model, section_header, config_key, current_pr...
class QProfilePicker (line 476) | class QProfilePicker(QDialog):
method __init__ (line 477) | def __init__(self, parent, current_profiles):
method move_items (line 550) | def move_items(self, source_list, destination_list):
method get_selected_profiles (line 555) | def get_selected_profiles(self):
class QHotkeyWidget (line 561) | class QHotkeyWidget(QWidget):
method __init__ (line 562) | def __init__(self, model, section_header, config_key, current_value):
method reset_values (line 576) | def reset_values(self, current_value):
method _launch_hotkey_dialog (line 579) | def _launch_hotkey_dialog(self, model, section_header, config_key):
class HotkeyListenerDialog (line 587) | class HotkeyListenerDialog(QDialog):
method __init__ (line 588) | def __init__(self, parent=None, hotkey=""):
method keyPressEvent (line 616) | def keyPressEvent(self, event):
method get_hotkey (line 644) | def get_hotkey(self):
FILE: src/gui/config_window.py
class ConfigWindow (line 17) | class ConfigWindow(QMainWindow):
method __init__ (line 20) | def __init__(self, parent=None, theme_changed_callback=None):
method _on_theme_changed (line 42) | def _on_theme_changed(self):
method _rebuild_tab (line 49) | def _rebuild_tab(self):
method closeEvent (line 55) | def closeEvent(self, event):
FILE: src/gui/d4lfitem.py
class D4LFItem (line 20) | class D4LFItem(QGroupBox):
method __init__ (line 21) | def __init__(self, item: DynamicItemFilterModel, affixesNames, allItem...
method load_item (line 82) | def load_item(self):
method create_affix_combobox (line 97) | def create_affix_combobox(self, affix_name):
method create_alert (line 124) | def create_alert(self, msg: str):
method create_form_layout (line 128) | def create_form_layout(self, minCount, minGreaterAffixCount):
method set_minPower (line 146) | def set_minPower(self, minPower):
method set_minGreaterAffix (line 149) | def set_minGreaterAffix(self, minGreaterAffix):
method set_minCount (line 155) | def set_minCount(self, minCount):
method find_affix_from_value (line 161) | def find_affix_from_value(self, target_value):
method find_item_from_value (line 167) | def find_item_from_value(self, target_value):
method save_item (line 173) | def save_item(self):
method save_item_create (line 198) | def save_item_create(self):
method item_changed (line 237) | def item_changed(self):
method has_changes (line 240) | def has_changes(self):
FILE: src/gui/dialog.py
class IgnoreScrollWheelSpinBox (line 35) | class IgnoreScrollWheelSpinBox(QSpinBox):
method __init__ (line 36) | def __init__(self):
method wheelEvent (line 40) | def wheelEvent(self, event):
class MinPowerDialog (line 47) | class MinPowerDialog(QDialog):
method __init__ (line 48) | def __init__(self, parent=None):
method get_value (line 73) | def get_value(self):
class MinGreaterDialog (line 77) | class MinGreaterDialog(QDialog):
method __init__ (line 78) | def __init__(self, parent=None):
method get_value (line 103) | def get_value(self):
class MinPercentDialog (line 107) | class MinPercentDialog(QDialog):
method __init__ (line 108) | def __init__(self, parent=None):
method get_value (line 133) | def get_value(self):
class CreateItem (line 137) | class CreateItem(QDialog):
method __init__ (line 138) | def __init__(self, item_list: list[str], parent=None):
method accept (line 164) | def accept(self):
method get_value (line 173) | def get_value(self):
class DeleteItem (line 186) | class DeleteItem(QDialog):
method __init__ (line 187) | def __init__(self, item_names, parent=None):
method get_value (line 227) | def get_value(self):
class DeleteAffixPool (line 231) | class DeleteAffixPool(QDialog):
method __init__ (line 232) | def __init__(self, nb_affix_pool, inherent: bool = False, parent=None):
method get_value (line 276) | def get_value(self):
class CreateSigil (line 280) | class CreateSigil(QDialog):
method __init__ (line 281) | def __init__(self, whitelist_sigils: list[str], blacklist_sigils: list...
method accept (line 321) | def accept(self):
method get_value (line 330) | def get_value(self):
class RemoveSigil (line 336) | class RemoveSigil(QDialog):
method __init__ (line 337) | def __init__(self, sigils: list[str], blacklist: bool = False, parent=...
method get_value (line 383) | def get_value(self):
class CreateTribute (line 387) | class CreateTribute(QDialog):
method __init__ (line 388) | def __init__(self, tributes: list[str], parent=None):
method accept (line 420) | def accept(self):
method get_value (line 431) | def get_value(self):
class AddTributeRarity (line 437) | class AddTributeRarity(QDialog):
method __init__ (line 438) | def __init__(self, rarities: list[ItemRarity], parent=None):
method accept (line 470) | def accept(self):
method get_value (line 483) | def get_value(self):
class RemoveTribute (line 488) | class RemoveTribute(QDialog):
method __init__ (line 489) | def __init__(self, tributes: list[str], parent=None):
method get_value (line 531) | def get_value(self):
class AddAspectUpgrade (line 536) | class AddAspectUpgrade(QDialog):
method __init__ (line 537) | def __init__(self, aspect_upgrades: list[str], parent=None):
method get_value (line 572) | def get_value(self):
class CreateUnique (line 576) | class CreateUnique(QDialog):
method __init__ (line 577) | def __init__(self, parent=None):
method get_value (line 616) | def get_value(self):
FILE: src/gui/importer/d4builds.py
class D4BuildsException (line 71) | class D4BuildsException(Exception):
function import_d4builds (line 76) | def import_d4builds(config: ImportConfig, driver: ChromiumDriver = None):
function _corrections (line 225) | def _corrections(input_str: str) -> str:
function _extract_build_metadata (line 237) | def _extract_build_metadata(data: lxml.html.HtmlElement) -> tuple[str, s...
function _extract_variant_name (line 251) | def _extract_variant_name(data: lxml.html.HtmlElement) -> str:
function _extract_d4builds_season_number (line 259) | def _extract_d4builds_season_number(data: lxml.html.HtmlElement) -> str:
function _get_item_slots (line 268) | def _get_item_slots(data: lxml.html.HtmlElement) -> dict[str, tuple[str,...
function _get_legendary_aspects (line 291) | def _get_legendary_aspects(data: lxml.html.HtmlElement) -> list[str]:
function _get_affix_name (line 311) | def _get_affix_name(stat: lxml.html.HtmlElement) -> str:
FILE: src/gui/importer/diablo_trade.py
class _AnnotatedFilter (line 31) | class _AnnotatedFilter:
class _Listing (line 38) | class _Listing:
class DiabloTradeException (line 48) | class DiabloTradeException(Exception):
function import_diablo_trade (line 53) | def import_diablo_trade(url: str, max_listings: int, driver: seleniumbas...
function _construct_api_url (line 126) | def _construct_api_url(listing_url: str, cursor: int = 1) -> str:
function _create_affixes_from_api_dict (line 141) | def _create_affixes_from_api_dict(affixes: list[dict[str, Any]]) -> list...
function _create_filters_from_items (line 156) | def _create_filters_from_items(items: list[_Listing]) -> list[dict[str, ...
FILE: src/gui/importer/gui_common.py
function extract_digits (line 44) | def extract_digits(text: str) -> int:
function fix_weapon_type (line 48) | def fix_weapon_type(input_str: str) -> ItemType | None:
function fix_offhand_type (line 87) | def fix_offhand_type(input_str: str, class_str: str) -> ItemType | None:
function format_number_as_short_string (line 104) | def format_number_as_short_string(n: int) -> str:
function get_class_name (line 109) | def get_class_name(input_str: str) -> str:
function normalize_profile_file_name (line 119) | def normalize_profile_file_name(file_name: str) -> str:
function build_default_profile_file_name (line 125) | def build_default_profile_file_name(
function _clean_build_header (line 144) | def _clean_build_header(source_name: str, build_header: str, season_numb...
function _normalize_profile_name_part (line 165) | def _normalize_profile_name_part(name_part: str) -> str:
function update_mingreateraffixcount (line 169) | def update_mingreateraffixcount(item_filter: ItemFilterModel, require_ga...
function sort_profile_filters (line 179) | def sort_profile_filters(filters: list[dict[str, ItemFilterModel]]) -> l...
function _profile_filter_sort_key (line 183) | def _profile_filter_sort_key(filter_entry: dict[str, ItemFilterModel]) -...
function get_with_retry (line 188) | def get_with_retry(url: str, custom_headers: dict[str, str] | None = Non...
function handle_popups (line 203) | def handle_popups[D: WebDriver | WebElement, T](
function match_to_enum (line 217) | def match_to_enum(enum_class, target_string: str, check_keys: bool = Fal...
function retry_importer (line 227) | def retry_importer(func=None, inject_webdriver: bool = False, uc=False):
function save_as_profile (line 249) | def save_as_profile(file_name: str, profile: ProfileModel, url: str, exc...
function add_to_profiles (line 269) | def add_to_profiles(build_name):
function _to_yaml_str (line 280) | def _to_yaml_str(profile: ProfileModel, exclude_defaults: bool, exclude:...
function _sort_profile_sections (line 294) | def _sort_profile_sections(d):
function _use_block_style (line 299) | def _use_block_style(d):
function _rm_style_info (line 304) | def _rm_style_info(d):
function setup_webdriver (line 316) | def setup_webdriver(uc: bool = False) -> ChromiumDriver:
FILE: src/gui/importer/importer_config.py
class ImportConfig (line 5) | class ImportConfig:
FILE: src/gui/importer/maxroll.py
class MaxrollException (line 50) | class MaxrollException(Exception):
function import_maxroll (line 55) | def import_maxroll(config: ImportConfig):
function _attribute_description_corrections (line 198) | def _attribute_description_corrections(input_str: str) -> str:
function _find_item_rarity (line 207) | def _find_item_rarity(resolved_item_id, mapping_data) -> ItemRarity:
function _find_item_affixes (line 221) | def _find_item_affixes(
function _find_skill_rank_affix_description (line 299) | def _find_skill_rank_affix_description(mapping_data: dict, affix_key: st...
function _find_skill_rank_label_from_descriptions (line 310) | def _find_skill_rank_label_from_descriptions(mapping_data: dict, param: ...
function _find_skill_rank_label_from_affix_key (line 325) | def _find_skill_rank_label_from_affix_key(affix_key: str) -> str:
function _find_legendary_aspect (line 336) | def _find_legendary_aspect(mapping_data: dict, legendary_aspect: dict) -...
function _attr_desc_special_handling (line 356) | def _attr_desc_special_handling(affix_id: str) -> str:
function _unique_name_special_handling (line 382) | def _unique_name_special_handling(unique_name: str) -> str:
function _find_item_type (line 392) | def _find_item_type(mapping_data: dict, value: str, class_name: str = ""...
function _normalize_item_type_str_for_import_helpers (line 410) | def _normalize_item_type_str_for_import_helpers(item_type_str: str) -> str:
function _extract_planner_url_and_id_from_planner (line 418) | def _extract_planner_url_and_id_from_planner(url: str) -> tuple[str, int...
function _extract_planner_url_and_id_from_guide (line 440) | def _extract_planner_url_and_id_from_guide(url: str) -> tuple[str, int, ...
function _resolve_visible_profile_index (line 462) | def _resolve_visible_profile_index(profiles: list[dict], visible_profile...
function _extract_guide_profile_id (line 473) | def _extract_guide_profile_id(embed: lxml.html.HtmlElement) -> int | None:
function _extract_active_guide_embed_tab_index (line 486) | def _extract_active_guide_embed_tab_index(embed: lxml.html.HtmlElement) ...
FILE: src/gui/importer/mobalytics.py
class MobalyticsException (line 45) | class MobalyticsException(Exception):
function import_mobalytics (line 50) | def import_mobalytics(config: ImportConfig):
function _corrections (line 235) | def _corrections(input_str: str) -> str:
function _fix_input_url (line 242) | def _fix_input_url(url: str) -> str:
function _extract_mobalytics_season_number (line 246) | def _extract_mobalytics_season_number(full_script_data_json: dict) -> str:
function _get_legendary_aspect (line 257) | def _get_legendary_aspect(name: str) -> str:
function _convert_raw_to_affixes (line 270) | def _convert_raw_to_affixes(raw_stats: list[dict], import_greater_affixe...
FILE: src/gui/importer/paragon_export.py
function _class_slug_from_name (line 30) | def _class_slug_from_name(class_name: str) -> str:
function _prefix_with_class_slug (line 39) | def _prefix_with_class_slug(slug: str, class_slug: str) -> str:
function _slugify (line 282) | def _slugify(s: str) -> str:
function _maxroll_class_slug (line 289) | def _maxroll_class_slug(board_id: str) -> str:
function _maxroll_board_slug (line 295) | def _maxroll_board_slug(board_id: str) -> str:
function _maxroll_glyph_slug (line 302) | def _maxroll_glyph_slug(glyph_id: str, board_id: str) -> str:
function build_paragon_profile_payload (line 316) | def build_paragon_profile_payload(
function extract_maxroll_paragon_steps (line 338) | def extract_maxroll_paragon_steps(active_profile: dict[str, Any]) -> lis...
function _fix_mobalytics_starting_board_slug (line 389) | def _fix_mobalytics_starting_board_slug(board_slug: str) -> str:
function extract_mobalytics_paragon_steps (line 396) | def extract_mobalytics_paragon_steps(paragon_data: dict[str, Any]) -> li...
function _parse_d4builds_paragon_boards (line 455) | def _parse_d4builds_paragon_boards(driver: WebDriver, class_slug: str) -...
function extract_d4builds_paragon_steps (line 577) | def extract_d4builds_paragon_steps(
function _rotation_info_maxroll (line 636) | def _rotation_info_maxroll(rot: int) -> str:
function _rotation_info_degrees (line 640) | def _rotation_info_degrees(rot: int) -> str:
function _transform_maxroll_location (line 645) | def _transform_maxroll_location(loc: int, rotation: int) -> int:
function _transform_xy_common (line 678) | def _transform_xy_common(x: int, y: int, rotation_deg: int, base: str) -...
FILE: src/gui/importer_window.py
class ImporterWindow (line 35) | class ImporterWindow(QMainWindow):
method __init__ (line 38) | def __init__(self, parent=None):
method _generate_checkbox (line 198) | def _generate_checkbox(self, name, settings_value, desc, default_value...
method _handle_text_changed (line 208) | def _handle_text_changed(self, text):
method _generate_button_click (line 212) | def _generate_button_click(self):
method _on_worker_finished (line 243) | def _on_worker_finished(self):
method closeEvent (line 249) | def closeEvent(self, event):
class _GuiLogHandler (line 265) | class _GuiLogHandler(logging.Handler):
method __init__ (line 268) | def __init__(self, text_widget: QTextEdit):
method emit (line 277) | def emit(self, record):
method _append_log (line 285) | def _append_log(self, message):
class _LogSignals (line 291) | class _LogSignals(QObject):
class _Worker (line 297) | class _Worker(QRunnable):
method __init__ (line 298) | def __init__(self, name, fn, *args, **kwargs):
method run (line 307) | def run(self):
class _WorkerSignals (line 313) | class _WorkerSignals(QObject):
FILE: src/gui/open_user_config_button.py
class OpenUserConfigButton (line 8) | class OpenUserConfigButton(QPushButton):
method __init__ (line 9) | def __init__(self):
method _open_userconfig_directory (line 14) | def _open_userconfig_directory():
FILE: src/gui/profile_editor/affixes_tab.py
function _item_type_summary (line 58) | def _item_type_summary(item_types: list[ItemType]) -> str:
class ItemTypePicker (line 64) | class ItemTypePicker(QDialog):
method __init__ (line 65) | def __init__(self, parent: QWidget, item_types: list[ItemType], select...
method _create_item_type_group (line 97) | def _create_item_type_group(
method clear_selection (line 119) | def clear_selection(self):
method get_selected_item_types (line 123) | def get_selected_item_types(self) -> list[ItemType]:
class AffixGroupEditor (line 127) | class AffixGroupEditor(QWidget):
method __init__ (line 128) | def __init__(self, dynamic_filter: DynamicItemFilterModel, parent=None):
method setup_ui (line 138) | def setup_ui(self):
method create_unique_aspect_groupbox (line 253) | def create_unique_aspect_groupbox(self):
method _refresh_widget_style (line 296) | def _refresh_widget_style(self, widget):
method set_unique_aspect_controls_enabled (line 300) | def set_unique_aspect_controls_enabled(self):
method update_unique_aspect_name (line 306) | def update_unique_aspect_name(self, current_text=None):
method refresh_unique_aspect_value_input (line 323) | def refresh_unique_aspect_value_input(self):
method update_unique_aspect_mode (line 345) | def update_unique_aspect_mode(self, current_text=None):
method update_unique_aspect_value (line 357) | def update_unique_aspect_value(self, value):
method init_affix_pool (line 379) | def init_affix_pool(self):
method init_inherent_pool (line 385) | def init_inherent_pool(self):
method add_affix_pool_item (line 391) | def add_affix_pool_item(self, pool: AffixFilterCountModel, inherent: b...
method add_affix_pool (line 409) | def add_affix_pool(self):
method add_inherent_pool (line 419) | def add_inherent_pool(self):
method remove_selected (line 429) | def remove_selected(self, layout_widget: QVBoxLayout, inherent: bool =...
method reorganize_pool (line 448) | def reorganize_pool(self, layout_widget: QVBoxLayout):
method refresh_item_type_summary (line 454) | def refresh_item_type_summary(self):
method edit_item_types (line 457) | def edit_item_types(self):
method update_min_power (line 463) | def update_min_power(self):
method update_min_greater_affix (line 466) | def update_min_greater_affix(self):
method toggle_auto_sync (line 469) | def toggle_auto_sync(self):
method _update_auto_sync_count (line 492) | def _update_auto_sync_count(self):
method sync_min_greater_from_checkboxes (line 497) | def sync_min_greater_from_checkboxes(self):
method _ensure_pool_widgets_initialized (line 502) | def _ensure_pool_widgets_initialized(self):
method iter_affix_widgets (line 510) | def iter_affix_widgets(self):
method count_want_greater_affixes (line 530) | def count_want_greater_affixes(self):
method update_greater_count_label (line 542) | def update_greater_count_label(self):
method convert_all_to_min_percent_of_affix (line 551) | def convert_all_to_min_percent_of_affix(self, percent: int):
class AffixPoolWidget (line 556) | class AffixPoolWidget(QWidget):
method __init__ (line 557) | def __init__(self, pool: AffixFilterCountModel, parent=None):
method setup_ui (line 562) | def setup_ui(self):
method _refresh_widget_style (line 645) | def _refresh_widget_style(self, widget):
method add_affix_item (line 649) | def add_affix_item(self, affix: AffixFilterModel):
method add_affix (line 656) | def add_affix(self):
method remove_selected (line 661) | def remove_selected(self, list_widget: QListWidget):
method update_min_count (line 667) | def update_min_count(self):
method update_max_count (line 670) | def update_max_count(self):
class AffixWidget (line 674) | class AffixWidget(QWidget):
method __init__ (line 675) | def __init__(self, affix: AffixFilterModel, parent=None):
method setup_ui (line 680) | def setup_ui(self):
method create_affix_name_combobox (line 699) | def create_affix_name_combobox(self):
method create_greater_checkbox (line 712) | def create_greater_checkbox(self):
method _refresh_widget_style (line 721) | def _refresh_widget_style(self, widget):
method update_parent_count_label (line 725) | def update_parent_count_label(self):
method create_mode_combobox (line 734) | def create_mode_combobox(self):
method create_value_input (line 743) | def create_value_input(self):
method update_name (line 748) | def update_name(self, current_text=None):
method refresh_value_input (line 756) | def refresh_value_input(self):
method update_mode (line 770) | def update_mode(self, current_text=None):
method update_value (line 778) | def update_value(self, value):
method update_greater (line 798) | def update_greater(self):
method set_min_percent (line 801) | def set_min_percent(self, percent: int, convert_mode: bool = False):
class AffixesTab (line 809) | class AffixesTab(QWidget):
method __init__ (line 810) | def __init__(self, affixes_model: list[DynamicItemFilterModel], parent...
method load (line 815) | def load(self):
method setup_ui (line 820) | def setup_ui(self):
method show_message (line 870) | def show_message(self, text):
method add_item_type (line 873) | def add_item_type(self):
method close_tab (line 884) | def close_tab(self, index):
method remove_item_type (line 889) | def remove_item_type(self):
method set_all_minGreaterAffix (line 900) | def set_all_minGreaterAffix(self):
method convert_all_to_min_percent_of_affix (line 911) | def convert_all_to_min_percent_of_affix(self):
method set_all_minPower (line 918) | def set_all_minPower(self):
FILE: src/gui/profile_editor/aspect_upgrades_tab.py
class AspectUpgradesTab (line 9) | class AspectUpgradesTab(QWidget):
method __init__ (line 10) | def __init__(self, aspect_upgrades: list[str], parent=None):
method load (line 16) | def load(self):
method setup_ui (line 21) | def setup_ui(self):
method create_button_layout (line 36) | def create_button_layout(self) -> QHBoxLayout:
method add_aspect (line 49) | def add_aspect(self):
method remove_aspect (line 56) | def remove_aspect(self):
FILE: src/gui/profile_editor/global_uniques_tab.py
class UniqueWidget (line 24) | class UniqueWidget(QWidget):
method __init__ (line 25) | def __init__(self, unique_model: GlobalUniqueModel, parent=None):
method setup_ui (line 31) | def setup_ui(self):
method create_general_groupbox (line 48) | def create_general_groupbox(self):
method update_profile_alias (line 83) | def update_profile_alias(self, value: str):
method update_min_power (line 86) | def update_min_power(self):
method update_min_greater_affix (line 89) | def update_min_greater_affix(self):
method update_min_percent (line 92) | def update_min_percent(self):
class UniquesTab (line 96) | class UniquesTab(QWidget):
method __init__ (line 97) | def __init__(self, unique_model_list: list[GlobalUniqueModel], parent=...
method load (line 102) | def load(self):
method setup_ui (line 107) | def setup_ui(self):
method close_tab (line 136) | def close_tab(self, index):
method remove_item_type (line 141) | def remove_item_type(self):
method rename_tabs (line 155) | def rename_tabs(self):
method add_item_type (line 159) | def add_item_type(self):
FILE: src/gui/profile_editor/profile_editor.py
class ProfileEditor (line 17) | class ProfileEditor(QTabWidget):
method __init__ (line 21) | def __init__(self, profile_model: ProfileModel, parent=None):
method tab_changed (line 46) | def tab_changed(self, index):
method show_warning (line 59) | def show_warning():
method save_all (line 72) | def save_all(self):
FILE: src/gui/profile_editor/sigils_tab.py
class ConditionWidget (line 25) | class ConditionWidget(QWidget):
method __init__ (line 28) | def __init__(self, condition: str, parent=None):
method update_condition (line 49) | def update_condition(self):
class SigilWidget (line 55) | class SigilWidget(Container):
method __init__ (line 58) | def __init__(self, sigil_name: str, sigil: SigilConditionModel, whitel...
method setup_ui (line 65) | def setup_ui(self):
method add_condition_to_list (line 109) | def add_condition_to_list(self, condition):
method add_condition (line 117) | def add_condition(self):
method remove_selected (line 121) | def remove_selected(self):
method revert_sigil_dungeon (line 127) | def revert_sigil_dungeon(self):
method update_sigil_dungeon (line 134) | def update_sigil_dungeon(self, classic=True):
method on_condition_update (line 144) | def on_condition_update(self, old_condition, condition: str):
class SigilsTab (line 151) | class SigilsTab(QWidget):
method __init__ (line 152) | def __init__(self, sigil_model: SigilFilterModel, parent=None):
method load (line 157) | def load(self):
method setup_ui (line 162) | def setup_ui(self):
method create_button_layout (line 171) | def create_button_layout(self):
method create_form (line 188) | def create_form(self):
method create_containers (line 201) | def create_containers(self):
method add_sigil (line 223) | def add_sigil(self, sigil_condition: SigilConditionModel, whitelist: b...
method create_sigil (line 234) | def create_sigil(self):
method remove_sigil (line 253) | def remove_sigil(self, blacklist: bool = False):
method update_priority (line 280) | def update_priority(self):
method on_dungeon_changed (line 283) | def on_dungeon_changed(self, sigil_widget: SigilWidget):
FILE: src/gui/profile_editor/tributes_tab.py
class TributesTab (line 21) | class TributesTab(QWidget):
method __init__ (line 22) | def __init__(self, tributes: list[TributeFilterModel] | None, parent=N...
method load (line 28) | def load(self):
method setup_ui (line 33) | def setup_ui(self):
method create_button_layout (line 50) | def create_button_layout(self) -> QHBoxLayout:
method _reload_tribute_list_widget (line 67) | def _reload_tribute_list_widget(self):
method _display_text (line 73) | def _display_text(tribute: TributeFilterModel) -> str:
method add_tribute (line 88) | def add_tribute(self):
method add_rarity (line 95) | def add_rarity(self):
method remove_selected (line 102) | def remove_selected(self):
method _existing_tribute_names (line 114) | def _existing_tribute_names(self) -> list[str]:
method _existing_rarities (line 117) | def _existing_rarities(self) -> list[ItemRarity]:
FILE: src/gui/profile_editor_window.py
class ProfileEditorWindow (line 18) | class ProfileEditorWindow(QMainWindow):
method __init__ (line 21) | def __init__(self, parent=None):
method _finish_construction (line 40) | def _finish_construction(self):
method closeEvent (line 45) | def closeEvent(self, event):
FILE: src/gui/profile_tab.py
class ProfileTab (line 32) | class ProfileTab(QWidget):
method __init__ (line 33) | def __init__(self):
method confirm_discard_changes (line 93) | def confirm_discard_changes(self):
method create_alert (line 105) | def create_alert(self, msg: str):
method show_tab (line 109) | def show_tab(self):
method load_file (line 114) | def load_file(self):
method open_file (line 122) | def open_file(self):
method load (line 132) | def load(self):
method create_profile_editor (line 155) | def create_profile_editor(self):
method load_yaml (line 162) | def load_yaml(self):
method update_filename_label (line 220) | def update_filename_label(self):
method save_yaml (line 227) | def save_yaml(self):
method check_close_save (line 231) | def check_close_save(self):
method refresh (line 236) | def refresh(self):
FILE: src/gui/unified_window.py
function ansi_to_html (line 68) | def ansi_to_html(text: str) -> str:
class ANSIConsoleWidget (line 99) | class ANSIConsoleWidget(QPlainTextEdit):
method __init__ (line 100) | def __init__(self, parent=None):
method append_ansi_text (line 105) | def append_ansi_text(self, text: str):
class QtConsoleHandler (line 111) | class QtConsoleHandler(logging.Handler, QObject):
method __init__ (line 114) | def __init__(self):
method emit (line 118) | def emit(self, record):
class QtActivityHandler (line 123) | class QtActivityHandler(logging.Handler, QObject):
method __init__ (line 126) | def __init__(self):
method emit (line 130) | def emit(self, record):
class BackendWorker (line 135) | class BackendWorker(QObject):
method run (line 138) | def run(self):
class UnifiedMainWindow (line 166) | class UnifiedMainWindow(QMainWindow):
method __init__ (line 167) | def __init__(self):
method _show_singleton_modal (line 283) | def _show_singleton_modal(self, window_attr: str, window_class, *args,...
method _emit_deferred_config_cleanup_logs (line 316) | def _emit_deferred_config_cleanup_logs(self, config):
method open_import_dialog (line 325) | def open_import_dialog(self):
method open_settings_dialog (line 332) | def open_settings_dialog(self):
method open_profile_editor (line 339) | def open_profile_editor(self):
method restore_geometry (line 345) | def restore_geometry(self):
method save_geometry (line 363) | def save_geometry(self):
method closeEvent (line 373) | def closeEvent(self, event):
method emit_startup_direct_to_console (line 395) | def emit_startup_direct_to_console(self):
method apply_theme (line 405) | def apply_theme(self):
FILE: src/item/data/affix.py
class AffixType (line 5) | class AffixType(enum.Enum):
class Affix (line 14) | class Affix:
method __eq__ (line 23) | def __eq__(self, other: Affix) -> bool:
FILE: src/item/data/aspect.py
class Aspect (line 5) | class Aspect:
method __eq__ (line 13) | def __eq__(self, other: Aspect) -> bool:
FILE: src/item/data/item_type.py
class ItemType (line 5) | class ItemType(Enum):
function is_armor (line 55) | def is_armor(item_type: ItemType) -> bool:
function is_consumable (line 66) | def is_consumable(item_type: ItemType) -> bool:
function is_non_sigil_mapping (line 70) | def is_non_sigil_mapping(item_type: ItemType) -> bool:
function is_sigil (line 74) | def is_sigil(item_type: ItemType) -> bool:
function is_jewelry (line 78) | def is_jewelry(item_type: ItemType) -> bool:
function is_socketable (line 82) | def is_socketable(item_type: ItemType) -> bool:
function is_weapon (line 86) | def is_weapon(item_type: ItemType) -> bool:
FILE: src/item/data/rarity.py
class ItemRarity (line 4) | class ItemRarity(Enum):
FILE: src/item/data/seasonal_attribute.py
class SeasonalAttribute (line 4) | class SeasonalAttribute(Enum):
FILE: src/item/descr/__init__.py
function keep_letters_and_spaces (line 1) | def keep_letters_and_spaces(text):
FILE: src/item/descr/read_descr_tts.py
function _get_affix_counts (line 61) | def _get_affix_counts(tts_section: list[str], item: Item, start: int) ->...
function _add_affixes_from_tts (line 97) | def _add_affixes_from_tts(tts_section: list[str], item: Item) -> Item:
function _add_affixes_from_tts_mixed (line 121) | def _add_affixes_from_tts_mixed(
function _raise_index_error (line 167) | def _raise_index_error(affixes, affix_bullets, item, img_item_descr: np....
function _add_sigil_affixes_from_tts (line 185) | def _add_sigil_affixes_from_tts(tts_section: list[str], item: Item) -> I...
function _create_base_item_from_tts (line 212) | def _create_base_item_from_tts(tts_item: list[str]) -> Item | None:
function _update_item_object (line 295) | def _update_item_object(item: Item, rarity=None, item_type=None) -> Item:
function _get_affix_starting_location_from_tts_section (line 304) | def _get_affix_starting_location_from_tts_section(tts_section: list[str]...
function _get_index_of_armor_dps_or_all_resist (line 320) | def _get_index_of_armor_dps_or_all_resist(tts_section: list[str], indica...
function _get_affixes_from_tts_section (line 328) | def _get_affixes_from_tts_section(tts_section: list[str], start: int, le...
function _get_aspect_from_tts_section (line 332) | def _get_aspect_from_tts_section(tts_section: list[str], item: Item, sta...
function _get_affix_from_text (line 341) | def _get_affix_from_text(text: str) -> Affix:
function _has_numbers (line 388) | def _has_numbers(affix_text):
function _get_aspect_from_text (line 393) | def _get_aspect_from_text(text: str, name: str) -> Aspect:
function _get_aspect_from_name (line 417) | def _get_aspect_from_name(text: str, name: str) -> Aspect | None:
function _get_item_rarity (line 426) | def _get_item_rarity(data: str) -> ItemRarity | None:
function _get_item_type (line 430) | def _get_item_type(data: str):
function _is_codex_upgrade (line 434) | def _is_codex_upgrade(tts_section: list[str]) -> bool:
function _is_cosmetic_upgrade (line 441) | def _is_cosmetic_upgrade(tts_section: list[str]):
function read_descr_mixed (line 445) | def read_descr_mixed(img_item_descr: np.ndarray) -> Item | None:
function read_descr (line 490) | def read_descr() -> Item | None:
FILE: src/item/descr/text.py
function closest_match (line 9) | def closest_match(target, candidates):
function closest_to (line 17) | def closest_to(value, choices):
function find_number (line 21) | def find_number(s: str, idx: int = 0) -> float | None:
function remove_text_after_first_keyword (line 38) | def remove_text_after_first_keyword(text: str, keywords: list[str]) -> str:
function clean_str (line 49) | def clean_str(s: str) -> str:
FILE: src/item/descr/texture.py
function find_seperators_long (line 11) | def find_seperators_long(img_item_descr: np.ndarray, sep_short_match: Te...
function find_seperator_short (line 31) | def find_seperator_short(img_item_descr: np.ndarray) -> TemplateMatch:
function _filter_outliers (line 45) | def _filter_outliers(template_matches: list[TemplateMatch]) -> list[Temp...
function _find_bullets (line 56) | def _find_bullets(
function find_affix_bullets (line 83) | def find_affix_bullets(img_item_descr: np.ndarray, sep_short_match: Temp...
function find_aspect_bullet (line 117) | def find_aspect_bullet(img_item_descr: np.ndarray, sep_short_match: Temp...
function find_aspect_search_area (line 134) | def find_aspect_search_area(img_item_descr: np.ndarray, aspect_bullet: T...
function find_codex_upgrade_icon (line 148) | def find_codex_upgrade_icon(img_item_descr: np.ndarray, aspect_bullet: T...
FILE: src/item/filter.py
class MatchedFilter (line 40) | class MatchedFilter:
class FilterResult (line 47) | class FilterResult:
class _UniqueKeyLoader (line 52) | class _UniqueKeyLoader(yaml.SafeLoader):
method construct_mapping (line 53) | def construct_mapping(self, node: MappingNode, deep=False):
class Filter (line 65) | class Filter:
method __new__ (line 81) | def __new__(cls):
method _check_affixes (line 86) | def _check_affixes(self, item: Item) -> FilterResult:
method _check_legendary_aspect (line 153) | def _check_legendary_aspect(self, item: Item) -> FilterResult:
method _check_cosmetic (line 179) | def _check_cosmetic(item: Item) -> FilterResult:
method _check_sigil (line 192) | def _check_sigil(self, item: Item) -> FilterResult:
method _check_tribute (line 233) | def _check_tribute(self, item: Item) -> FilterResult:
method _check_global_unique_filter (line 258) | def _check_global_unique_filter(self, item: Item) -> FilterResult:
method _did_files_change (line 288) | def _did_files_change(self) -> bool:
method _match_affixes_count (line 304) | def _match_affixes_count(
method _match_affixes_sigils (line 347) | def _match_affixes_sigils(
method _match_affixes_uniques (line 360) | def _match_affixes_uniques(
method _match_greater_affix_count (line 394) | def _match_greater_affix_count(expected_min_count: int, item_affixes: ...
method _match_item_roll_is_in_percent_range (line 398) | def _match_item_roll_is_in_percent_range(expected_percent: int, item_a...
method _is_smaller_roll_better (line 418) | def _is_smaller_roll_better(item_aspect_or_affix: Aspect | Affix) -> b...
method _match_item_value_threshold (line 426) | def _match_item_value_threshold(expected_value: float, item_aspect_or_...
method _match_item_aspect_or_affix (line 431) | def _match_item_aspect_or_affix(
method _match_item_power (line 462) | def _match_item_power(min_power: int, item_power: int, max_power: int ...
method _match_item_type (line 466) | def _match_item_type(expected_item_types: list[ItemType], item_type: I...
method load_files (line 471) | def load_files(self):
method get_paragon_filters (line 554) | def get_paragon_filters(self) -> dict[str, object]:
method should_keep (line 560) | def should_keep(self, item: Item) -> FilterResult:
FILE: src/item/find_descr.py
function _choose_best_result (line 23) | def _choose_best_result(res_left: SearchResult, res_right: SearchResult)...
function _template_search (line 33) | def _template_search(img: np.ndarray, anchor: int, roi: np.ndarray, take...
function find_descr (line 49) | def find_descr(
FILE: src/item/models.py
class Item (line 17) | class Item:
method __eq__ (line 31) | def __eq__(self, other):
class ItemJSONEncoder (line 69) | class ItemJSONEncoder(json.JSONEncoder):
method default (line 70) | def default(self, o):
FILE: src/logger.py
class ThreadNameFilter (line 27) | class ThreadNameFilter(logging.Filter):
method filter (line 28) | def filter(self, record):
class ColoredFormatter (line 34) | class ColoredFormatter(logging.Formatter):
method __init__ (line 35) | def __init__(
method format (line 55) | def format(self, record: logging.LogRecord) -> str:
function _setup_log_filename (line 60) | def _setup_log_filename(fmt: str) -> str:
function create_formatter (line 69) | def create_formatter(colored=False):
function setup (line 76) | def setup(log_level: str = "DEBUG", *, enable_stdout: bool = True) -> None:
function clean_up_old_log_files (line 118) | def clean_up_old_log_files():
function _log_unhandled_exceptions (line 130) | def _log_unhandled_exceptions(args: typing.Any) -> None:
FILE: src/loot_mover.py
function move_items_to_stash (line 17) | def move_items_to_stash():
function move_items_to_inventory (line 50) | def move_items_to_inventory():
function _move_items (line 86) | def _move_items(
FILE: src/main.py
function main (line 42) | def main():
function check_for_proper_tts_configuration (line 107) | def check_for_proper_tts_configuration():
function get_d4_local_prefs_file (line 175) | def get_d4_local_prefs_file() -> Path | None:
function hide_console (line 197) | def hide_console():
FILE: src/overlay.py
class Overlay (line 10) | class Overlay:
method __init__ (line 11) | def __init__(self):
method run (line 21) | def run(self):
FILE: src/paragon_overlay.py
function _tk_thread_main (line 47) | def _tk_thread_main() -> None:
function _ensure_ui_thread (line 79) | def _ensure_ui_thread() -> None:
function _call_on_ui_thread (line 92) | def _call_on_ui_thread(fn: object) -> object:
function _post_to_ui_thread (line 104) | def _post_to_ui_thread(fn: object) -> None:
function _is_alive (line 110) | def _is_alive(w: tk.Misc | None, mapped: bool = False) -> bool:
function _tk_btn (line 149) | def _tk_btn(parent: tk.Misc, text: str = "", cmd: Callable | None = None...
function _tk_lbl (line 163) | def _tk_lbl(parent: tk.Misc, text: str = "", **kw) -> tk.Label:
function _dpi_scale_for_widget (line 177) | def _dpi_scale_for_widget(w: tk.Misc) -> float:
function _params_ini_path (line 191) | def _params_ini_path() -> Path:
function _load_overlay_settings (line 198) | def _load_overlay_settings() -> dict[str, Any]:
function _save_overlay_settings (line 245) | def _save_overlay_settings(values: dict[str, Any]) -> None:
function _clamp_int (line 260) | def _clamp_int(v: int | None, lo: int, hi: int, default: int) -> int:
function _iter_paragon_payloads (line 268) | def _iter_paragon_payloads(paragon: object) -> list[dict[str, Any]]:
function _format_build_display_name (line 277) | def _format_build_display_name(raw_name: object) -> str:
function _resolve_build_index (line 303) | def _resolve_build_index(
function load_builds_from_path (line 327) | def load_builds_from_path(preset_path: str | None = None) -> list[dict[s...
function parse_rotation (line 365) | def parse_rotation(rot_str: str) -> int:
function nodes_to_grid (line 372) | def nodes_to_grid(nodes: list[int] | list[bool]) -> list[list[bool]]:
class OverlayConfig (line 383) | class OverlayConfig:
class ParagonOverlay (line 409) | class ParagonOverlay(tk.Toplevel):
method __init__ (line 412) | def __init__(
method _apply_dpi_scaling (line 496) | def _apply_dpi_scaling(self) -> None:
method _build_ui (line 513) | def _build_ui(self) -> None:
method _bind_events (line 625) | def _bind_events(self) -> None:
method _poll_close_request (line 635) | def _poll_close_request(self) -> None:
method _poll_window_state (line 644) | def _poll_window_state(self) -> None:
method _on_config_changed (line 655) | def _on_config_changed(self, changed_keys: set[str] | frozenset[str]) ...
method _apply_live_colorblind_change (line 661) | def _apply_live_colorblind_change(self) -> None:
method _select_build (line 672) | def _select_build(self, idx: int) -> None:
method _toggle_grid_lock (line 683) | def _toggle_grid_lock(self) -> None:
method _toggle_gold_frames (line 688) | def _toggle_gold_frames(self) -> None:
method _reset_grid_defaults (line 695) | def _reset_grid_defaults(self) -> None:
method _accent_frame_color (line 710) | def _accent_frame_color(self) -> str:
method _accent_frame_thickness (line 719) | def _accent_frame_thickness(self) -> int:
method _grid_frame_thickness (line 723) | def _grid_frame_thickness(self) -> int:
method _apply_accent_frames (line 727) | def _apply_accent_frames(self, *, force: bool = False) -> None:
method _reload_profiles (line 750) | def _reload_profiles(self) -> None:
method _is_descendant (line 773) | def _is_descendant(self, child: tk.Misc, parent: tk.Misc) -> bool:
method _close_popup (line 787) | def _close_popup(self, attr_name: str, btn_widget: tk.Button, escape_i...
method _handle_global_click (line 801) | def _handle_global_click(self, e: tk.Event, attr_name: str, btn_widget...
method _close_build_dropdown (line 812) | def _close_build_dropdown(self) -> None:
method _close_settings_dropdown (line 831) | def _close_settings_dropdown(self) -> None:
method _show_dropdown (line 837) | def _show_dropdown(
method _virtual_screen_bounds (line 924) | def _virtual_screen_bounds(self) -> tuple[int, int, int, int]:
method _show_build_menu (line 938) | def _show_build_menu(self) -> None:
method _show_settings_dropdown (line 1018) | def _show_settings_dropdown(self) -> None:
method _measure_build_popup_width (line 1030) | def _measure_build_popup_width(self, popup: tk.Misc) -> int:
method _build_build_popup (line 1073) | def _build_build_popup(self, host: tk.Misc) -> Any:
method _build_settings_popup (line 1144) | def _build_settings_popup(self, host: tk.Misc) -> Any:
method _update_board_selection (line 1314) | def _update_board_selection(self) -> None:
method _select_board_card (line 1325) | def _select_board_card(self, idx: int) -> None:
method _toggle_collapsed_mode (line 1332) | def _toggle_collapsed_mode(self) -> None:
method _refresh_lists (line 1342) | def _refresh_lists(self) -> None:
method _on_boards_mousewheel (line 1406) | def _on_boards_mousewheel(self, e: tk.Event) -> None:
method _move_grid (line 1419) | def _move_grid(self, dx: int, dy: int) -> None:
method _zoom_grid (line 1432) | def _zoom_grid(self, delta: int) -> None:
method _warmup_settings_assets (line 1443) | def _warmup_settings_assets(self) -> None:
method _on_grid_drag_start (line 1476) | def _on_grid_drag_start(self, e: tk.Event) -> None:
method _on_grid_drag_move (line 1497) | def _on_grid_drag_move(self, e: tk.Event) -> None:
method _on_grid_drag_end (line 1511) | def _on_grid_drag_end(self, _: tk.Event) -> None:
method _get_resolution (line 1519) | def _get_resolution(self) -> tuple[int, int]:
method _get_cam_roi (line 1525) | def _get_cam_roi(self) -> tuple[int, int, int, int] | None:
method _apply_geometry (line 1534) | def _apply_geometry(self) -> None:
method redraw (line 1545) | def redraw(self) -> None:
method close (line 1593) | def close(self) -> None:
method _persist_state (line 1610) | def _persist_state(self) -> None:
function run_paragon_overlay (line 1639) | def run_paragon_overlay(preset_path: str | None = None, *, parent: tk.Mi...
function request_close (line 1689) | def request_close(overlay: ParagonOverlay | None = None) -> None:
FILE: src/scripts/__init__.py
function correct_name (line 1) | def correct_name(name: str) -> str | None:
FILE: src/scripts/common.py
class FilterColors (line 37) | class FilterColors:
function get_filter_colors (line 66) | def get_filter_colors() -> FilterColors:
function mark_as_junk (line 80) | def mark_as_junk():
function mark_as_favorite (line 85) | def mark_as_favorite():
function reset_canvas (line 93) | def reset_canvas(root, canvas):
function reset_item_status (line 101) | def reset_item_status(occupied, inv):
function drop_item_from_inventory (line 117) | def drop_item_from_inventory() -> None:
function is_ignored_item (line 129) | def is_ignored_item(item_descr: Item):
function _scaled_overlay_font_size (line 162) | def _scaled_overlay_font_size(minimum_font_size: int, window_height: int...
function draw_text_with_background (line 173) | def draw_text_with_background(
FILE: src/scripts/handler.py
function _setting_key (line 46) | def _setting_key(section: str, field_name: str) -> str:
function _field_metadata (line 50) | def _field_metadata(model_class: type[Any], field_name: str) -> dict[str...
function _collect_reload_group_keys (line 54) | def _collect_reload_group_keys(section: str, model_class: type[Any], gro...
function _collect_hotkey_setting_keys (line 62) | def _collect_hotkey_setting_keys() -> set[str]:
function _has_any_changed (line 72) | def _has_any_changed(changed_keys: AbstractSet[str], relevant_keys: set[...
class ScriptHandler (line 83) | class ScriptHandler:
method __init__ (line 84) | def __init__(self):
method _create_vision_mode (line 102) | def _create_vision_mode(self, vision_mode_type: VisionModeType):
method _graceful_exit (line 107) | def _graceful_exit(self):
method _on_config_changed (line 110) | def _on_config_changed(self, changed_keys: AbstractSet[str]) -> None:
method _hotkey_signature (line 124) | def _hotkey_signature(self, config: IniConfigLoader) -> tuple[str | bo...
method _refresh_hotkeys (line 139) | def _refresh_hotkeys(self, config: IniConfigLoader) -> None:
method _refresh_language_assets (line 151) | def _refresh_language_assets(self, config: IniConfigLoader) -> None:
method _refresh_logging_level (line 159) | def _refresh_logging_level(self, config: IniConfigLoader) -> None:
method _notify_manual_restart_required (line 170) | def _notify_manual_restart_required(self, reason: str) -> None:
method toggle_paragon_overlay (line 177) | def toggle_paragon_overlay(self):
method _run_paragon_overlay (line 213) | def _run_paragon_overlay(self, preset_path: str) -> None:
method _clear_key_binds (line 227) | def _clear_key_binds(self) -> None:
method _register_hotkey (line 236) | def _register_hotkey(self, hotkey: str, callback: Callable[[], None]) ...
method setup_key_binds (line 239) | def setup_key_binds(self):
method filter_items (line 263) | def filter_items(self, force_refresh=ItemRefreshType.no_refresh, no_ma...
method move_items_to_inventory (line 274) | def move_items_to_inventory(self):
method move_items_to_stash (line 277) | def move_items_to_stash(self):
method _start_or_stop_loot_interaction_thread (line 280) | def _start_or_stop_loot_interaction_thread(self, loot_interaction_meth...
method _wrapper_run_loot_interaction_method (line 301) | def _wrapper_run_loot_interaction_method(self, loot_interaction_method...
method run_vision_mode (line 316) | def run_vision_mode(self):
function run_loot_filter (line 329) | def run_loot_filter(force_refresh: ItemRefreshType = ItemRefreshType.no_...
FILE: src/scripts/loot_filter_tts.py
function check_items (line 29) | def check_items(
FILE: src/scripts/vision_mode_fast.py
class VisionModeFast (line 24) | class VisionModeFast:
method __init__ (line 25) | def __init__(self):
method adjust_textbox_size (line 41) | def adjust_textbox_size(self):
method clear_textbox (line 59) | def clear_textbox(self):
method create_textbox (line 63) | def create_textbox(self):
method draw_from_queue (line 79) | def draw_from_queue(self):
method insert_colored_text (line 91) | def insert_colored_text(self, text, color):
method refresh_clear_timer (line 100) | def refresh_clear_timer(self):
method request_clear (line 106) | def request_clear(self):
method request_draw (line 109) | def request_draw(self, text, color):
method on_tts (line 112) | def on_tts(self, _):
method start (line 152) | def start(self):
method stop (line 157) | def stop(self):
method running (line 163) | def running(self):
function create_match_text (line 167) | def create_match_text(matches: list[MatchedFilter]):
FILE: src/scripts/vision_mode_with_highlighting.py
class CancellationRequested (line 38) | class CancellationRequested(Exception):
class VisionModeWithHighlighting (line 43) | class VisionModeWithHighlighting:
method __init__ (line 44) | def __init__(self):
method draw_rect (line 94) | def draw_rect(self, canvas: tk.Canvas, bullet_width, obj, off, color):
method draw_text (line 102) | def draw_text(self, canvas, text, color, previous_text_y, offset, canv...
method create_signal_rect (line 155) | def create_signal_rect(self, canvas, w, thick, color):
method draw_from_queue (line 174) | def draw_from_queue(self):
method draw_empty_outline (line 198) | def draw_empty_outline(self, item_roi, color, text: str | None):
method draw_match_outline (line 213) | def draw_match_outline(self, item_roi, should_keep_res, item_descr):
method draw_no_match_outline (line 234) | def draw_no_match_outline(self, item_roi):
method draw_codex_upgrade_outline (line 240) | def draw_codex_upgrade_outline(self, item_roi, should_keep_result: Fil...
method on_tts (line 259) | def on_tts(self, _):
method evaluate_item_and_queue_draw (line 285) | def evaluate_item_and_queue_draw(self, item_descr: Item):
method check_for_thread_cancellation (line 402) | def check_for_thread_cancellation(cancel_event: Event):
method stop_thread_and_wait (line 407) | def stop_thread_and_wait(thread: Thread, cancel_event: Event):
method check_for_item_still_selected (line 411) | def check_for_item_still_selected(self, item_center):
method get_coords_from_roi (line 424) | def get_coords_from_roi(self, item_roi):
method request_clear (line 433) | def request_clear(self):
method request_empty_outline (line 436) | def request_empty_outline(self, item_descr, item_roi, color, text: str...
method request_match_box (line 439) | def request_match_box(self, item_descr, item_roi, should_keep_res, ite...
method request_no_match_box (line 442) | def request_no_match_box(self, item_descr, item_roi):
method request_codex_upgrade_box (line 445) | def request_codex_upgrade_box(self, item_descr, item_roi, res):
method start (line 448) | def start(self):
method stop (line 453) | def stop(self):
method running (line 467) | def running(self):
FILE: src/startup_messages.py
function emit_startup_messages (line 14) | def emit_startup_messages():
function emit_early_startup_logs (line 23) | def emit_early_startup_logs():
FILE: src/template_finder.py
class TemplateMatch (line 24) | class TemplateMatch:
method __eq__ (line 32) | def __eq__(self, other):
method __hash__ (line 37) | def __hash__(self):
class SearchResult (line 42) | class SearchResult:
method __post_init__ (line 46) | def __post_init__(self):
class SearchArgs (line 52) | class SearchArgs:
method __call__ (line 65) | def __call__(self, cls):
method as_dict (line 69) | def as_dict(self):
method detect (line 72) | def detect(self, img: np.ndarray = None) -> SearchResult:
method is_visible (line 79) | def is_visible(self, img: np.ndarray = None) -> bool:
method wait_until_visible (line 82) | def wait_until_visible(self, timeout: float = 30, suppress_debug: bool...
method wait_until_hidden (line 90) | def wait_until_hidden(self, timeout: float = 3, suppress_debug: bool =...
method wait_for_update (line 99) | def wait_for_update(
function _process_template_refs (line 115) | def _process_template_refs(ref: str | np.ndarray | list[str]) -> list[Te...
function _get_cv_result (line 134) | def _get_cv_result(
function search (line 174) | def search(
FILE: src/tools/gen_data.py
function remove_content_in_braces (line 39) | def remove_content_in_braces(input_string) -> str:
function get_random_number_idx (line 56) | def get_random_number_idx(s: str) -> list[int]:
function is_placeholder_or_test_name (line 65) | def is_placeholder_or_test_name(name) -> bool:
function check_ms (line 90) | def check_ms(input_string) -> str:
function main (line 108) | def main(d4data_dir: Path, companion_app_dir: Path):
function generate_aspects (line 276) | def generate_aspects(d4data_dir, language):
function generate_uniques (line 312) | def generate_uniques(d4data_dir, language):
FILE: src/tts.py
class ItemIdentifiers (line 22) | class ItemIdentifiers(enum.Enum):
class Publisher (line 31) | class Publisher:
method __init__ (line 32) | def __init__(self):
method find_item (line 36) | def find_item(self) -> None:
method publish (line 51) | def publish(self, data):
method subscribe (line 56) | def subscribe(self, subscriber):
method unsubscribe (line 60) | def unsubscribe(self, subscriber):
function create_pipe (line 65) | def create_pipe():
function read_pipe (line 94) | def read_pipe() -> None:
function find_item_start (line 126) | def find_item_start(data: list[str]) -> int | None:
function filter_data (line 143) | def filter_data(data: str) -> bool:
function fix_data (line 147) | def fix_data(data: str) -> str:
function start_connection (line 156) | def start_connection() -> None:
FILE: src/ui/char_inventory.py
class CharInventory (line 7) | class CharInventory(InventoryBase):
method __init__ (line 8) | def __init__(self):
FILE: src/ui/inventory_base.py
class ItemSlot (line 16) | class ItemSlot:
class InventoryBase (line 23) | class InventoryBase(Menu):
method __init__ (line 29) | def __init__(self, rows: int = 3, columns: int = 11, is_stash: bool = ...
method get_max_slot_size (line 39) | def get_max_slot_size(self):
method get_item_slots (line 44) | def get_item_slots(self, img: np.ndarray | None = None) -> tuple[list[...
method hover_item (line 85) | def hover_item(self, item: ItemSlot):
method hover_left_of_item (line 89) | def hover_left_of_item(self, item: ItemSlot):
method hover_item_with_delay (line 98) | def hover_item_with_delay(self, item: ItemSlot, delay_factor: tuple[fl...
FILE: src/ui/menu.py
class ToggleMethod (line 19) | class ToggleMethod(Enum):
class Menu (line 24) | class Menu:
method __init__ (line 25) | def __init__(self):
method open (line 32) | def open(self) -> bool:
method _check_match (line 48) | def _check_match(self, res: SearchResult) -> bool:
method is_open (line 57) | def is_open(self, img: np.ndarray = None) -> bool:
method wait_until_open (line 67) | def wait_until_open(self, timeout: float = 10) -> bool:
FILE: src/ui/stash.py
class Stash (line 14) | class Stash(InventoryBase):
method __init__ (line 15) | def __init__(self):
method switch_to_tab (line 24) | def switch_to_tab(tab_idx) -> bool:
FILE: src/ui/vendor.py
class Vendor (line 9) | class Vendor(InventoryBase):
method __init__ (line 10) | def __init__(self):
FILE: src/utils/custom_mouse.py
function isNumeric (line 11) | def isNumeric(val):
function is_list_of_points (line 15) | def is_list_of_points(value):
class BezierCurve (line 27) | class BezierCurve:
method binomial (line 29) | def binomial(n, k):
method bernsteinPolynomialPoint (line 34) | def bernsteinPolynomialPoint(x, i, n):
method bernsteinPolynomial (line 39) | def bernsteinPolynomial(points):
method curvePoints (line 54) | def curvePoints(n, points):
class HumanCurve (line 64) | class HumanCurve:
method __init__ (line 67) | def __init__(self, fromPoint, toPoint, **kwargs):
method generateCurve (line 72) | def generateCurve(self, **kwargs):
method generateInternalKnots (line 96) | def generateInternalKnots(self, leftBoundary, rightBoundary, downBound...
method generatePoints (line 122) | def generatePoints(self, knots):
method distortPoints (line 132) | def distortPoints(self, points, distortionMean, distortionStdev, disto...
method tweenPoints (line 155) | def tweenPoints(self, points, tween, targetPoints):
class mouse (line 175) | class mouse:
method move (line 176) | def move(
method _is_clicking_safe (line 219) | def _is_clicking_safe():
method click (line 223) | def click(button):
method get_position (line 228) | def get_position():
FILE: src/utils/image_operations.py
class ThresholdTypes (line 11) | class ThresholdTypes(Enum):
function threshold (line 17) | def threshold(
function crop (line 58) | def crop(img: np.ndarray, roi: tuple[int, int, int, int]) -> np.ndarray:
function mask_by_roi (line 75) | def mask_by_roi(img: np.ndarray, roi: tuple[int, int, int, int], masking...
function alpha_to_mask (line 96) | def alpha_to_mask(img: np.ndarray) -> np.ndarray | None:
function create_mask (line 108) | def create_mask(size: tuple[int, int], roi: tuple[int, int, int, int]) -...
function color_filter (line 121) | def color_filter(
function overlay_image (line 157) | def overlay_image(image1: np.ndarray, image2: np.ndarray, x_offset: int,...
function get_typographic_lines (line 195) | def get_typographic_lines(img: np.ndarray, should_invert: bool = False) ...
function compare_histograms (line 227) | def compare_histograms(imageA, imageB):
FILE: src/utils/misc.py
function is_in_roi (line 20) | def is_in_roi(roi: list[float], pos: tuple[float, float]):
function hms (line 27) | def hms(seconds: int):
function set_cv2_window (line 35) | def set_cv2_window(name, x, y, size):
function generate_random_name (line 41) | def generate_random_name(length_min=8, length_max=14):
function random_number_gaussian (line 47) | def random_number_gaussian(min_val, max_val):
function random_coordinate_around_center (line 55) | def random_coordinate_around_center(x, y, radius_x, radius_y):
function convert_args_to_numpy (line 66) | def convert_args_to_numpy(func):
function run_until_condition (line 88) | def run_until_condition(
function scale_vector_to_distance (line 112) | def scale_vector_to_distance(vector, target_distance):
function slugify (line 119) | def slugify(value, allow_unicode=False, separator="_"):
function find_and_eval_math_in_string (line 141) | def find_and_eval_math_in_string(s):
function remove_commas_from_numbers (line 151) | def remove_commas_from_numbers(s: str) -> str:
FILE: src/utils/process_handler.py
function kill_thread (line 13) | def kill_thread(thread):
function safe_exit (line 21) | def safe_exit(error_code=0):
function set_process_name (line 59) | def set_process_name(name, window_spec):
FILE: src/utils/roi_operations.py
function compare_tuples (line 9) | def compare_tuples(t1, t2, uncertainty):
function create_roi_from_rel (line 13) | def create_roi_from_rel(point, rel_roi):
function fit_roi_to_window_size (line 23) | def fit_roi_to_window_size(roi, size):
function get_center (line 47) | def get_center(roi: tuple[int, int, int, int]) -> tuple[int, int]:
function intersect (line 57) | def intersect(*rects: list[tuple[int, int, int, int]] | tuple[int, int, ...
function bounding_box (line 77) | def bounding_box(
function to_grid (line 110) | def to_grid(roi: tuple[int, int, int, int], rows: int, columns: int) -> ...
class Condition (line 138) | class Condition(Enum):
function is_in_roi (line 144) | def is_in_roi(
FILE: src/utils/window.py
class WindowSpec (line 28) | class WindowSpec:
method match (line 31) | def match(self, hwnd: int, check_window_name: bool = True) -> bool:
function _list_active_window_ids (line 36) | def _list_active_window_ids() -> list[int]:
function get_window_spec_id (line 42) | def get_window_spec_id(window_spec: WindowSpec) -> int | None:
function _get_window_name_from_id (line 53) | def _get_window_name_from_id(hwnd: int) -> str:
function _get_process_from_window_name (line 57) | def _get_process_from_window_name(hwnd: int) -> str:
function start_detecting_window (line 65) | def start_detecting_window(window_spec: WindowSpec):
function detect_window (line 74) | def detect_window(window_spec: WindowSpec):
function find_and_set_window_position (line 81) | def find_and_set_window_position(window_spec: WindowSpec):
function stop_detecting_window (line 91) | def stop_detecting_window():
function move_window_to_foreground (line 99) | def move_window_to_foreground(window_spec: WindowSpec):
function is_window_foreground (line 106) | def is_window_foreground(window_spec: WindowSpec) -> bool:
function screenshot (line 114) | def screenshot(
FILE: tests/config/helper_test.py
class TestKeyMustExist (line 6) | class TestKeyMustExist:
method test_existing_key (line 7) | def test_existing_key(self):
method test_modifier_key_works (line 11) | def test_modifier_key_works(self):
method test_non_existing_key (line 14) | def test_non_existing_key(self):
class TestSingletonDecorator (line 20) | class TestSingletonDecorator:
class SingletonDummyClass (line 22) | class SingletonDummyClass:
method __init__ (line 23) | def __init__(self, *args, **kwargs):
method test_singleton_instance (line 26) | def test_singleton_instance(self):
class TestStrToIntList (line 33) | class TestStrToIntList:
method test_empty_string (line 34) | def test_empty_string(self):
method test_single_integer (line 38) | def test_single_integer(self):
method test_multiple_integers (line 42) | def test_multiple_integers(self):
method test_invalid_input (line 46) | def test_invalid_input(self):
method test_negative_numbers (line 51) | def test_negative_numbers(self):
method test_whitespace (line 55) | def test_whitespace(self):
FILE: tests/config/loader_test.py
function isolated_ini_loader (line 15) | def isolated_ini_loader(tmp_path: Path):
class TestIniConfigLoader (line 49) | class TestIniConfigLoader:
method test_reload_if_changed_updates_models_and_revision (line 50) | def test_reload_if_changed_updates_models_and_revision(self, isolated_...
method test_property_access_auto_reloads_changed_config (line 61) | def test_property_access_auto_reloads_changed_config(self, isolated_in...
method test_save_value_updates_model_without_reloading_from_file (line 68) | def test_save_value_updates_model_without_reloading_from_file(self, is...
method test_save_value_notifies_change_listeners (line 75) | def test_save_value_notifies_change_listeners(self, isolated_ini_loade...
method test_reload_if_changed_notifies_changed_keys (line 84) | def test_reload_if_changed_notifies_changed_keys(self, isolated_ini_lo...
method test_reload_if_changed_removes_defunct_model_keys (line 95) | def test_reload_if_changed_removes_defunct_model_keys(
FILE: tests/config/models_test.py
class TestSigil (line 15) | class TestSigil:
method _setup (line 17) | def _setup(self, mock_ini_loader: IniConfigLoader) -> None:
method test_all_bad_cases (line 22) | def test_all_bad_cases(data: dict[str, Any]) -> None:
method test_all_good_cases (line 29) | def test_all_good_cases(data: dict[str, Any]) -> None:
class TestUnique (line 34) | class TestUnique:
method _setup (line 36) | def _setup(self, mock_ini_loader: IniConfigLoader) -> None:
method test_all_bad_cases (line 41) | def test_all_bad_cases(data: dict[str, Any], expected_msg: str) -> None:
method test_all_good_cases (line 47) | def test_all_good_cases() -> None:
class TestGeneralProfiles (line 51) | class TestGeneralProfiles:
method test_profiles_empty_entries_are_removed (line 53) | def test_profiles_empty_entries_are_removed() -> None:
FILE: tests/config/ui_test.py
function test_set_resolution (line 25) | def test_set_resolution(res):
function test_transformation (line 31) | def test_transformation(result):
function test_colors (line 39) | def test_colors():
function test_templates (line 43) | def test_templates():
FILE: tests/conftest.py
function mock_ini_loader (line 13) | def mock_ini_loader(mocker: MockerFixture):
FILE: tests/gui/importer/test_d4builds.py
function test_extract_build_metadata_from_planner_header (line 28) | def test_extract_build_metadata_from_planner_header() -> None:
function test_extract_build_metadata_prefers_description_for_guides (line 62) | def test_extract_build_metadata_prefers_description_for_guides() -> None:
function test_extract_d4builds_season_number_from_gear_dropdown (line 85) | def test_extract_d4builds_season_number_from_gear_dropdown() -> None:
function test_import_d4builds (line 109) | def test_import_d4builds(url: str, mock_ini_loader: MockerFixture, mocke...
FILE: tests/gui/importer/test_diablo_trade.py
function test_import_diablo_trade (line 20) | def test_import_diablo_trade(url: str, mock_ini_loader: MockerFixture, m...
FILE: tests/gui/importer/test_gui_common.py
function test_build_default_profile_file_name_maxroll (line 5) | def test_build_default_profile_file_name_maxroll() -> None:
function test_build_default_profile_file_name_d4builds_strips_title_suffix (line 13) | def test_build_default_profile_file_name_d4builds_strips_title_suffix() ...
function test_build_default_profile_file_name_d4builds_strips_spaced_title_suffix (line 21) | def test_build_default_profile_file_name_d4builds_strips_spaced_title_su...
function test_build_default_profile_file_name_keeps_unknown_class_and_empty_variant (line 29) | def test_build_default_profile_file_name_keeps_unknown_class_and_empty_v...
function test_build_default_profile_file_name_adds_season_and_strips_matching_header_marker (line 37) | def test_build_default_profile_file_name_adds_season_and_strips_matching...
function test_build_default_profile_file_name_replaces_stale_season_marker_in_header (line 49) | def test_build_default_profile_file_name_replaces_stale_season_marker_in...
function test_to_yaml_str_sorts_aspect_upgrades_and_uses_block_style (line 57) | def test_to_yaml_str_sorts_aspect_upgrades_and_uses_block_style(mock_ini...
FILE: tests/gui/importer/test_maxroll.py
function test_import_maxroll (line 31) | def test_import_maxroll(url: str, mock_ini_loader: MockerFixture, mocker...
function test_find_item_type_uses_fix_weapon_type_with_slot_context (line 45) | def test_find_item_type_uses_fix_weapon_type_with_slot_context() -> None:
function test_find_item_type_uses_fix_offhand_type_with_slot_and_class_context (line 52) | def test_find_item_type_uses_fix_offhand_type_with_slot_and_class_contex...
function test_find_item_type_uses_fix_offhand_type_when_item_type_implies_offhand (line 59) | def test_find_item_type_uses_fix_offhand_type_when_item_type_implies_off...
function test_resolve_visible_profile_index_skips_hidden_profiles (line 66) | def test_resolve_visible_profile_index_skips_hidden_profiles() -> None:
function test_find_item_affixes_resolves_skill_rank_category_from_affix_key (line 77) | def test_find_item_affixes_resolves_skill_rank_category_from_affix_key()...
function test_find_item_affixes_resolves_skill_rank_category_from_related_description (line 94) | def test_find_item_affixes_resolves_skill_rank_category_from_related_des...
FILE: tests/gui/importer/test_mobalytics.py
function test_extract_mobalytics_paragon_steps_normalizes_warlock_starting_board (line 31) | def test_extract_mobalytics_paragon_steps_normalizes_warlock_starting_bo...
function test_import_mobalytics (line 48) | def test_import_mobalytics(url: str, mock_ini_loader: MockerFixture, moc...
FILE: tests/item/filter/data/affixes.py
class TestItem (line 7) | class TestItem(Item):
method __init__ (line 8) | def __init__(self, rarity: ItemRarity = ItemRarity.Legendary, power=91...
FILE: tests/item/filter/data/aspects.py
class TestItem (line 7) | class TestItem(Item):
method __init__ (line 8) | def __init__(self, rarity=ItemRarity.Legendary, power=910, is_codex_up...
FILE: tests/item/filter/data/sigils.py
class TestSigil (line 7) | class TestSigil(Item):
method __init__ (line 8) | def __init__(self, rarity=ItemRarity.Common, item_type=ItemType.Sigil,...
FILE: tests/item/filter/data/tributes.py
class TestTribute (line 6) | class TestTribute(Item):
method __init__ (line 7) | def __init__(self, rarity=ItemRarity.Common, item_type=ItemType.Tribut...
FILE: tests/item/filter/data/uniques.py
class TestUnique (line 8) | class TestUnique(Item):
method __init__ (line 9) | def __init__(
FILE: tests/item/filter/filter_test.py
function _create_mocked_filter (line 25) | def _create_mocked_filter(mocker: MockerFixture) -> Filter:
function test_affixes (line 42) | def test_affixes(_name: str, result: list[str], item: Item, mocker: Mock...
function test_aspects (line 51) | def test_aspects(_name: str, result: list[str], item: Item, mocker: Mock...
function test_global_uniques (line 63) | def test_global_uniques(_name: str, result: list[str], item: Item, mocke...
function test_sigils (line 70) | def test_sigils(_name: str, result: list[str], item: Item, mocker: Mocke...
function test_sigil_empty_lists (line 78) | def test_sigil_empty_lists(mocker: MockerFixture):
function test_sigil_priority (line 89) | def test_sigil_priority(mocker: MockerFixture):
function test_tributes (line 100) | def test_tributes(_name: str, result: list[str], item: Item, mocker: Moc...
function test_uniques_with_affixes (line 111) | def test_uniques_with_affixes(_name: str, result: list[str], item: Item,...
function test_mythic_always_kept (line 120) | def test_mythic_always_kept(_name: str, result: bool, item: Item, mocker...
FILE: tests/item/read_descr_season6_tts_test.py
function test_items (line 147) | def test_items(input_item: list[str], expected_item: Item):
FILE: tests/item/read_descr_season8_tts_test.py
function test_items (line 90) | def test_items(input_item: list[str], expected_item: Item):
FILE: tests/item/read_descr_season_11_tts_test.py
function test_items (line 431) | def test_items(input_item: list[str], expected_item: Item):
FILE: tests/item/read_descr_season_12_tts_test.py
function test_items (line 197) | def test_items(input_item: list[str], expected_item: Item):
FILE: tests/item/read_descr_season_13_tts_test.py
function test_items (line 394) | def test_items(input_item: list[str], expected_item: Item):
FILE: tests/item/read_descr_tts_test.py
function test_loot_filter_controls_are_not_tts_item_start (line 7) | def test_loot_filter_controls_are_not_tts_item_start():
function test_loot_filter_controls_do_not_raise_tts_parser_error (line 11) | def test_loot_filter_controls_do_not_raise_tts_parser_error():
FILE: tests/template_finder_test.py
function test_search (line 7) | def test_search():
function test_search_best_match (line 18) | def test_search_best_match():
function test_search_all (line 29) | def test_search_all():
function test_search_all_multiple_templates (line 38) | def test_search_all_multiple_templates():
FILE: tests/ui/char_inventory_test.py
function test_char_inventory (line 21) | def test_char_inventory(img_res, input_img):
function test_get_item_slots (line 37) | def test_get_item_slots(img_res, input_img, occupied, junk, fav):
FILE: tests/ui/chest_test.py
function test_chest (line 12) | def test_chest(img_res, input_img):
FILE: tests/utils/image_operations_test.py
function test_binary_threshold (line 16) | def test_binary_threshold() -> None:
function test_crop (line 34) | def test_crop() -> None:
function test_mask_by_roi (line 47) | def test_mask_by_roi() -> None:
function test_alpha_to_mask (line 95) | def test_alpha_to_mask() -> None:
function test_create_mask (line 107) | def test_create_mask() -> None:
function filter_img (line 115) | def filter_img() -> np.ndarray:
function color_range (line 120) | def color_range() -> list[np.ndarray]:
function test_color_filter_mask_shape (line 124) | def test_color_filter_mask_shape(filter_img: np.ndarray, color_range: li...
function test_color_filter_no_img (line 129) | def test_color_filter_no_img(filter_img: np.ndarray, color_range: list[n...
function test_color_filter_with_img (line 134) | def test_color_filter_with_img(filter_img: np.ndarray, color_range: list...
function test_overlay_image (line 139) | def test_overlay_image() -> None:
FILE: tests/utils/roi_operations_test.py
function test_get_center (line 4) | def test_get_center() -> None:
function test_intersect (line 11) | def test_intersect() -> None:
function test_bounding_box (line 23) | def test_bounding_box() -> None:
function test_is_coor_in_roi (line 49) | def test_is_coor_in_roi() -> None:
FILE: tts/saapi.cpp
function BOOL (line 12) | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID ...
function InitPipe (line 30) | void InitPipe() { hPipe = CreateFile(_T("\\\\.\\pipe\\d4lf"), GENERIC_WR...
function SA_SayW (line 32) | bool SA_SayW(const wchar_t* str) {
function SA_BrlShowTextW (line 46) | bool SA_BrlShowTextW(const wchar_t* str) { return true; }
function SA_IsRunning (line 48) | bool SA_IsRunning() { return true; }
function SA_StopAudio (line 50) | bool SA_StopAudio() { return true; }
Condensed preview — 153 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,026K chars).
[
{
"path": ".clang-format",
"chars": 93,
"preview": "Language: Cpp\nBasedOnStyle: Google\nColumnLimit: 140\nIndentWidth: 4\nTabWidth: 4\nUseTab: Never\n"
},
{
"path": ".gitattributes",
"chars": 368,
"preview": "# Set the default behavior, in case people don't have core.autocrlf set.\n* text=auto\n\n# Denote all files that are truly "
},
{
"path": ".github/actions/setup_env/action.yml",
"chars": 405,
"preview": "name: Setup env\n\nruns:\n using: \"composite\"\n steps:\n - name: Setup uv\n uses: astral-sh/setup-uv@v8.1.0\n wi"
},
{
"path": ".github/workflows/ci.yml",
"chars": 559,
"preview": "name: CI\n\non:\n pull_request:\n push:\n branches: [main]\n\nconcurrency:\n group: \"${{github.workflow}}-${{github.ref}}\""
},
{
"path": ".github/workflows/notify.yml",
"chars": 348,
"preview": "name: Notify\n\non:\n release:\n types: [published]\n\njobs:\n github-releases-to-discord:\n runs-on: ubuntu-latest\n "
},
{
"path": ".github/workflows/release.yml",
"chars": 1753,
"preview": "name: Release\n\non:\n pull_request:\n types: [closed]\n workflow_dispatch:\n\nconcurrency:\n group: release\n\njobs:\n rele"
},
{
"path": ".gitignore",
"chars": 500,
"preview": "!config/bnip/.gitkeep\n*.bak\n*.log\n*.pyc\n*.pyo\n*.spec\n*_generated.py\n*info_*.png\n*info_log_parsed.txt\n.coverage\n.idea/\n.p"
},
{
"path": ".pre-commit-config.yaml",
"chars": 2235,
"preview": "default_install_hook_types: [pre-push]\ndefault_language_version:\n python: python3.14\nminimum_prek_version: '0.3.0'\nrepo"
},
{
"path": "LICENSE.txt",
"chars": 1074,
"preview": "MIT License\n\nCopyright (c) 2026 d4lf contributors\n\nPermission is hereby granted, free of charge, to any person obtaining"
},
{
"path": "README.md",
"chars": 43828,
"preview": "# \n\n## Note: D4LF will be updated for Season 13. However, there were a lot of itemization update"
},
{
"path": "assets/lang/enUS/How to add to these files.md",
"chars": 1231,
"preview": "These files are all autogenerated from data from d4companion and d4data.\nAny manual additions to them will be overwritte"
},
{
"path": "assets/lang/enUS/affixes.json",
"chars": 57124,
"preview": "{\n \"abyss_damage\": \"abyss damage\",\n \"advance_resource_generation\": \"advance resource generation\",\n \"aegis_coold"
},
{
"path": "assets/lang/enUS/aspects.json",
"chars": 13026,
"preview": "[\n \"accelerating\",\n \"aggressive\",\n \"agile\",\n \"aphotic\",\n \"apostles\",\n \"archdruids\",\n \"assimilation\""
},
{
"path": "assets/lang/enUS/corrections.json",
"chars": 1921,
"preview": "{\n \"bad_tts_uniques\": {\n \"bane_ofahjad-den\": \"bane_of_ahjad-den\",\n \"galvanicazurite\": \"galvanic_azurite"
},
{
"path": "assets/lang/enUS/item_types.json",
"chars": 877,
"preview": "{\n \"Amulet\": \"amulet\",\n \"Axe\": \"axe\",\n \"Axe2H\": \"two-handed axe\",\n \"Boots\": \"boots\",\n \"Bow\": \"bow\",\n \""
},
{
"path": "assets/lang/enUS/paragon_maxroll_ids.json",
"chars": 9549,
"preview": "{\n \"boards\": {\n \"Paragon_Barb_00\": \"Start\",\n \"Paragon_Barb_01\": \"Hemorrhage\",\n \"Paragon_Barb_02\""
},
{
"path": "assets/lang/enUS/sigils.json",
"chars": 26977,
"preview": "{\n \"dungeons\": {\n \"abandoned_mineworks\": \"abandoned mineworks\",\n \"akkhans_grasp\": \"akkhans grasp\",\n "
},
{
"path": "assets/lang/enUS/tooltips.json",
"chars": 34,
"preview": "{\n \"ItemPower\": \"item power\"\n}\n"
},
{
"path": "assets/lang/enUS/tributes.json",
"chars": 1449,
"preview": "{\n \"ancestral_tribute_of_armaments\": \"ancestral tribute of armaments\",\n \"greater_tribute_of_armaments\": \"greater t"
},
{
"path": "assets/lang/enUS/uniques.json",
"chars": 17761,
"preview": "{\n \"100000_steps\": {\n \"num_inherents\": 0\n },\n \"accord_of_the_wilds\": {\n \"num_inherents\": 0\n },"
},
{
"path": "build.py",
"chars": 2491,
"preview": "import os\nimport shutil\nfrom pathlib import Path\n\nfrom src import __version__\n\nEXE_NAME = \"d4lf.exe\"\n\n\ndef build(release"
},
{
"path": "pyproject.toml",
"chars": 4730,
"preview": "[build-system]\nbuild-backend = \"hatchling.build\"\nrequires = [\"hatchling\"]\n\n[dependency-groups]\ndev = [\n \"prek\",\n \""
},
{
"path": "pytest.ini",
"chars": 163,
"preview": "[pytest]\naddopts = --strict-markers\nmarkers =\n requests: mark a test using requests\n selenium: mark a test using s"
},
{
"path": "src/__init__.py",
"chars": 95,
"preview": "import concurrent.futures\n\nTP = concurrent.futures.ThreadPoolExecutor()\n\n__version__ = \"9.1.3\"\n"
},
{
"path": "src/autoupdater.py",
"chars": 10744,
"preview": "import logging\nimport shutil\nimport sys\nimport time\nimport zipfile\nfrom pathlib import Path\n\nimport requests\n\nimport src"
},
{
"path": "src/cam.py",
"chars": 4006,
"preview": "import logging\nimport threading\nimport time\n\nimport mss\nimport mss.windows\nimport numpy as np\n\nfrom src.config.ui import"
},
{
"path": "src/config/__init__.py",
"chars": 293,
"preview": "import sys\nfrom pathlib import Path\n\n\ndef get_base_dir(bundled: bool = False) -> Path:\n if getattr(sys, \"frozen\", Fal"
},
{
"path": "src/config/data.py",
"chars": 3148,
"preview": "\"\"\"Everything is this file is based on UHD resolution (3840x2160).\"\"\"\n\nimport logging\nfrom dataclasses import dataclass\n"
},
{
"path": "src/config/helper.py",
"chars": 874,
"preview": "import sys\nimport threading\n\nif sys.platform != \"darwin\":\n import keyboard\n\n\ndef check_greater_than_zero(v: int) -> i"
},
{
"path": "src/config/loader.py",
"chars": 10219,
"preview": "\"\"\"Configuration loading, validation, persistence, and live change notifications.\"\"\"\n\nfrom __future__ import annotations"
},
{
"path": "src/config/profile_models.py",
"chars": 11222,
"preview": "\"\"\"New config loading and verification using pydantic. For now, both will exist in parallel hence _new.\"\"\"\n\nimport enum\n"
},
{
"path": "src/config/settings_models.py",
"chars": 15540,
"preview": "import enum\nimport logging\nfrom typing import TYPE_CHECKING, Any\n\nfrom pydantic import BaseModel, ConfigDict, Field, fie"
},
{
"path": "src/config/ui.py",
"chars": 5856,
"preview": "import logging\n\nimport cv2\nimport numpy as np\n\nfrom src.config.data import POSITIONS, Template, load_templates\nfrom src."
},
{
"path": "src/dataloader.py",
"chars": 3379,
"preview": "import json\nimport logging\nimport pathlib\nimport threading\n\nfrom src.config import BASE_DIR\nfrom src.config.loader impor"
},
{
"path": "src/gui/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/gui/activity_log_widget.py",
"chars": 4640,
"preview": "from PyQt6.QtCore import Qt, QUrl\nfrom PyQt6.QtGui import QDesktopServices\nfrom PyQt6.QtWidgets import QHBoxLayout, QLab"
},
{
"path": "src/gui/collapsible_widget.py",
"chars": 5538,
"preview": "from PyQt6.QtCore import pyqtSignal\nfrom PyQt6.QtGui import QFont\nfrom PyQt6.QtWidgets import QHBoxLayout, QLabel, QSize"
},
{
"path": "src/gui/config_tab.py",
"chars": 26485,
"preview": "import enum\nimport os\nimport subprocess\nimport sys\nimport typing\nfrom pathlib import Path\n\nfrom pydantic import BaseMode"
},
{
"path": "src/gui/config_window.py",
"chars": 2043,
"preview": "import logging\nimport sys\nfrom pathlib import Path\n\nfrom PyQt6.QtCore import QPoint, QSettings, QSize, Qt\nfrom PyQt6.QtG"
},
{
"path": "src/gui/d4lfitem.py",
"chars": 10785,
"preview": "from PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets import (\n QComboBox,\n QCompleter,\n QFormLayout,\n QGroupBox,"
},
{
"path": "src/gui/dialog.py",
"chars": 23057,
"preview": "from PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets import (\n QCheckBox,\n QComboBox,\n QCompleter,\n QDialog,\n "
},
{
"path": "src/gui/importer/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/gui/importer/d4builds.py",
"chars": 14076,
"preview": "import logging\nimport re\nimport time\nfrom typing import TYPE_CHECKING\n\nimport lxml.html\nfrom selenium.webdriver.common.b"
},
{
"path": "src/gui/importer/diablo_trade.py",
"chars": 8715,
"preview": "import dataclasses\nimport datetime\nimport json\nimport logging\nimport pathlib\nfrom typing import TYPE_CHECKING, Any\nfrom "
},
{
"path": "src/gui/importer/gui_common.py",
"chars": 12727,
"preview": "import datetime\nimport functools\nimport logging\nimport pathlib\nimport re\nimport shutil\nimport time\nfrom typing import TY"
},
{
"path": "src/gui/importer/importer_config.py",
"chars": 279,
"preview": "from dataclasses import dataclass\n\n\n@dataclass\nclass ImportConfig:\n url: str\n import_aspect_upgrades: bool\n add"
},
{
"path": "src/gui/importer/maxroll.py",
"chars": 21269,
"preview": "import json\nimport logging\nimport re\n\nimport lxml.html\n\nimport src.logger\nfrom src.config.profile_models import (\n Af"
},
{
"path": "src/gui/importer/mobalytics.py",
"chars": 13037,
"preview": "import json\nimport logging\nimport re\nfrom urllib.parse import unquote\n\nimport jsonpath\nimport lxml.html\n\nimport src.logg"
},
{
"path": "src/gui/importer/paragon_export.py",
"chars": 26717,
"preview": "from __future__ import annotations\n\nimport datetime\nimport logging\nimport re\nimport time\nfrom typing import TYPE_CHECKIN"
},
{
"path": "src/gui/importer_window.py",
"chars": 11989,
"preview": "import logging\nimport sys\nimport threading\nfrom pathlib import Path\n\nfrom PyQt6.QtCore import QObject, QPoint, QRunnable"
},
{
"path": "src/gui/open_user_config_button.py",
"chars": 387,
"preview": "import os\n\nfrom PyQt6.QtWidgets import QPushButton\n\nfrom src.config.loader import IniConfigLoader\n\n\nclass OpenUserConfig"
},
{
"path": "src/gui/profile_editor/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/gui/profile_editor/affixes_tab.py",
"chars": 38973,
"preview": "import logging\n\nfrom PyQt6.QtCore import QSettings, Qt, QTimer\nfrom PyQt6.QtGui import QDoubleValidator, QIntValidator\nf"
},
{
"path": "src/gui/profile_editor/aspect_upgrades_tab.py",
"chars": 2226,
"preview": "from PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets import QDialog, QHBoxLayout, QLabel, QListWidget, QPushButton, QVBoxLay"
},
{
"path": "src/gui/profile_editor/global_uniques_tab.py",
"chars": 6128,
"preview": "from PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets import (\n QDialog,\n QFormLayout,\n QFrame,\n QGroupBox,\n Q"
},
{
"path": "src/gui/profile_editor/profile_editor.py",
"chars": 4338,
"preview": "import logging\n\nfrom PyQt6.QtCore import Qt, pyqtSignal\nfrom PyQt6.QtWidgets import QMessageBox, QTabWidget\n\nfrom src.co"
},
{
"path": "src/gui/profile_editor/sigils_tab.py",
"chars": 13863,
"preview": "from PyQt6.QtCore import Qt, pyqtSignal\nfrom PyQt6.QtWidgets import (\n QComboBox,\n QCompleter,\n QDialog,\n QF"
},
{
"path": "src/gui/profile_editor/tributes_tab.py",
"chars": 4360,
"preview": "from PyQt6.QtCore import Qt\nfrom PyQt6.QtWidgets import (\n QAbstractItemView,\n QDialog,\n QHBoxLayout,\n QLabe"
},
{
"path": "src/gui/profile_editor_window.py",
"chars": 1764,
"preview": "import logging\nimport sys\nfrom pathlib import Path\n\nfrom PyQt6.QtCore import QPoint, QSettings, QSize, Qt, QTimer\nfrom P"
},
{
"path": "src/gui/profile_tab.py",
"chars": 10047,
"preview": "import copy\nimport logging\nimport pathlib\n\nimport yaml\nfrom pydantic import ValidationError\nfrom PyQt6.QtCore import QSe"
},
{
"path": "src/gui/themes.py",
"chars": 7145,
"preview": "\"\"\"Original simple gray theme with dynamic asset paths.\"\"\"\n\nfrom src.config import BASE_DIR\n\n# Convert paths to use forw"
},
{
"path": "src/gui/unified_window.py",
"chars": 13888,
"preview": "import logging\nimport re\nimport sys\nimport time\nfrom contextlib import suppress\nfrom pathlib import Path\n\nfrom PyQt6.QtC"
},
{
"path": "src/item/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/item/data/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/item/data/affix.py",
"chars": 800,
"preview": "import enum\nfrom dataclasses import dataclass\n\n\nclass AffixType(enum.Enum):\n greater = enum.auto()\n inherent = enu"
},
{
"path": "src/item/data/aspect.py",
"chars": 388,
"preview": "from dataclasses import dataclass\n\n\n@dataclass\nclass Aspect:\n name: str\n loc: tuple[int, int] = None\n min_value"
},
{
"path": "src/item/data/item_type.py",
"chars": 2618,
"preview": "from enum import Enum\n\n\n# The values will be overwritten depending on which language is loaded\nclass ItemType(Enum):\n "
},
{
"path": "src/item/data/rarity.py",
"chars": 180,
"preview": "from enum import Enum\n\n\nclass ItemRarity(Enum):\n Common = \"common\"\n Legendary = \"legendary\"\n Magic = \"magic\"\n "
},
{
"path": "src/item/data/seasonal_attribute.py",
"chars": 111,
"preview": "from enum import Enum\n\n\nclass SeasonalAttribute(Enum):\n bloodied = \"bloodied\"\n sanctified = \"sanctified\"\n"
},
{
"path": "src/item/descr/__init__.py",
"chars": 140,
"preview": "def keep_letters_and_spaces(text):\n return \"\".join(char for char in text if char.isalpha() or char.isspace()).strip()"
},
{
"path": "src/item/descr/read_descr_tts.py",
"chars": 22174,
"preview": "import copy\nimport logging\nimport re\nfrom typing import TYPE_CHECKING\n\nimport rapidfuzz\n\nimport src.tts\nfrom src import "
},
{
"path": "src/item/descr/text.py",
"chars": 2362,
"preview": "import re\n\nimport rapidfuzz\nimport rapidfuzz.distance.Levenshtein\n\nfrom src.dataloader import Dataloader\n\n\ndef closest_m"
},
{
"path": "src/item/descr/texture.py",
"chars": 7098,
"preview": "import math\n\nimport numpy as np\n\nfrom src.config.data import COLORS\nfrom src.config.ui import ResManager\nfrom src.templa"
},
{
"path": "src/item/filter.py",
"chars": 26801,
"preview": "import logging\nimport pathlib\nimport re\nimport sys\nimport time\nfrom dataclasses import dataclass, field\nfrom typing impo"
},
{
"path": "src/item/find_descr.py",
"chars": 3697,
"preview": "from copy import copy\nfrom typing import TYPE_CHECKING\n\nfrom src.config.ui import ResManager\nfrom src.item.data.rarity i"
},
{
"path": "src/item/models.py",
"chars": 2981,
"preview": "import json\nimport logging\nfrom dataclasses import dataclass, field\nfrom typing import TYPE_CHECKING\n\nif TYPE_CHECKING:\n"
},
{
"path": "src/logger.py",
"chars": 4295,
"preview": "from __future__ import annotations\n\nimport datetime\nimport logging\nimport logging.handlers\nimport sys\nimport threading\ni"
},
{
"path": "src/loot_mover.py",
"chars": 3699,
"preview": "import logging\nfrom typing import TYPE_CHECKING\n\nfrom src.cam import Cam\nfrom src.config.loader import IniConfigLoader\nf"
},
{
"path": "src/main.py",
"chars": 9808,
"preview": "import ctypes\nimport logging\nimport os\nimport pathlib\nimport subprocess\nimport sys\nimport time\nfrom pathlib import Path\n"
},
{
"path": "src/overlay.py",
"chars": 657,
"preview": "import logging\nimport threading\nimport tkinter as tk\n\nLOGGER = logging.getLogger(__name__)\n\nLOCK = threading.Lock()\n\n\ncl"
},
{
"path": "src/paragon_overlay.py",
"chars": 69016,
"preview": "\"\"\"Paragon overlay (tkinter).\"\"\"\n\nfrom __future__ import annotations\n\nimport base64\nimport configparser\nimport ctypes\nim"
},
{
"path": "src/scripts/__init__.py",
"chars": 453,
"preview": "def correct_name(name: str) -> str | None:\n if name:\n return (\n name\n .replace(\" (CRUCIB"
},
{
"path": "src/scripts/common.py",
"chars": 8171,
"preview": "from __future__ import annotations\n\nimport logging\nimport sys\nimport time\nfrom dataclasses import dataclass\nfrom typing "
},
{
"path": "src/scripts/handler.py",
"chars": 14738,
"preview": "from __future__ import annotations\n\nimport logging\nimport sys\nimport threading\nimport time\nfrom contextlib import suppre"
},
{
"path": "src/scripts/loot_filter_tts.py",
"chars": 5515,
"preview": "import logging\nimport time\nfrom typing import TYPE_CHECKING\n\nimport src.item.descr.read_descr_tts\nfrom src.cam import Ca"
},
{
"path": "src/scripts/vision_mode_fast.py",
"chars": 6540,
"preview": "import logging\nimport queue\nimport tkinter as tk\nfrom tkinter import font\nfrom tkinter.font import Font\n\nimport src.item"
},
{
"path": "src/scripts/vision_mode_with_highlighting.py",
"chars": 20625,
"preview": "import logging\nimport math\nimport queue\nimport threading\nimport time\nimport tkinter as tk\nfrom threading import Event, T"
},
{
"path": "src/startup_messages.py",
"chars": 1364,
"preview": "import logging\nfrom pathlib import Path\n\nfrom src import __version__\nfrom src.config.loader import IniConfigLoader\n\nBANN"
},
{
"path": "src/template_finder.py",
"chars": 11015,
"preview": "import logging\nimport threading\nimport time\nfrom dataclasses import dataclass\n\nimport cv2\nimport numpy as np\n\nfrom src i"
},
{
"path": "src/tools/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/tools/data/custom_affixes_enUS.json",
"chars": 1059,
"preview": "{\n \"corpse_damage\": \"corpse damage\",\n \"crowd_control_duration_lucky_hit_up_to_a_chance_to_heal_life\": \"crowd contr"
},
{
"path": "src/tools/data/custom_sigils_enUS.json",
"chars": 129,
"preview": "{\n \"dungeons\": {},\n \"major\": {},\n \"positive\": {\n \"chaos_rifts\": \"chaos rifts have opened in this place.\""
},
{
"path": "src/tools/gen_data.py",
"chars": 17338,
"preview": "# generate data from d4data repo\nimport json\nimport re\nfrom pathlib import Path\n\nD4LF_BASE_DIR = Path(__file__).parent.p"
},
{
"path": "src/tts.py",
"chars": 4745,
"preview": "import enum\nimport logging\nimport queue\nimport re\nimport sys\nimport threading\n\nimport pywintypes\nimport win32file\nimport"
},
{
"path": "src/ui/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/ui/char_inventory.py",
"chars": 624,
"preview": "from src.config.loader import IniConfigLoader\nfrom src.config.ui import ResManager\nfrom src.template_finder import Searc"
},
{
"path": "src/ui/inventory_base.py",
"chars": 3661,
"preview": "from dataclasses import dataclass\n\nimport cv2\nimport numpy as np\n\nfrom src.cam import Cam\nfrom src.config.ui import ResM"
},
{
"path": "src/ui/menu.py",
"chars": 2490,
"preview": "import logging\nimport sys\nimport time\nfrom enum import Enum\nfrom typing import TYPE_CHECKING\n\nif sys.platform != \"darwin"
},
{
"path": "src/ui/stash.py",
"chars": 1215,
"preview": "import logging\nimport time\n\nfrom src.cam import Cam\nfrom src.config.loader import IniConfigLoader\nfrom src.config.ui imp"
},
{
"path": "src/ui/vendor.py",
"chars": 523,
"preview": "import logging\n\nfrom src.template_finder import SearchArgs\nfrom src.ui.inventory_base import InventoryBase\n\nLOGGER = log"
},
{
"path": "src/utils/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "src/utils/custom_mouse.py",
"chars": 9275,
"preview": "# Mostly copied from: https://github.com/patrikoss/pyclick\nimport math\nimport random\nimport time\n\nimport mouse as _mouse"
},
{
"path": "src/utils/image_operations.py",
"chars": 9652,
"preview": "import logging\nfrom copy import deepcopy\nfrom enum import Enum\n\nimport cv2\nimport numpy as np\n\nLOGGER = logging.getLogge"
},
{
"path": "src/utils/misc.py",
"chars": 4889,
"preview": "import ast\nimport math\nimport random\nimport re\nimport string\nimport time\nimport unicodedata\nfrom functools import wraps\n"
},
{
"path": "src/utils/process_handler.py",
"chars": 1994,
"preview": "import ctypes\nimport logging\nimport os\nimport time\n\nimport psutil\n\nfrom src.utils.window import get_window_spec_id\n\nLOGG"
},
{
"path": "src/utils/roi_operations.py",
"chars": 6535,
"preview": "import logging\nfrom enum import Enum\n\nfrom src.config.ui import ResManager\n\nLOGGER = logging.getLogger(__name__)\n\n\ndef c"
},
{
"path": "src/utils/window.py",
"chars": 4365,
"preview": "import ctypes\nimport logging\nimport pathlib\nimport threading\nimport time\nfrom dataclasses import dataclass\nfrom datetime"
},
{
"path": "tests/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/config/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/config/data/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/config/data/sigils.py",
"chars": 688,
"preview": "all_bad_cases = [\n # 1 item\n {\"Sigils\": {\"blacklist\": \"monster_cold_resist\"}},\n {\"Sigils\": {\"blacklist\": [\"mons"
},
{
"path": "tests/config/data/uniques.py",
"chars": 477,
"preview": "all_bad_cases = [\n ({\"GlobalUniques\": [{\"minPower\": -20}]}, \"must be greater than zero\"), # Has to be above 0\n ({"
},
{
"path": "tests/config/helper_test.py",
"chars": 1841,
"preview": "import pytest\n\nfrom src.config.helper import singleton, str_to_int_list, validate_hotkey\n\n\nclass TestKeyMustExist:\n d"
},
{
"path": "tests/config/loader_test.py",
"chars": 5010,
"preview": "from __future__ import annotations\n\nimport logging\nfrom typing import TYPE_CHECKING\n\nimport pytest\n\nfrom src.config.load"
},
{
"path": "tests/config/models_test.py",
"chars": 1714,
"preview": "import re\nfrom typing import TYPE_CHECKING, Any\n\nimport pytest\nfrom pydantic import ValidationError\n\nfrom src.config.pro"
},
{
"path": "tests/config/ui_test.py",
"chars": 1975,
"preview": "import numpy as np\nimport pytest\nfrom natsort import natsorted\n\nfrom src.config.data import COLORS\nfrom src.config.ui im"
},
{
"path": "tests/conftest.py",
"chars": 470,
"preview": "import typing\n\nimport pytest\n\nfrom src.config.loader import IniConfigLoader\nfrom src.config.settings_models import Brows"
},
{
"path": "tests/gui/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/gui/importer/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/gui/importer/test_d4builds.py",
"chars": 4620,
"preview": "import os\nimport typing\n\nimport lxml.html\nimport pytest\n\nfrom src.dataloader import Dataloader\nfrom src.gui.importer.d4b"
},
{
"path": "tests/gui/importer/test_diablo_trade.py",
"chars": 864,
"preview": "import os\nimport typing\n\nimport pytest\n\nfrom src.dataloader import Dataloader\nfrom src.gui.importer.diablo_trade import "
},
{
"path": "tests/gui/importer/test_gui_common.py",
"chars": 2497,
"preview": "from src.config.profile_models import ProfileModel\nfrom src.gui.importer.gui_common import _to_yaml_str, build_default_p"
},
{
"path": "tests/gui/importer/test_maxroll.py",
"chars": 4351,
"preview": "import os\nimport typing\n\nimport pytest\n\nfrom src.dataloader import Dataloader\nfrom src.gui.importer.importer_config impo"
},
{
"path": "tests/gui/importer/test_mobalytics.py",
"chars": 2479,
"preview": "import os\nimport typing\n\nimport pytest\n\nfrom src.dataloader import Dataloader\nfrom src.gui.importer.importer_config impo"
},
{
"path": "tests/item/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/item/descr/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/item/filter/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/item/filter/data/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/item/filter/data/affixes.py",
"chars": 5763,
"preview": "from src.item.data.affix import Affix, AffixType\nfrom src.item.data.item_type import ItemType\nfrom src.item.data.rarity "
},
{
"path": "tests/item/filter/data/aspects.py",
"chars": 750,
"preview": "from src.item.data.aspect import Aspect\nfrom src.item.data.item_type import ItemType\nfrom src.item.data.rarity import It"
},
{
"path": "tests/item/filter/data/filters.py",
"chars": 12303,
"preview": "from src.config.profile_models import (\n AffixFilterCountModel,\n AffixFilterModel,\n GlobalUniqueModel,\n Item"
},
{
"path": "tests/item/filter/data/items.py",
"chars": 689,
"preview": "from src.item.data.affix import Affix\nfrom src.item.data.item_type import ItemType\nfrom src.item.data.rarity import Item"
},
{
"path": "tests/item/filter/data/sigils.py",
"chars": 2780,
"preview": "from src.item.data.affix import Affix\nfrom src.item.data.item_type import ItemType\nfrom src.item.data.rarity import Item"
},
{
"path": "tests/item/filter/data/tributes.py",
"chars": 690,
"preview": "from src.item.data.item_type import ItemType\nfrom src.item.data.rarity import ItemRarity\nfrom src.item.models import Ite"
},
{
"path": "tests/item/filter/data/uniques.py",
"chars": 5105,
"preview": "from src.item.data.affix import Affix, AffixType\nfrom src.item.data.aspect import Aspect\nfrom src.item.data.item_type im"
},
{
"path": "tests/item/filter/filter_test.py",
"chars": 6018,
"preview": "from __future__ import annotations\n\nimport typing\n\nimport pytest\nfrom natsort import natsorted\n\nfrom src.config.loader i"
},
{
"path": "tests/item/read_descr_season6_tts_test.py",
"chars": 5729,
"preview": "import pytest\n\nimport src.tts\nfrom src.item.data.affix import Affix, AffixType\nfrom src.item.data.aspect import Aspect\nf"
},
{
"path": "tests/item/read_descr_season8_tts_test.py",
"chars": 3542,
"preview": "import pytest\n\nimport src.tts\nfrom src.item.data.affix import Affix, AffixType\nfrom src.item.data.aspect import Aspect\nf"
},
{
"path": "tests/item/read_descr_season_11_tts_test.py",
"chars": 16662,
"preview": "import pytest\n\nimport src.tts\nfrom src.item.data.affix import Affix, AffixType\nfrom src.item.data.aspect import Aspect\nf"
},
{
"path": "tests/item/read_descr_season_12_tts_test.py",
"chars": 7336,
"preview": "import pytest\n\nimport src.tts\nfrom src.item.data.affix import Affix, AffixType\nfrom src.item.data.aspect import Aspect\nf"
},
{
"path": "tests/item/read_descr_season_13_tts_test.py",
"chars": 13663,
"preview": "import pytest\n\nimport src.tts\nfrom src.item.data.affix import Affix, AffixType\nfrom src.item.data.aspect import Aspect\nf"
},
{
"path": "tests/item/read_descr_tts_test.py",
"chars": 420,
"preview": "import src.tts\nfrom src.item.descr.read_descr_tts import read_descr\n\nLOOT_FILTER_TTS = [\"SELECT ALL\", \"Checkbox Disabled"
},
{
"path": "tests/template_finder_test.py",
"chars": 1908,
"preview": "import cv2\n\nimport src.template_finder\nfrom src.utils.misc import is_in_roi\n\n\ndef test_search():\n \"\"\"Test default sea"
},
{
"path": "tests/ui/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/ui/char_inventory_test.py",
"chars": 1958,
"preview": "import cv2\nimport pytest\n\nfrom src.cam import Cam\nfrom src.config import BASE_DIR\nfrom src.ui.char_inventory import Char"
},
{
"path": "tests/ui/chest_test.py",
"chars": 442,
"preview": "import cv2\nimport pytest\n\nfrom src.cam import Cam\nfrom src.config import BASE_DIR\nfrom src.ui.stash import Stash\n\nBASE_P"
},
{
"path": "tests/utils/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/utils/image_operations_test.py",
"chars": 5790,
"preview": "import numpy as np\nimport pytest\n\nfrom src.utils.image_operations import (\n ThresholdTypes,\n alpha_to_mask,\n co"
},
{
"path": "tests/utils/roi_operations_test.py",
"chars": 1719,
"preview": "from src.utils.roi_operations import bounding_box, get_center, intersect, is_in_roi\n\n\ndef test_get_center() -> None:\n "
},
{
"path": "tts/install_dll.cmd",
"chars": 14599,
"preview": "@echo off\nsetlocal\ncd /d \"%~dp0\"\n\nnet session >nul 2>&1\nif %errorlevel% neq 0 (\n echo Requesting administrator access"
},
{
"path": "tts/saapi.cpp",
"chars": 1366,
"preview": "#include \"saapi.h\"\n\n#include <tchar.h>\n\n#include <sstream>\n#include <string>\n#define WIN32_LEAN_AND_MEAN\n#include <windo"
},
{
"path": "tts/saapi.h",
"chars": 268,
"preview": "void InitPipe();\n\nextern \"C\" __declspec(dllexport) bool SA_SayW(const wchar_t* str);\nextern \"C\" __declspec(dllexport) bo"
},
{
"path": "tts/saapi.vcxproj",
"chars": 2949,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msb"
}
]
About this extraction
This page contains the full source code of the aeon0/d4lf GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 153 files (948.8 KB), approximately 236.1k tokens, and a symbol index with 1038 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.