fix: add comma to loot regex patterns for items like 'Dominax Original Garter, Adjusted (L)'
- Add comma (,) to character class in PATTERN_LOOT_EN and PATTERN_LOOT_SV - Add comma (,) to PATTERN_LOOT_NO_VALUE_EN and PATTERN_LOOT_NO_VALUE_SV - Fixes items with commas in their names not being tracked
This commit is contained in:
parent
e4fd75e2a7
commit
6d8f710244
|
|
@ -43,53 +43,53 @@ class HuntingSessionStats:
|
||||||
total_shrapnel_ped: Decimal = Decimal('0.0')
|
total_shrapnel_ped: Decimal = Decimal('0.0')
|
||||||
total_universal_ammo_ped: Decimal = Decimal('0.0')
|
total_universal_ammo_ped: Decimal = Decimal('0.0')
|
||||||
total_other_loot_ped: Decimal = Decimal('0.0') # Non-shrapnel, non-UA loot
|
total_other_loot_ped: Decimal = Decimal('0.0') # Non-shrapnel, non-UA loot
|
||||||
|
|
||||||
# Cost tracking
|
# Cost tracking
|
||||||
weapon_cost_ped: Decimal = Decimal('0.0')
|
weapon_cost_ped: Decimal = Decimal('0.0')
|
||||||
armor_cost_ped: Decimal = Decimal('0.0')
|
armor_cost_ped: Decimal = Decimal('0.0')
|
||||||
healing_cost_ped: Decimal = Decimal('0.0')
|
healing_cost_ped: Decimal = Decimal('0.0')
|
||||||
plates_cost_ped: Decimal = Decimal('0.0')
|
plates_cost_ped: Decimal = Decimal('0.0')
|
||||||
total_cost_ped: Decimal = Decimal('0.0')
|
total_cost_ped: Decimal = Decimal('0.0')
|
||||||
|
|
||||||
# Combat tracking
|
# Combat tracking
|
||||||
damage_dealt: Decimal = Decimal('0.0')
|
damage_dealt: Decimal = Decimal('0.0')
|
||||||
damage_taken: Decimal = Decimal('0.0')
|
damage_taken: Decimal = Decimal('0.0')
|
||||||
healing_done: Decimal = Decimal('0.0')
|
healing_done: Decimal = Decimal('0.0')
|
||||||
shots_fired: int = 0
|
shots_fired: int = 0
|
||||||
kills: int = 0
|
kills: int = 0
|
||||||
|
|
||||||
# Special events
|
# Special events
|
||||||
globals_count: int = 0
|
globals_count: int = 0
|
||||||
hofs_count: int = 0
|
hofs_count: int = 0
|
||||||
personal_globals: List[Dict[str, Any]] = field(default_factory=list)
|
personal_globals: List[Dict[str, Any]] = field(default_factory=list)
|
||||||
|
|
||||||
# Calculated metrics
|
# Calculated metrics
|
||||||
@property
|
@property
|
||||||
def net_profit_ped(self) -> Decimal:
|
def net_profit_ped(self) -> Decimal:
|
||||||
"""Calculate net profit (excluding shrapnel from loot)."""
|
"""Calculate net profit (excluding shrapnel from loot)."""
|
||||||
return self.total_other_loot_ped - self.total_cost_ped
|
return self.total_other_loot_ped - self.total_cost_ped
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def return_percentage(self) -> Decimal:
|
def return_percentage(self) -> Decimal:
|
||||||
"""Calculate return percentage (loot/cost * 100)."""
|
"""Calculate return percentage (loot/cost * 100)."""
|
||||||
if self.total_cost_ped > 0:
|
if self.total_cost_ped > 0:
|
||||||
return (self.total_other_loot_ped / self.total_cost_ped) * Decimal('100')
|
return (self.total_other_loot_ped / self.total_cost_ped) * Decimal('100')
|
||||||
return Decimal('0.0')
|
return Decimal('0.0')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cost_per_kill(self) -> Decimal:
|
def cost_per_kill(self) -> Decimal:
|
||||||
"""Calculate cost per kill."""
|
"""Calculate cost per kill."""
|
||||||
if self.kills > 0:
|
if self.kills > 0:
|
||||||
return self.total_cost_ped / self.kills
|
return self.total_cost_ped / self.kills
|
||||||
return Decimal('0.0')
|
return Decimal('0.0')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dpp(self) -> Decimal:
|
def dpp(self) -> Decimal:
|
||||||
"""Calculate Damage Per PED (efficiency metric)."""
|
"""Calculate Damage Per PED (efficiency metric)."""
|
||||||
if self.total_cost_ped > 0:
|
if self.total_cost_ped > 0:
|
||||||
return self.damage_dealt / self.total_cost_ped
|
return self.damage_dealt / self.total_cost_ped
|
||||||
return Decimal('0.0')
|
return Decimal('0.0')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def damage_per_kill(self) -> Decimal:
|
def damage_per_kill(self) -> Decimal:
|
||||||
"""Calculate average damage per kill."""
|
"""Calculate average damage per kill."""
|
||||||
|
|
@ -117,29 +117,29 @@ class LogWatcher:
|
||||||
# Swedish: "Du fick Shrapnel x (4627) Värde: 0.4627 PED"
|
# Swedish: "Du fick Shrapnel x (4627) Värde: 0.4627 PED"
|
||||||
PATTERN_LOOT_EN = re.compile(
|
PATTERN_LOOT_EN = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]:?\s*\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]:?\s*\[?\]?\s*'
|
||||||
r'You\s+received\s+\[?([\w\s\-()]+?)\]?\s+x\s*\((\d+)\)\s*'
|
r'You\s+received\s+\[?([\w\s\-(),]+?)\]?\s+x\s*\((\d+)\)\s*'
|
||||||
r'Value:\s+(\d+(?:\.\d+)?)\s+PED',
|
r'Value:\s+(\d+(?:\.\d+)?)\s+PED',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_LOOT_SV = re.compile(
|
PATTERN_LOOT_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Du\s+fick\s+([\w\s\-()]+?)\s+x\s*\((\d+)\)\s*'
|
r'Du\s+fick\s+([\w\s\-(),]+?)\s+x\s*\((\d+)\)\s*'
|
||||||
r'Värde:\s+(\d+(?:\.\d+)?)\s+PED',
|
r'Värde:\s+(\d+(?:\.\d+)?)\s+PED',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
# LOOT PATTERN WITHOUT VALUE (some items don't show value)
|
# LOOT PATTERN WITHOUT VALUE (some items don't show value)
|
||||||
# English: "You received Animal Thyroid Oil x 5"
|
# English: "You received Animal Thyroid Oil x 5"
|
||||||
PATTERN_LOOT_NO_VALUE_EN = re.compile(
|
PATTERN_LOOT_NO_VALUE_EN = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]:?\s*\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]:?\s*\[?\]?\s*'
|
||||||
r'You\s+received\s+\[?([\w\s\-()]+?)\]?\s+x\s*(\d+)',
|
r'You\s+received\s+\[?([\w\s\-(),]+?)\]?\s+x\s*(\d+)',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_LOOT_NO_VALUE_SV = re.compile(
|
PATTERN_LOOT_NO_VALUE_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Du\s+fick\s+([\w\s\-()]+?)\s+x\s*(\d+)',
|
r'Du\s+fick\s+([\w\s\-(),]+?)\s+x\s*(\d+)',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -151,7 +151,7 @@ class LogWatcher:
|
||||||
r'You\s+killed\s+\[?([\w\s\-()]+?)\]?',
|
r'You\s+killed\s+\[?([\w\s\-()]+?)\]?',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_KILL_SV = re.compile(
|
PATTERN_KILL_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Du\s+dödade\s+\[?([\w\s\-()]+?)\]?',
|
r'Du\s+dödade\s+\[?([\w\s\-()]+?)\]?',
|
||||||
|
|
@ -201,7 +201,7 @@ class LogWatcher:
|
||||||
r'(?:for|with\s+a\s+value\s+of)\s+(\d+(?:\.\d+)?)\s+PED',
|
r'(?:for|with\s+a\s+value\s+of)\s+(\d+(?:\.\d+)?)\s+PED',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_HOF_MARKER = re.compile(
|
PATTERN_HOF_MARKER = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[\w+\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[\w+\]\s+\[?\]?\s*'
|
||||||
r'.*?Hall\s+of\s+Fame',
|
r'.*?Hall\s+of\s+Fame',
|
||||||
|
|
@ -232,7 +232,7 @@ class LogWatcher:
|
||||||
r'You\s+have\s+advanced\s+to\s+level\s+(\d+)\s+in\s+([\w\s]+)',
|
r'You\s+have\s+advanced\s+to\s+level\s+(\d+)\s+in\s+([\w\s]+)',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_LEVEL_UP_SV = re.compile(
|
PATTERN_LEVEL_UP_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Du\s+har\s+avancerat\s+till\s+nivå\s+(\d+)\s+i\s+([\w\s]+)',
|
r'Du\s+har\s+avancerat\s+till\s+nivå\s+(\d+)\s+i\s+([\w\s]+)',
|
||||||
|
|
@ -322,7 +322,7 @@ class LogWatcher:
|
||||||
r'(You\s+Evaded|You\s+dodged|The\s+target\s+Evaded\s+your\s+attack|The\s+target\s+Dodged|The\s+attack\s+missed\s+you)',
|
r'(You\s+Evaded|You\s+dodged|The\s+target\s+Evaded\s+your\s+attack|The\s+target\s+Dodged|The\s+attack\s+missed\s+you)',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_EVADE_SV = re.compile(
|
PATTERN_EVADE_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'(Du\s+undvek|Målet\s+undvek\s+din\s+attack|Attacken\s+missade\s+dig)',
|
r'(Du\s+undvek|Målet\s+undvek\s+din\s+attack|Attacken\s+missade\s+dig)',
|
||||||
|
|
@ -337,13 +337,13 @@ class LogWatcher:
|
||||||
r'Your\s+([\w\s\-()]+?)\s+has\s+decayed\s+(\d+(?:\.\d+)?)\s+PEC',
|
r'Your\s+([\w\s\-()]+?)\s+has\s+decayed\s+(\d+(?:\.\d+)?)\s+PEC',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_DECAY_SV = re.compile(
|
PATTERN_DECAY_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Din\s+([\w\s\-()]+?)\s+har\s+decayed\s+(\d+(?:\.\d+)?)\s+PEC',
|
r'Din\s+([\w\s\-()]+?)\s+har\s+decayed\s+(\d+(?:\.\d+)?)\s+PEC',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_WEAPON_BROKEN_SV = re.compile(
|
PATTERN_WEAPON_BROKEN_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Din\s+([\w\s\-()]+?)\s+har\s+nått\s+minimalt\s+skick',
|
r'Din\s+([\w\s\-()]+?)\s+har\s+nått\s+minimalt\s+skick',
|
||||||
|
|
@ -365,7 +365,7 @@ class LogWatcher:
|
||||||
r'Överföring\s+slutförd.*?((\d+(?:\.\d+)?))\s+PED',
|
r'Överföring\s+slutförd.*?((\d+(?:\.\d+)?))\s+PED',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_PED_TRANSFER_EN = re.compile(
|
PATTERN_PED_TRANSFER_EN = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+'
|
||||||
r'Transfer\s+complete.*?((\d+(?:\.\d+)?))\s+PED',
|
r'Transfer\s+complete.*?((\d+(?:\.\d+)?))\s+PED',
|
||||||
|
|
@ -394,7 +394,7 @@ class LogWatcher:
|
||||||
r'You\s+received\s+(\d+(?:\.\d+)?)\s+PED\s+from\s+your\s+teammates',
|
r'You\s+received\s+(\d+(?:\.\d+)?)\s+PED\s+from\s+your\s+teammates',
|
||||||
re.IGNORECASE
|
re.IGNORECASE
|
||||||
)
|
)
|
||||||
|
|
||||||
PATTERN_TEAM_SHARE_SV = re.compile(
|
PATTERN_TEAM_SHARE_SV = re.compile(
|
||||||
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[System\]\s+\[?\]?\s*'
|
||||||
r'Du\s+fick\s+(\d+(?:\.\d+)?)\s+PED\s+från\s+dina\s+lagkamrater',
|
r'Du\s+fick\s+(\d+(?:\.\d+)?)\s+PED\s+från\s+dina\s+lagkamrater',
|
||||||
|
|
@ -529,7 +529,7 @@ class LogWatcher:
|
||||||
def _is_shrapnel(self, item_name: str) -> bool:
|
def _is_shrapnel(self, item_name: str) -> bool:
|
||||||
"""Check if item is Shrapnel."""
|
"""Check if item is Shrapnel."""
|
||||||
return item_name.strip().lower() == 'shrapnel'
|
return item_name.strip().lower() == 'shrapnel'
|
||||||
|
|
||||||
def _is_universal_ammo(self, item_name: str) -> bool:
|
def _is_universal_ammo(self, item_name: str) -> bool:
|
||||||
"""Check if item is Universal Ammo."""
|
"""Check if item is Universal Ammo."""
|
||||||
name = item_name.strip().lower()
|
name = item_name.strip().lower()
|
||||||
|
|
@ -539,7 +539,7 @@ class LogWatcher:
|
||||||
"""Categorize loot item and return LootItem."""
|
"""Categorize loot item and return LootItem."""
|
||||||
is_shrapnel = self._is_shrapnel(item_name)
|
is_shrapnel = self._is_shrapnel(item_name)
|
||||||
is_ua = self._is_universal_ammo(item_name)
|
is_ua = self._is_universal_ammo(item_name)
|
||||||
|
|
||||||
return LootItem(
|
return LootItem(
|
||||||
name=item_name.strip(),
|
name=item_name.strip(),
|
||||||
quantity=1, # Will be set by caller
|
quantity=1, # Will be set by caller
|
||||||
|
|
@ -558,12 +558,12 @@ class LogWatcher:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Try each pattern in priority order
|
# Try each pattern in priority order
|
||||||
|
|
||||||
# KILL - Swedish
|
# KILL - Swedish
|
||||||
match = self.PATTERN_KILL_SV.match(line)
|
match = self.PATTERN_KILL_SV.match(line)
|
||||||
if match:
|
if match:
|
||||||
return self._create_kill_event(match, line, 'swedish')
|
return self._create_kill_event(match, line, 'swedish')
|
||||||
|
|
||||||
# KILL - English
|
# KILL - English
|
||||||
match = self.PATTERN_KILL_EN.match(line)
|
match = self.PATTERN_KILL_EN.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -578,12 +578,12 @@ class LogWatcher:
|
||||||
match = self.PATTERN_LOOT_EN.match(line)
|
match = self.PATTERN_LOOT_EN.match(line)
|
||||||
if match:
|
if match:
|
||||||
return self._create_loot_event_en(match, line)
|
return self._create_loot_event_en(match, line)
|
||||||
|
|
||||||
# LOOT WITHOUT VALUE - Swedish
|
# LOOT WITHOUT VALUE - Swedish
|
||||||
match = self.PATTERN_LOOT_NO_VALUE_SV.match(line)
|
match = self.PATTERN_LOOT_NO_VALUE_SV.match(line)
|
||||||
if match:
|
if match:
|
||||||
return self._create_loot_event_no_value(match, line, 'swedish')
|
return self._create_loot_event_no_value(match, line, 'swedish')
|
||||||
|
|
||||||
# LOOT WITHOUT VALUE - English
|
# LOOT WITHOUT VALUE - English
|
||||||
match = self.PATTERN_LOOT_NO_VALUE_EN.match(line)
|
match = self.PATTERN_LOOT_NO_VALUE_EN.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -608,7 +608,7 @@ class LogWatcher:
|
||||||
match = self.PATTERN_PERSONAL_GLOBAL_EN.match(line)
|
match = self.PATTERN_PERSONAL_GLOBAL_EN.match(line)
|
||||||
if match:
|
if match:
|
||||||
return self._create_personal_global_event(match, line, 'english')
|
return self._create_personal_global_event(match, line, 'english')
|
||||||
|
|
||||||
# HOF - English
|
# HOF - English
|
||||||
match = self.PATTERN_HOF_EN.match(line)
|
match = self.PATTERN_HOF_EN.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -665,7 +665,7 @@ class LogWatcher:
|
||||||
'language': 'english'
|
'language': 'english'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# LEVEL UP - Swedish
|
# LEVEL UP - Swedish
|
||||||
match = self.PATTERN_LEVEL_UP_SV.match(line)
|
match = self.PATTERN_LEVEL_UP_SV.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -797,7 +797,7 @@ class LogWatcher:
|
||||||
raw_line=line,
|
raw_line=line,
|
||||||
data={'type': match.group(2), 'language': 'english'}
|
data={'type': match.group(2), 'language': 'english'}
|
||||||
)
|
)
|
||||||
|
|
||||||
# EVADE/DODGE/MISS - Swedish
|
# EVADE/DODGE/MISS - Swedish
|
||||||
match = self.PATTERN_EVADE_SV.match(line)
|
match = self.PATTERN_EVADE_SV.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -821,7 +821,7 @@ class LogWatcher:
|
||||||
'language': 'english'
|
'language': 'english'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# DECAY - Swedish
|
# DECAY - Swedish
|
||||||
match = self.PATTERN_DECAY_SV.match(line)
|
match = self.PATTERN_DECAY_SV.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -877,7 +877,7 @@ class LogWatcher:
|
||||||
'language': 'english'
|
'language': 'english'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# TEAM SHARE - English
|
# TEAM SHARE - English
|
||||||
match = self.PATTERN_TEAM_SHARE_EN.match(line)
|
match = self.PATTERN_TEAM_SHARE_EN.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -890,7 +890,7 @@ class LogWatcher:
|
||||||
'language': 'english'
|
'language': 'english'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# TEAM SHARE - Swedish
|
# TEAM SHARE - Swedish
|
||||||
match = self.PATTERN_TEAM_SHARE_SV.match(line)
|
match = self.PATTERN_TEAM_SHARE_SV.match(line)
|
||||||
if match:
|
if match:
|
||||||
|
|
@ -911,10 +911,10 @@ class LogWatcher:
|
||||||
item_name = match.group(2).strip()
|
item_name = match.group(2).strip()
|
||||||
quantity = int(match.group(3))
|
quantity = int(match.group(3))
|
||||||
value_ped = Decimal(match.group(4))
|
value_ped = Decimal(match.group(4))
|
||||||
|
|
||||||
loot_item = self._categorize_loot(item_name, value_ped)
|
loot_item = self._categorize_loot(item_name, value_ped)
|
||||||
loot_item.quantity = quantity
|
loot_item.quantity = quantity
|
||||||
|
|
||||||
return LogEvent(
|
return LogEvent(
|
||||||
timestamp=self._parse_timestamp(match.group(1)),
|
timestamp=self._parse_timestamp(match.group(1)),
|
||||||
event_type='loot',
|
event_type='loot',
|
||||||
|
|
@ -934,10 +934,10 @@ class LogWatcher:
|
||||||
item_name = match.group(2).strip()
|
item_name = match.group(2).strip()
|
||||||
quantity = int(match.group(3))
|
quantity = int(match.group(3))
|
||||||
value_ped = Decimal(match.group(4)) if match.group(4) else Decimal('0')
|
value_ped = Decimal(match.group(4)) if match.group(4) else Decimal('0')
|
||||||
|
|
||||||
loot_item = self._categorize_loot(item_name, value_ped)
|
loot_item = self._categorize_loot(item_name, value_ped)
|
||||||
loot_item.quantity = quantity
|
loot_item.quantity = quantity
|
||||||
|
|
||||||
return LogEvent(
|
return LogEvent(
|
||||||
timestamp=self._parse_timestamp(match.group(1)),
|
timestamp=self._parse_timestamp(match.group(1)),
|
||||||
event_type='loot',
|
event_type='loot',
|
||||||
|
|
@ -951,15 +951,15 @@ class LogWatcher:
|
||||||
'language': 'english'
|
'language': 'english'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_loot_event_no_value(self, match: re.Match, line: str, language: str) -> LogEvent:
|
def _create_loot_event_no_value(self, match: re.Match, line: str, language: str) -> LogEvent:
|
||||||
"""Create loot event without value."""
|
"""Create loot event without value."""
|
||||||
item_name = match.group(2).strip()
|
item_name = match.group(2).strip()
|
||||||
quantity = int(match.group(3))
|
quantity = int(match.group(3))
|
||||||
|
|
||||||
loot_item = self._categorize_loot(item_name, Decimal('0'))
|
loot_item = self._categorize_loot(item_name, Decimal('0'))
|
||||||
loot_item.quantity = quantity
|
loot_item.quantity = quantity
|
||||||
|
|
||||||
return LogEvent(
|
return LogEvent(
|
||||||
timestamp=self._parse_timestamp(match.group(1)),
|
timestamp=self._parse_timestamp(match.group(1)),
|
||||||
event_type='loot',
|
event_type='loot',
|
||||||
|
|
@ -973,7 +973,7 @@ class LogWatcher:
|
||||||
'language': language
|
'language': language
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_kill_event(self, match: re.Match, line: str, language: str) -> LogEvent:
|
def _create_kill_event(self, match: re.Match, line: str, language: str) -> LogEvent:
|
||||||
"""Create kill event."""
|
"""Create kill event."""
|
||||||
return LogEvent(
|
return LogEvent(
|
||||||
|
|
@ -1027,7 +1027,7 @@ class LogWatcher:
|
||||||
'language': language
|
'language': language
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def _create_hof_event(self, match: re.Match, line: str, language: str) -> LogEvent:
|
def _create_hof_event(self, match: re.Match, line: str, language: str) -> LogEvent:
|
||||||
"""Create Hall of Fame event."""
|
"""Create Hall of Fame event."""
|
||||||
return LogEvent(
|
return LogEvent(
|
||||||
|
|
@ -1118,40 +1118,40 @@ class LogWatcher:
|
||||||
class HuntingSessionTracker:
|
class HuntingSessionTracker:
|
||||||
"""
|
"""
|
||||||
Tracks hunting session statistics from LogWatcher events.
|
Tracks hunting session statistics from LogWatcher events.
|
||||||
|
|
||||||
This class accumulates all hunting-related data and provides
|
This class accumulates all hunting-related data and provides
|
||||||
real-time metrics like profit/loss, return percentage, etc.
|
real-time metrics like profit/loss, return percentage, etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.stats = HuntingSessionStats()
|
self.stats = HuntingSessionStats()
|
||||||
self._session_start: Optional[datetime] = None
|
self._session_start: Optional[datetime] = None
|
||||||
self._session_active = False
|
self._session_active = False
|
||||||
|
|
||||||
# Callbacks for real-time updates
|
# Callbacks for real-time updates
|
||||||
self._on_stats_update: Optional[Callable] = None
|
self._on_stats_update: Optional[Callable] = None
|
||||||
|
|
||||||
def start_session(self):
|
def start_session(self):
|
||||||
"""Start a new hunting session."""
|
"""Start a new hunting session."""
|
||||||
self._session_start = datetime.now()
|
self._session_start = datetime.now()
|
||||||
self._session_active = True
|
self._session_active = True
|
||||||
self.stats = HuntingSessionStats()
|
self.stats = HuntingSessionStats()
|
||||||
logger.info("Hunting session started")
|
logger.info("Hunting session started")
|
||||||
|
|
||||||
def end_session(self) -> HuntingSessionStats:
|
def end_session(self) -> HuntingSessionStats:
|
||||||
"""End the current hunting session and return final stats."""
|
"""End the current hunting session and return final stats."""
|
||||||
self._session_active = False
|
self._session_active = False
|
||||||
logger.info("Hunting session ended")
|
logger.info("Hunting session ended")
|
||||||
return self.stats
|
return self.stats
|
||||||
|
|
||||||
def is_active(self) -> bool:
|
def is_active(self) -> bool:
|
||||||
"""Check if session is active."""
|
"""Check if session is active."""
|
||||||
return self._session_active
|
return self._session_active
|
||||||
|
|
||||||
def set_stats_callback(self, callback: Callable[[HuntingSessionStats], None]):
|
def set_stats_callback(self, callback: Callable[[HuntingSessionStats], None]):
|
||||||
"""Set callback for real-time stats updates."""
|
"""Set callback for real-time stats updates."""
|
||||||
self._on_stats_update = callback
|
self._on_stats_update = callback
|
||||||
|
|
||||||
def _notify_update(self):
|
def _notify_update(self):
|
||||||
"""Notify listeners of stats update."""
|
"""Notify listeners of stats update."""
|
||||||
if self._on_stats_update:
|
if self._on_stats_update:
|
||||||
|
|
@ -1159,71 +1159,71 @@ class HuntingSessionTracker:
|
||||||
self._on_stats_update(self.stats)
|
self._on_stats_update(self.stats)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Stats callback error: {e}")
|
logger.error(f"Stats callback error: {e}")
|
||||||
|
|
||||||
def on_loot(self, event: LogEvent):
|
def on_loot(self, event: LogEvent):
|
||||||
"""Process loot event."""
|
"""Process loot event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
data = event.data
|
data = event.data
|
||||||
value_ped = data.get('value_ped', Decimal('0.0'))
|
value_ped = data.get('value_ped', Decimal('0.0'))
|
||||||
is_shrapnel = data.get('is_shrapnel', False)
|
is_shrapnel = data.get('is_shrapnel', False)
|
||||||
is_ua = data.get('is_universal_ammo', False)
|
is_ua = data.get('is_universal_ammo', False)
|
||||||
|
|
||||||
self.stats.total_loot_ped += value_ped
|
self.stats.total_loot_ped += value_ped
|
||||||
|
|
||||||
if is_shrapnel:
|
if is_shrapnel:
|
||||||
self.stats.total_shrapnel_ped += value_ped
|
self.stats.total_shrapnel_ped += value_ped
|
||||||
elif is_ua:
|
elif is_ua:
|
||||||
self.stats.total_universal_ammo_ped += value_ped
|
self.stats.total_universal_ammo_ped += value_ped
|
||||||
else:
|
else:
|
||||||
self.stats.total_other_loot_ped += value_ped
|
self.stats.total_other_loot_ped += value_ped
|
||||||
|
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_kill(self, event: LogEvent):
|
def on_kill(self, event: LogEvent):
|
||||||
"""Process kill event."""
|
"""Process kill event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stats.kills += 1
|
self.stats.kills += 1
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_damage_dealt(self, event: LogEvent):
|
def on_damage_dealt(self, event: LogEvent):
|
||||||
"""Process damage dealt event."""
|
"""Process damage dealt event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
damage = event.data.get('damage', Decimal('0.0'))
|
damage = event.data.get('damage', Decimal('0.0'))
|
||||||
self.stats.damage_dealt += damage
|
self.stats.damage_dealt += damage
|
||||||
self.stats.shots_fired += 1 # Each damage event = 1 shot
|
self.stats.shots_fired += 1 # Each damage event = 1 shot
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_damage_taken(self, event: LogEvent):
|
def on_damage_taken(self, event: LogEvent):
|
||||||
"""Process damage taken event."""
|
"""Process damage taken event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
damage = event.data.get('damage', Decimal('0.0'))
|
damage = event.data.get('damage', Decimal('0.0'))
|
||||||
self.stats.damage_taken += damage
|
self.stats.damage_taken += damage
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_heal(self, event: LogEvent):
|
def on_heal(self, event: LogEvent):
|
||||||
"""Process heal event."""
|
"""Process heal event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
heal_amount = event.data.get('heal_amount', Decimal('0.0'))
|
heal_amount = event.data.get('heal_amount', Decimal('0.0'))
|
||||||
self.stats.healing_done += heal_amount
|
self.stats.healing_done += heal_amount
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_global(self, event: LogEvent):
|
def on_global(self, event: LogEvent):
|
||||||
"""Process global event."""
|
"""Process global event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stats.globals_count += 1
|
self.stats.globals_count += 1
|
||||||
|
|
||||||
# Store personal global details
|
# Store personal global details
|
||||||
if event.event_type == 'personal_global':
|
if event.event_type == 'personal_global':
|
||||||
self.stats.personal_globals.append({
|
self.stats.personal_globals.append({
|
||||||
|
|
@ -1231,16 +1231,16 @@ class HuntingSessionTracker:
|
||||||
'creature': event.data.get('creature', 'Unknown'),
|
'creature': event.data.get('creature', 'Unknown'),
|
||||||
'value_ped': event.data.get('value_ped', Decimal('0.0'))
|
'value_ped': event.data.get('value_ped', Decimal('0.0'))
|
||||||
})
|
})
|
||||||
|
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_hof(self, event: LogEvent):
|
def on_hof(self, event: LogEvent):
|
||||||
"""Process Hall of Fame event."""
|
"""Process Hall of Fame event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stats.hofs_count += 1
|
self.stats.hofs_count += 1
|
||||||
|
|
||||||
# Store HoF details
|
# Store HoF details
|
||||||
if 'creature' in event.data:
|
if 'creature' in event.data:
|
||||||
self.stats.personal_globals.append({
|
self.stats.personal_globals.append({
|
||||||
|
|
@ -1249,53 +1249,53 @@ class HuntingSessionTracker:
|
||||||
'value_ped': event.data.get('value_ped', Decimal('0.0')),
|
'value_ped': event.data.get('value_ped', Decimal('0.0')),
|
||||||
'is_hof': True
|
'is_hof': True
|
||||||
})
|
})
|
||||||
|
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def on_decay(self, event: LogEvent):
|
def on_decay(self, event: LogEvent):
|
||||||
"""Process decay event."""
|
"""Process decay event."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Convert PEC to PED
|
# Convert PEC to PED
|
||||||
amount_pec = event.data.get('amount_pec', Decimal('0.0'))
|
amount_pec = event.data.get('amount_pec', Decimal('0.0'))
|
||||||
amount_ped = amount_pec / Decimal('100')
|
amount_ped = amount_pec / Decimal('100')
|
||||||
|
|
||||||
self.stats.weapon_cost_ped += amount_ped
|
self.stats.weapon_cost_ped += amount_ped
|
||||||
self.stats.total_cost_ped += amount_ped
|
self.stats.total_cost_ped += amount_ped
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def add_weapon_cost(self, cost_ped: Decimal):
|
def add_weapon_cost(self, cost_ped: Decimal):
|
||||||
"""Manually add weapon cost (for calculated decay)."""
|
"""Manually add weapon cost (for calculated decay)."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stats.weapon_cost_ped += cost_ped
|
self.stats.weapon_cost_ped += cost_ped
|
||||||
self.stats.total_cost_ped += cost_ped
|
self.stats.total_cost_ped += cost_ped
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def add_armor_cost(self, cost_ped: Decimal):
|
def add_armor_cost(self, cost_ped: Decimal):
|
||||||
"""Manually add armor cost."""
|
"""Manually add armor cost."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stats.armor_cost_ped += cost_ped
|
self.stats.armor_cost_ped += cost_ped
|
||||||
self.stats.total_cost_ped += cost_ped
|
self.stats.total_cost_ped += cost_ped
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def add_healing_cost(self, cost_ped: Decimal):
|
def add_healing_cost(self, cost_ped: Decimal):
|
||||||
"""Manually add healing cost."""
|
"""Manually add healing cost."""
|
||||||
if not self._session_active:
|
if not self._session_active:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stats.healing_cost_ped += cost_ped
|
self.stats.healing_cost_ped += cost_ped
|
||||||
self.stats.total_cost_ped += cost_ped
|
self.stats.total_cost_ped += cost_ped
|
||||||
self._notify_update()
|
self._notify_update()
|
||||||
|
|
||||||
def get_stats(self) -> HuntingSessionStats:
|
def get_stats(self) -> HuntingSessionStats:
|
||||||
"""Get current stats."""
|
"""Get current stats."""
|
||||||
return self.stats
|
return self.stats
|
||||||
|
|
||||||
def get_summary(self) -> Dict[str, Any]:
|
def get_summary(self) -> Dict[str, Any]:
|
||||||
"""Get session summary as dictionary."""
|
"""Get session summary as dictionary."""
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue