Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding script to parse additional timeless jewel passives #76

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
374 changes: 374 additions & 0 deletions PyPoE/cli/exporter/wiki/parsers/alternatePassives.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,374 @@
"""
Overview
===============================================================================

+----------+------------------------------------------------------------------+
| Path | PyPoE/cli/exporter/wiki/parsers/passives.py |
+----------+------------------------------------------------------------------+
| Version | 1.0.0a0 |
+----------+------------------------------------------------------------------+
| Revision | $Id$ |
+----------+------------------------------------------------------------------+
| Author | Omega_K2 |
+----------+------------------------------------------------------------------+

Description
===============================================================================



Agreement
===============================================================================

See PyPoE/LICENSE

Documentation
===============================================================================

Public API
-------------------------------------------------------------------------------

Internal API
-------------------------------------------------------------------------------
"""

# =============================================================================
# Imports
# =============================================================================

# Python
import re
import os.path
import warnings
from functools import partialmethod
from collections import OrderedDict

# 3rd-party

# self
from PyPoE.cli.core import console, Msg
from PyPoE.cli.exporter.wiki import parser
from PyPoE.cli.exporter.wiki.handler import ExporterHandler, ExporterResult
from PyPoE.poe.file.psg import PSGFile

# =============================================================================
# Globals
# =============================================================================

__all__ = []


# =============================================================================
# Classes
# =============================================================================


class WikiCondition(parser.WikiCondition):
COPY_KEYS = (
'main_page',
)

NAME = 'Passive skill'
ADD_INCLUDE = False
INDENT = 36


class AlternatePassiveSkillCommandHandler(ExporterHandler):
def __init__(self, sub_parser):
self.parser = sub_parser.add_parser(
'alt-passive',
help='Passive skill exporter',
)
self.parser.set_defaults(func=lambda args: self.parser.print_help())

self.add_default_subparser_filters(
sub_parser=self.parser.add_subparsers(),
cls=AlternatePassiveSkillParser,
)

# filtering
'''a_filter = sub.add_parser(
'filter',
help='Extract passives using filters.'
)
self.add_default_parsers(
parser=a_filter,
cls=PassiveSkillParser,
func=PassiveSkillParser.by_filter,
)

a_filter.add_argument(
'-ft-id', '--filter-id', '--filter-metadata-id',
help='Regular expression on the id',
type=str,
dest='re_id',
)'''

def add_default_parsers(self, *args, **kwargs):
super().add_default_parsers(*args, **kwargs)
self.add_format_argument(kwargs['parser'])
self.add_image_arguments(kwargs['parser'])
kwargs['parser'].add_argument(
'-ft-id', '--filter-id', '--filter-metadata-id',
help='Regular expression on the id',
type=str,
dest='re_id',
)


class AlternatePassiveSkillParser(parser.BaseParser):
_files = [
'AlternatePassiveSkills.dat',
]

_passive_column_index_filter = partialmethod(
parser.BaseParser._column_index_filter,
dat_file_name='AlternatePassiveSkills.dat',
error_msg='Several passives have not been found:\n%s',
)

_MAX_STAT_ID = 4

_COPY_KEYS = OrderedDict((
('Id', {
'template': 'id',
}),
('Name', {
'template': 'name',
}),
('FlavourText', {
'template': 'flavour_text',
'default': '',
}),
('AlternateTreeVersionsKey', {
'template': 'jewel_type',
'format': lambda value: [x for x in value][0],
'condition': lambda passive: passive['AlternateTreeVersionsKey']
}),
))

def _apply_filter(self, parsed_args, passives):
if parsed_args.re_id:
parsed_args.re_id = re.compile(parsed_args.re_id, flags=re.UNICODE)
else:
return passives

new = []

for passive in passives:
if parsed_args.re_id and not \
parsed_args.re_id.match(passive['Id']):
continue

new.append(passive)

return new

def by_rowid(self, parsed_args):
return self.export(
parsed_args,
self.rr['AlternatePassiveSkills.dat'][parsed_args.start:parsed_args.end],
)

def by_id(self, parsed_args):
return self.export(parsed_args, self._passive_column_index_filter(
column_id='Id', arg_list=parsed_args.id
))

def by_name(self, parsed_args):
return self.export(parsed_args, self._passive_column_index_filter(
column_id='Name', arg_list=parsed_args.name
))

def export(self, parsed_args, passives):
r = ExporterResult()

passives = self._apply_filter(parsed_args, passives)

console(f'Found {len(passives)} passives. Removing Royale passives...')
passives = [
passive for passive in passives
if not passive['Id'].startswith('royale')
]
console(f'{len(passives)} passives left for processing.')

if not passives:
console(
'No passives found for the specified parameters. Quitting.',
msg=Msg.warning,
)
return r

console('Accessing additional data...')

psg = PSGFile()
psg.read(
file_path_or_raw=self.file_system.get_file(
'Metadata/PassiveSkillGraph.psg'
),
)

