Skip to content

Commit

Permalink
allow greater affixes
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisoro committed Oct 23, 2024
1 parent 88baa65 commit b197d1e
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 85 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ To use TTS, you need to:

Currently, `use_tts` enables either a mixed mode where image processing is still used for item and affix position detection,
but TTS is used for everything text-related. This results in a small improvement in performance and a major improvement
in accuracy. Or a full mode where only TTS is used.
in accuracy. Or a full mode where only TTS is used. This will be super fast but loses the overlay.

**IT IS NOT POSSIBLE TO DETECT GREATER AFFIXES USING TTS!!! YOU NEED TO SPECIFY THEM USING THEIR VALUES!**
**YOU NEED TO ENABLE ADVANCED TOOLTIP INFORMATION**

**The following is currently supported using any form of tts:**

Expand Down
6 changes: 4 additions & 2 deletions src/item/data/affix.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ class AffixType(enum.Enum):

@dataclass
class Affix:
name: str
type: AffixType = AffixType.normal
loc: tuple[int, int] = None
max_value: float | None = None
min_value: float | None = None
name: str = ""
text: str = ""
type: AffixType = AffixType.normal
value: float | None = None

def __eq__(self, other: "Affix") -> bool:
Expand Down
2 changes: 2 additions & 0 deletions src/item/descr/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def keep_letters_and_spaces(text):
return "".join(char for char in text if char.isalpha() or char.isspace()).strip().replace(" ", " ")
180 changes: 104 additions & 76 deletions src/item/descr/read_descr_tts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import copy
import logging
import re

import numpy as np
import rapidfuzz
Expand All @@ -12,15 +13,63 @@
from src.item.data.aspect import Aspect
from src.item.data.item_type import ItemType, is_armor, is_consumable, is_jewelry, is_mapping, is_socketable, is_weapon
from src.item.data.rarity import ItemRarity
from src.item.descr import keep_letters_and_spaces
from src.item.descr.text import clean_str, closest_match, find_number
from src.item.descr.texture import find_affix_bullets, find_seperator_short, find_seperators_long
from src.item.models import Item
from src.template_finder import TemplateMatch
from src.utils.window import screenshot

_AFFIX_RE = re.compile(
r"(?P<affixvalue1>[0-9]+)[^0-9]+\[(?P<minvalue1>[0-9]+) - (?P<maxvalue1>[0-9]+)\]|"
r"(?P<affixvalue2>[0-9]+\.[0-9]+).+?\[(?P<minvalue2>[0-9]+\.[0-9]+) - (?P<maxvalue2>[0-9]+\.[0-9]+)\]|"
r"(?P<affixvalue3>[.0-9]+)[^0-9]+\[(?P<onlyvalue>[.0-9]+)\]|"
r".?![^\[\]]*[\[\]](?P<affixvalue4>\d+.?:\.\d+?)(?P<greateraffix1>[ ]*)|"
r"(?P<greateraffix2>\d+)(?![^\[]*\[).*",
re.DOTALL,
)

_AFFIX_REPLACEMENTS = [
"%",
"+",
",",
"[+]",
"[x]",
]
LOGGER = logging.getLogger(__name__)


def _add_affixes_from_tts(tts_section: list[str], item: Item) -> Item:
inherent_num = 0
affixes_num = 3 if item.rarity == ItemRarity.Legendary else 4
if is_weapon(item.item_type) or item.item_type in [ItemType.Amulet, ItemType.Boots]:
inherent_num = 1
elif item.item_type in [ItemType.Ring]:
inherent_num = 2
elif item.item_type in [ItemType.Shield]:
inherent_num = 4
affixes = _get_affixes_from_tts_section(tts_section, item, inherent_num + affixes_num)
for i, affix_text in enumerate(affixes):
if i < inherent_num:
affix = Affix(text=affix_text)
affix.type = AffixType.inherent
affix.name = rapidfuzz.process.extractOne(
keep_letters_and_spaces(affix_text), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)[0]
item.inherent.append(affix)
elif i < inherent_num + affixes_num:
affix = _get_affix_from_text(affix_text)
item.affixes.append(affix)
else:
name = closest_match(clean_str(affix_text)[:AFFIX_COMPARISON_CHARS], Dataloader().aspect_unique_dict)
item.aspect = Aspect(
name=name,
text=affix_text,
value=find_number(affix_text),
)
return item


def _add_affixes_from_tts_mixed(
tts_section: list[str], item: Item, inherent_affix_bullets: list[TemplateMatch], affix_bullets: list[TemplateMatch]
) -> Item:
Expand All @@ -30,91 +79,32 @@ def _add_affixes_from_tts_mixed(
len(inherent_affix_bullets)
+ len([x for x in affix_bullets if any(x.name.startswith(s) for s in ["affix", "greater_affix", "rerolled"])]),
)
for i, affix in enumerate(affixes):
for i, affix_text in enumerate(affixes):
if i < len(inherent_affix_bullets):
name = rapidfuzz.process.extractOne(
clean_str(affix), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)
item.inherent.append(
Affix(
name=name[0],
loc=inherent_affix_bullets[i].center,
text=affix,
type=AffixType.inherent,
value=find_number(affix),
)
)
affix = Affix(text=affix_text)
affix.type = AffixType.inherent
affix.name = rapidfuzz.process.extractOne(
keep_letters_and_spaces(affix_text), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)[0]
affix.loc = (inherent_affix_bullets[i].center,)
item.inherent.append(affix)
elif i < len(inherent_affix_bullets) + len(affix_bullets):
name = rapidfuzz.process.extractOne(
clean_str(affix), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)
affix = _get_affix_from_text(affix_text)
affix.loc = (affix_bullets[i - len(inherent_affix_bullets)].center,)
if affix_bullets[i - len(inherent_affix_bullets)].name.startswith("greater_affix"):
a_type = AffixType.greater
affix.type = AffixType.greater
elif affix_bullets[i - len(inherent_affix_bullets)].name.startswith("rerolled"):
a_type = AffixType.rerolled
affix.type = AffixType.rerolled
else:
a_type = AffixType.normal
item.affixes.append(
Affix(
name=name[0],
loc=affix_bullets[i - len(inherent_affix_bullets)].center,
text=affix,
type=a_type,
value=find_number(affix),
)
)
affix.type = AffixType.normal
item.affixes.append(affix)
else:
name = closest_match(clean_str(affix)[:AFFIX_COMPARISON_CHARS], Dataloader().aspect_unique_dict)
name = closest_match(clean_str(affix_text)[:AFFIX_COMPARISON_CHARS], Dataloader().aspect_unique_dict)
item.aspect = Aspect(
name=name,
loc=affix_bullets[i - len(inherent_affix_bullets) - len(affix_bullets)].center,
text=affix,
value=find_number(affix),
)
return item


def _add_affixes_from_tts(tts_section: list[str], item: Item) -> Item:
inherent_num = 0
affixes_num = 3 if item.rarity == ItemRarity.Legendary else 4
if is_weapon(item.item_type) or item.item_type in [ItemType.Amulet, ItemType.Boots]:
inherent_num = 1
elif item.item_type in [ItemType.Ring]:
inherent_num = 2
elif item.item_type in [ItemType.Shield]:
inherent_num = 4
affixes = _get_affixes_from_tts_section(tts_section, item, inherent_num + affixes_num)
for i, affix in enumerate(affixes):
if i < inherent_num:
name = rapidfuzz.process.extractOne(
clean_str(affix), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)
item.inherent.append(
Affix(
name=name[0],
text=affix,
type=AffixType.inherent,
value=find_number(affix),
)
)
elif i < inherent_num + affixes_num:
name = rapidfuzz.process.extractOne(
clean_str(affix), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)
item.affixes.append(
Affix(
name=name[0],
text=affix,
type=AffixType.normal,
value=find_number(affix),
)
)
else:
name = closest_match(clean_str(affix)[:AFFIX_COMPARISON_CHARS], Dataloader().aspect_unique_dict)
item.aspect = Aspect(
name=name,
text=affix,
value=find_number(affix),
text=affix_text,
value=find_number(affix_text),
)
return item