node_index = {}
for group in psg.groups:
for node in group.nodes:
node_index[node.passive_skill] = node
# Connections are one-way, make them two way
for psg_id, node in node_index.items():
for other_psg_id in node.connections:
node_index[other_psg_id].connections.append(psg_id)

self._image_init(parsed_args)

console('Found %s, parsing...' % len(passives))

for passive in passives:
data = OrderedDict()

# Copy over simple fields from the .dat
for row_key, copy_data in self._COPY_KEYS.items():
value = passive[row_key]

condition = copy_data.get('condition')
if condition is not None and not condition(passive):
continue

# Skip default values to reduce size of template
if value == copy_data.get('default'):
continue

fmt = copy_data.get('format')
if fmt:
value = fmt(value)
data[copy_data['template']] = value

# Flag if it's an atlas skill
if passive['Id'].startswith('atlas'):
data['is_atlas_passive'] = True

for i in range(len(passive['PassiveType'])):
if passive['PassiveType'][i] == 4:
data['is_keystone'] = True
elif passive['PassiveType'][i] == 3:
data['is_notable'] = True

data['int_id'] = 0

# Handle icon paths
if passive['Icon_DDSFile']:
icon = passive['Icon_DDSFile'].split('/')
if passive['Icon_DDSFile'].startswith(
'Art/2DArt/SkillIcons/passives/'):
if icon[-2] == 'passives':
data['icon'] = icon[-1]
else:
data['icon'] = '%s (%s)' % (icon[-1], icon[-2])
else:
data['icon'] = icon[-1]
# atlas_start_node doesn't have an icon path
else:
data['icon'] = ''
warnings.warn(f"Icon path file not found for {passive['Id']}: {passive['Name']}")

data['icon'] = data['icon'].replace('.dds', '')

# Handle Stats
stat_ids = []
values = []

j = 0
for i in range(0, self._MAX_STAT_ID):
try:
stat = passive['StatsKeys'][i]
except IndexError:
break
j = i + 1
stat_ids.append(stat['Id'])
data['stat%s_id' % j] = stat['Id']
value = passive['Stat%sMin' % j], passive['Stat%sMax' % j]
values.append(value)
data['stat%s_min' % j] = passive['Stat%sMin' % j]
data['stat%s_max' % j] = passive['Stat%sMax' % j]

data['stat_text'] = '<br>'.join(self._get_stats(
stat_ids, values,
translation_file=get_translation_file(passive['Id'])
))

# For now this is being added to the stat text
# for ps_buff in passive['PassiveSkillBuffsKeys']:
# stat_ids = [stat['Id'] for stat in
# ps_buff['BuffDefinitionsKey']['StatsKeys']]
# values = ps_buff['Buff_StatValues']
# # if passive['Id'] == 'AscendancyChampion7':
# # index = stat_ids.index('damage_taken_+%_from_hits')
# # del stat_ids[index]
# # del values[index]
# for i, (sid, val) in enumerate(zip(stat_ids, values)):
# j += 1
# data['stat%s_id' % j] = sid
# data['stat%s_value' % j] = val
#
# text = '<br>'.join(self._get_stats(
# stat_ids, values,
# translation_file='passive_skill_aura_stat_descriptions.txt'
# ))
#
# if data['stat_text']:
# data['stat_text'] += '<br>' + text
# else:
# data['stat_text'] = text

# node = node_index.get(passive['PassiveSkillGraphId'])
# if node and node.connections:
# data['connections'] = ','.join([
# self.rr['AlternatePassiveSkills.dat'].index['PassiveSkillGraphId'][
# psg_id]['Id'] for psg_id in node.connections])

# extract icons if specified
if parsed_args.store_images and data['icon'] != '':
fn = data['icon'] + ' passive skill icon'
dds = os.path.join(self._img_path, fn + '.dds')
png = os.path.join(self._img_path, fn + '.png')
if not (os.path.exists(dds) or os.path.exists(png)):
self._write_dds(
data=self.file_system.get_file(passive['Icon_DDSFile']),
out_path=dds,
parsed_args=parsed_args,
)

cond = WikiCondition(
data=data,
cmdargs=parsed_args,
)

r.add_result(
text=cond,
out_file='passive_skill_%s.txt' % data['id'],
wiki_page=[
{
'page': 'Passive Skill:' + self._format_wiki_title(data['id']),
'condition': cond,
},
],
wiki_message='Passive skill updater',
)

return r


# =============================================================================
# Functions
# =============================================================================

def get_translation_file(passive_id: str):
'''
Determines which translation file should be used based on the passive skill ID.

Parameters
----------
passive_id: the Id of the passive skill
'''
if passive_id.startswith('atlas'):
return 'atlas_stat_descriptions.txt'
else:
return 'passive_skill_stat_descriptions.txt'
2 changes: 1 addition & 1 deletion PyPoE/poe/file/specification/data/stable.py
Original file line number Diff line number Diff line change
Expand Up @@ -956,7 +956,7 @@
type='ref|string',
),
Field(
name='DDSIcon',
name='Icon_DDSFile',
type='ref|string',
file_path=True,
file_ext='.dds',
Expand Down