Expand Down Expand Up @@ -190,6 +180,42 @@ def _get_affixes_from_tts_section(tts_section: list[str], item: Item, length: in
return tts_section[start : start + length]


def _get_affix_from_text(text: str) -> Affix:
result = Affix(text=text)
for x in _AFFIX_REPLACEMENTS:
text = text.replace(x, "")
matched_groups = {}
for match in _AFFIX_RE.finditer(text):
matched_groups = {name: value for name, value in match.groupdict().items() if value is not None}
if not matched_groups:
raise Exception(f"Could not match affix text: {text}")
for x in ["minvalue1", "minvalue2"]:
if matched_groups.get(x) is not None:
result.min_value = float(matched_groups[x])
break
for x in ["maxvalue1", "maxvalue2"]:
if matched_groups.get(x) is not None:
result.max_value = float(matched_groups[x])
break
for x in ["affixvalue1", "affixvalue2", "affixvalue3", "affixvalue4"]:
if matched_groups.get(x) is not None:
result.value = float(matched_groups[x])
break
for x in ["greateraffix1", "greateraffix2"]:
if matched_groups.get(x) is not None:
result.type = AffixType.greater
if x == "greateraffix2":
result.value = float(matched_groups[x])
break
if matched_groups.get("onlyvalue") is not None:
result.min_value = float(matched_groups.get("onlyvalue"))
result.max_value = float(matched_groups.get("onlyvalue"))
result.name = rapidfuzz.process.extractOne(
keep_letters_and_spaces(text), list(Dataloader().affix_dict), scorer=rapidfuzz.distance.Levenshtein.distance
)[0]
return result


def _get_item_rarity(data: str) -> ItemRarity | None:
res = rapidfuzz.process.extractOne(data, [rar.value for rar in ItemRarity], scorer=rapidfuzz.distance.Levenshtein.distance)
try:
Expand Down Expand Up @@ -294,6 +320,8 @@ def read_descr() -> Item | None:
return item
if all([not is_armor(item.item_type), not is_jewelry(item.item_type), not is_weapon(item.item_type)]):
return None
if item.rarity not in [ItemRarity.Legendary, ItemRarity.Mythic, ItemRarity.Unique]:
return item

item.codex_upgrade = _is_codex_upgrade(tts_section, item)
return _add_affixes_from_tts(tts_section, item)
2 changes: 1 addition & 1 deletion src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def main():
ScriptHandler()

if IniConfigLoader().general.use_tts in [UseTTSType.full, UseTTSType.mixed]:
LOGGER.debug(f"tts: {IniConfigLoader().general.use_tts.value}")
LOGGER.debug(f"TTS mode: {IniConfigLoader().general.use_tts.value}")
tts.start_connection()

overlay = Overlay()
Expand Down
14 changes: 10 additions & 4 deletions src/tts.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,18 @@ def read_pipe() -> None:

while True:
try:
data = win32file.ReadFile(handle, 512)[1].decode().strip()

# Block until data is available (assumes PIPE_WAIT)
win32file.ReadFile(handle, 0, None)
# Query message size
_, _, message_size = win32pipe.PeekNamedPipe(handle, 0)
# Read message
_, data = win32file.ReadFile(handle, message_size, None)
data = data.decode().replace("\x00", "")
if not data:
continue
if "DISCONNECTED" in data:
break
for s in [s for s in data.split("\x00") if s]:
_DATA_QUEUE.put(s)
_DATA_QUEUE.put(data)
except Exception as e:
print(f"Error while reading data: {e}")

Expand Down

0 comments on commit b197d1e

Please sign in to comment.