Difference between revisions of "Ability/Script"
From Wildermyth Wiki
< Ability
| Line 9: | Line 9: | ||
import re | import re | ||
aspect_directory = 'assets/data/aspects' | |||
theme_directory = 'assets/data/themes' | |||
text_file = 'assets/text/aspects/aspects.properties' | text_file = 'assets/text/aspects/aspects.properties' | ||
# make dictionary of localized names | |||
aspectNames = {} | aspectNames = {} | ||
| Line 31: | Line 34: | ||
elif len(key_split) == 2 and key_split[-1] == 'blurb': | elif len(key_split) == 2 and key_split[-1] == 'blurb': | ||
aspectBlurbs[key_split[0]] = val[:-1] | aspectBlurbs[key_split[0]] = val[:-1] | ||
# read game version | |||
with open('version.txt','r',encoding='utf8') as f: | with open('version.txt','r',encoding='utf8') as f: | ||
lines = f.readlines() | lines = f.readlines() | ||
version = lines[0] | version = lines[0] | ||
# make list of abilities, by deck | |||
decks = {'common':[],'warrior':[],'hunter':[],'mystic':[],'upgrade':[]} | decks = {'common':[],'warrior':[],'hunter':[],'mystic':[],'upgrade':[]} | ||
aspectfiles = glob( | aspectfiles = glob(aspect_directory + '/*') | ||
for file in aspectfiles: | for file in aspectfiles: | ||
with open(file) as f: | with open(file) as f: | ||
| Line 46: | Line 53: | ||
if entry.get('info', {}).get('abilityDeckUsage') == deck: | if entry.get('info', {}).get('abilityDeckUsage') == deck: | ||
decks[deck] += [entry] | decks[deck] += [entry] | ||
# move upgrades to other deck, if they upgrade that deck's ability | |||
upgrades = [] | upgrades = [] | ||
| Line 65: | Line 74: | ||
decks['upgrade'] = upgrades | decks['upgrade'] = upgrades | ||
# sort ability decks by localized name | |||
for deck in decks: | for deck in decks: | ||
decks[deck] = sorted(decks[deck], key=lambda d: aspectNames[d['id']]) | decks[deck] = sorted(decks[deck], key=lambda d: aspectNames[d['id']]) | ||
# make dictionary of theme links | |||
''' | |||
themes = {} | |||
themefiles = glob(theme_directory + '/*') | |||
for file in themefiles: | |||
with open(file) as f: | |||
data = json.load(f) | |||
theme = data['id'] | |||
for piece in data['pieces']: | |||
'themePiece_' + theme | |||
for entry in data: | |||
for deck in decks: | |||
if entry.get('info', {}).get('abilityDeckUsage') == deck: | |||
decks[deck] += [entry] | |||
''' | |||
# wiki blurb for each deck | |||
deck_blurbs = { | deck_blurbs = { | ||
| Line 77: | Line 106: | ||
} | } | ||
# function to replace [tag]...[] or [tag]...[tag] | |||
# with new...new, while respecting nested brackets | |||
def replace_tag(tag,new,string): | def replace_tag(tag,new,string): | ||
| Line 96: | Line 126: | ||
break | break | ||
return string | return string | ||
# list of words to replace with links in the wiki | |||
linkwords = [ | linkwords = [ | ||
| Line 120: | Line 152: | ||
'grayplane', | 'grayplane', | ||
] | ] | ||
# function to turn game-formatted ability blurb into wiki-formatted ability blurb | |||
def parse(blurb): | def parse(blurb): | ||
out = blurb | out = blurb | ||
out = replace_tag('b',"'''",out) | out = replace_tag('b',"'''",out) # bold -> bold | ||
out = replace_tag('blue',"",out) | out = replace_tag('blue',"",out) # blue -> plain | ||
out = replace_tag('gray',"",out) | out = replace_tag('gray',"",out) # gray -> plain | ||
out = out.replace('<self>','Hero') | out = out.replace('<self>','Hero') | ||
out = out.replace('<name>','Hero') | out = out.replace('<name>','Hero') | ||
out = re.sub(r'\<int:(.*?)\>', 'x', out) | ### TODO: rewrite these to appropriately respect nested brackets and braces | ||
out = re.sub(r'\<float:(.*?)\>', 'x', out) | |||
out = re.sub(r'\<int:(.*?)\>', 'x', out) # formula -> x | |||
out = re.sub(r'\<float:(.*?)\>', 'x', out) # formula -> x | |||
out = re.sub(r'\<mf:.*?/.*?/(.*?)\>', '\g<1>', out) # use they/them | |||
# status effect -> link | |||
out = re.sub(r'\[:statusEffect\.(.*?)\](.*?)\[\]', '[[\g<1>|\g<2>]]', out) | out = re.sub(r'\[:statusEffect\.(.*?)\](.*?)\[\]', '[[\g<1>|\g<2>]]', out) | ||
out = re.sub(r'\[statusEffect:(.*?)\](.*?)\[\]', '[[\g<1>|\g<2>]]', out) | out = re.sub(r'\[statusEffect:(.*?)\](.*?)\[\]', '[[\g<1>|\g<2>]]', out) | ||
# assume any requirements are not met | |||
out = re.sub(r'\<self\..*?:.*?[^<]/(.*?)\>', '\g<1>', out) | out = re.sub(r'\<self\..*?:.*?[^<]/(.*?)\>', '\g<1>', out) | ||
out = re.sub(r'\<self\..*?:.*?\>', '', out) | out = re.sub(r'\<self\..*?:.*?\>', '', out) | ||
out = re.sub(r'\<test:.*?:(.*?[^<])/.*?\>', '\g<1>', out) | out = re.sub(r'\<test:.*?:(.*?[^<])/.*?\>', '\g<1>', out) | ||
out = re.sub(r'\<test:.*?:.*?\>', '', out) | out = re.sub(r'\<test:.*?:.*?\>', '', out) | ||
# leave out "(Currently ...)" note which is based on hero's current stats | |||
out = re.sub(r'\(Current.*?\)', '', out) | out = re.sub(r'\(Current.*?\)', '', out) | ||
### | |||
for word in linkwords: | out = out.replace('\\n','<br />') # newline -> newline | ||
for word in linkwords: # replace words with links | |||
out = out.replace(word,'[[%s]]'%word) | out = out.replace(word,'[[%s]]'%word) | ||
return out | return out | ||
# build output string | |||
string = '' | |||
for deck in decks: | for deck in decks: | ||
# add deck blurb | |||
string += deck_blurbs[deck] | string += deck_blurbs[deck] | ||
# add table headings | |||
if deck != 'upgrade': | if deck != 'upgrade': | ||
string += '\n{| class="wikitable"\n! Name\n! Description\n|-\n' | string += '\n{| class="wikitable"\n! Name\n! Description\n|-\n' | ||
else: | else: | ||
string += '\n{| class="wikitable sortable"\n! Name\n! | string += '\n{| class="wikitable sortable"\n! Name\n! Requirement\n! Description\n|-\n' | ||
# add ability lines | |||
for entry in decks[deck]: | for entry in decks[deck]: | ||
# add name | |||
name = aspectNames[entry['id']] | name = aspectNames[entry['id']] | ||
if name[-1] == '+': | if name[-1] == '+': | ||
nameStr = '[[' + name[:-1] + ']]+' | nameStr = '[[' + name[:-1] + ']]+' | ||
| Line 164: | Line 218: | ||
nameStr = '[[' + name + ']]' | nameStr = '[[' + name + ']]' | ||
nameStr = nameStr.replace('/',']]/[[') | nameStr = nameStr.replace('/',']]/[[') | ||
string += '| %s\n'%(nameStr) | |||
# add requirements | |||
if deck == 'upgrade': | if deck == 'upgrade': | ||
requirements = [] | requirements = [] | ||
themes = [] | |||
legs = {} | |||
arms = {} | |||
for req in entry.get('abilityRequiresOneAspectOf'): | for req in entry.get('abilityRequiresOneAspectOf'): | ||
requirements += [req] | |||
reqname = | |||
reqname = | # get associated info | ||
requirements += ['[[%s|%s]]'%( | theme = None | ||
string += '| %s\n'%(' | if req.startswith('themePiece_'): | ||
theme = 'theme_' + req.split('_')[1] | |||
if req.endswith('leftArm') or req.endswith('rightArm'): | |||
if arms.get(theme): arms[theme] += 1 | |||
else: arms[theme] = 1 | |||
if req.endswith('leftLeg') or req.endswith('rightLeg'): | |||
if legs.get(theme): legs[theme] += 1 | |||
else: legs[theme] = 1 | |||
elif req.startswith('theme_'): | |||
theme = req | |||
themes += [theme] | |||
# start requirement string | |||
reqStr = '' | |||
# only precede with theme if there is only one | |||
onlyTheme = None | |||
if len(set(themes)) == 1 and themes[0] is not None: | |||
reqStr += '[[%s]]'%aspectNames[themes[0]] | |||
onlyTheme = themes[0] | |||
# compress requirements | |||
reqs = [] | |||
for i,req in enumerate(requirements): | |||
# if requirement = theme, don't repeat it | |||
if onlyTheme is not None and aspectNames[req] == aspectNames[onlyTheme]: | |||
continue | |||
# combine 2 arms | |||
if (req.endswith('leftArm') or req.endswith('rightArm')) and arms[themes[i]] != 1: | |||
if arms.get(themes[i]) >= 2: | |||
reqname = re.sub(r'\(.*?\)', '(either)', aspectNames[req]) | |||
arms[themes[i]] = -1 | |||
elif arms.get(themes[i]) < 0: | |||
continue | |||
# combine 2 legs | |||
elif (req.endswith('leftLeg') or req.endswith('rightLeg')) and legs[themes[i]] != 1: | |||
if legs.get(themes[i]) >= 2: | |||
reqname = re.sub(r'\(.*?\)', '(either)', aspectNames[req]) | |||
legs[themes[i]] = -1 | |||
elif legs.get(themes[i]) < 0: | |||
continue | |||
else: | |||
reqname = aspectNames[req] | |||
# requirements don't need to link if the theme already will | |||
if onlyTheme is None: | |||
reqs += ['[[%s|%s]]'%(aspectNames[themes[i]],reqname)] | |||
else: | |||
reqs += ['%s'%(reqname)] | |||
# join requirements into our string | |||
if len(reqs) > 0: | |||
reqStr += ': ' | |||
reqStr += ' or '.join(reqs) | |||
string += '| %s\n'%(reqStr) | |||
# add blurb | |||
blurb = aspectBlurbs[entry['id']] | |||
string += '| %s\n|-\n'%parse(blurb) | string += '| %s\n|-\n'%parse(blurb) | ||
string += '|} | |||
# close table | |||
string += '|}\n' | |||
string += '<small>Last updated [[%s]] using [[/Script|this script]].</small>\n'%version[:-1] | string += '<small>Last updated [[%s]] using [[/Script|this script]].</small>\n'%version[:-1] | ||
# write the final string | |||
with open('abilityTable.txt', 'w') as f: | with open('abilityTable.txt', 'w') as f: | ||
f.write(string)</nowiki> | f.write(string)</nowiki> | ||
|} | |} | ||
Revision as of 08:23, 5 March 2022
This is a Python script to generate the Ability tables. Copy the code in the box below into a text file named abilityTable.py (or whatever you want) inside the Wildermyth directory. Then run it by calling python abilityTable.py from the command line. It will create a text file named abilityTable.txt with the appropriate wiki-formatted content.
| abilityTable.py |
import json
from glob import glob
import re
aspect_directory = 'assets/data/aspects'
theme_directory = 'assets/data/themes'
text_file = 'assets/text/aspects/aspects.properties'
# make dictionary of localized names
aspectNames = {}
aspectBlurbs = {}
with open(text_file,'r',encoding='utf8') as f:
lines = f.readlines()
for line in lines:
if line[0] == '#':
continue
try:
key,val = line.split('=')
except:
continue
try:
key_split = key.split('.')
except:
continue
if len(key_split) == 2 and key_split[-1] == 'name':
aspectNames[key_split[0]] = val[:-1]
elif len(key_split) == 2 and key_split[-1] == 'blurb':
aspectBlurbs[key_split[0]] = val[:-1]
# read game version
with open('version.txt','r',encoding='utf8') as f:
lines = f.readlines()
version = lines[0]
# make list of abilities, by deck
decks = {'common':[],'warrior':[],'hunter':[],'mystic':[],'upgrade':[]}
aspectfiles = glob(aspect_directory + '/*')
for file in aspectfiles:
with open(file) as f:
data = json.load(f)
for entry in data:
for deck in decks:
if entry.get('info', {}).get('abilityDeckUsage') == deck:
decks[deck] += [entry]
# move upgrades to other deck, if they upgrade that deck's ability
upgrades = []
for entry in decks['upgrade']:
upgradeFor = entry.get('info', {}).get('upgradeForAbility')
if upgradeFor is not None:
for deck in decks:
for base in decks[deck]:
if base['id'] == upgradeFor:
decks[deck] += [entry]
break
else:
continue
break
else:
upgrades += [entry]
else:
upgrades += [entry]
decks['upgrade'] = upgrades
# sort ability decks by localized name
for deck in decks:
decks[deck] = sorted(decks[deck], key=lambda d: aspectNames[d['id']])
# make dictionary of theme links
'''
themes = {}
themefiles = glob(theme_directory + '/*')
for file in themefiles:
with open(file) as f:
data = json.load(f)
theme = data['id']
for piece in data['pieces']:
'themePiece_' + theme
for entry in data:
for deck in decks:
if entry.get('info', {}).get('abilityDeckUsage') == deck:
decks[deck] += [entry]
'''
# wiki blurb for each deck
deck_blurbs = {
'common':'=== General Abilities ===\nThese abilities are available to all heroes.\n',
'warrior':'=== Warrior Abilities ===\nThese abilities are only available to [[Warrior]]s.\n',
'hunter':'=== Hunter Abilities ===\nThese abilities are only available to [[Hunter]]s.\n',
'mystic':'=== Mystic Abilities ===\nThese abilities are only available to [[Mystic]]s.\n',
'upgrade':'=== Theme Upgrades ===\nThese upgrades require specific [[theme]]s.',
}
# function to replace [tag]...[] or [tag]...[tag]
# with new...new, while respecting nested brackets
def replace_tag(tag,new,string):
while True:
start = string.find('[%s]'%tag)
if start < 0:
break
num = 1
for j in re.finditer(r'\[.*?\]',string[start+len(tag)+2:]):
tag_ = string[start+len(tag)+2+j.start():start+len(tag)+2+j.end()][1:-1]
if tag_ not in ['',tag]:
num += 1
else:
num -= 1
if num == 0:
end = start+len(tag)+2+j.end()
string = string[:start] + new + string[start+len(tag)+2:end-2-len(tag_)] + new + string[end:]
break
return string
# list of words to replace with links in the wiki
linkwords = [
'Bonus Damage',
'Potency',
'Stunt Chance',
'Temp Health',
'Temporary Health',
'Armor',
'Warding',
'Dodge',
'Block',
'Speed',
'Recovery Rate',
'Retirement Age',
'Grayplane',
'bonus damage',
'spell damage',
'armor',
'warding',
'dodge',
'block',
'augment',
'grayplane',
]
# function to turn game-formatted ability blurb into wiki-formatted ability blurb
def parse(blurb):
out = blurb
out = replace_tag('b',"'''",out) # bold -> bold
out = replace_tag('blue',"",out) # blue -> plain
out = replace_tag('gray',"",out) # gray -> plain
out = out.replace('<self>','Hero')
out = out.replace('<name>','Hero')
### TODO: rewrite these to appropriately respect nested brackets and braces
out = re.sub(r'\<int:(.*?)\>', 'x', out) # formula -> x
out = re.sub(r'\<float:(.*?)\>', 'x', out) # formula -> x
out = re.sub(r'\<mf:.*?/.*?/(.*?)\>', '\g<1>', out) # use they/them
# status effect -> link
out = re.sub(r'\[:statusEffect\.(.*?)\](.*?)\[\]', '[[\g<1>|\g<2>]]', out)
out = re.sub(r'\[statusEffect:(.*?)\](.*?)\[\]', '[[\g<1>|\g<2>]]', out)
# assume any requirements are not met
out = re.sub(r'\<self\..*?:.*?[^<]/(.*?)\>', '\g<1>', out)
out = re.sub(r'\<self\..*?:.*?\>', '', out)
out = re.sub(r'\<test:.*?:(.*?[^<])/.*?\>', '\g<1>', out)
out = re.sub(r'\<test:.*?:.*?\>', '', out)
# leave out "(Currently ...)" note which is based on hero's current stats
out = re.sub(r'\(Current.*?\)', '', out)
###
out = out.replace('\\n','<br />') # newline -> newline
for word in linkwords: # replace words with links
out = out.replace(word,'[[%s]]'%word)
return out
# build output string
string = ''
for deck in decks:
# add deck blurb
string += deck_blurbs[deck]
# add table headings
if deck != 'upgrade':
string += '\n{| class="wikitable"\n! Name\n! Description\n|-\n'
else:
string += '\n{| class="wikitable sortable"\n! Name\n! Requirement\n! Description\n|-\n'
# add ability lines
for entry in decks[deck]:
# add name
name = aspectNames[entry['id']]
if name[-1] == '+':
nameStr = '[[' + name[:-1] + ']]+'
else:
nameStr = '[[' + name + ']]'
nameStr = nameStr.replace('/',']]/[[')
string += '| %s\n'%(nameStr)
# add requirements
if deck == 'upgrade':
requirements = []
themes = []
legs = {}
arms = {}
for req in entry.get('abilityRequiresOneAspectOf'):
requirements += [req]
# get associated info
theme = None
if req.startswith('themePiece_'):
theme = 'theme_' + req.split('_')[1]
if req.endswith('leftArm') or req.endswith('rightArm'):
if arms.get(theme): arms[theme] += 1
else: arms[theme] = 1
if req.endswith('leftLeg') or req.endswith('rightLeg'):
if legs.get(theme): legs[theme] += 1
else: legs[theme] = 1
elif req.startswith('theme_'):
theme = req
themes += [theme]
# start requirement string
reqStr = ''
# only precede with theme if there is only one
onlyTheme = None
if len(set(themes)) == 1 and themes[0] is not None:
reqStr += '[[%s]]'%aspectNames[themes[0]]
onlyTheme = themes[0]
# compress requirements
reqs = []
for i,req in enumerate(requirements):
# if requirement = theme, don't repeat it
if onlyTheme is not None and aspectNames[req] == aspectNames[onlyTheme]:
continue
# combine 2 arms
if (req.endswith('leftArm') or req.endswith('rightArm')) and arms[themes[i]] != 1:
if arms.get(themes[i]) >= 2:
reqname = re.sub(r'\(.*?\)', '(either)', aspectNames[req])
arms[themes[i]] = -1
elif arms.get(themes[i]) < 0:
continue
# combine 2 legs
elif (req.endswith('leftLeg') or req.endswith('rightLeg')) and legs[themes[i]] != 1:
if legs.get(themes[i]) >= 2:
reqname = re.sub(r'\(.*?\)', '(either)', aspectNames[req])
legs[themes[i]] = -1
elif legs.get(themes[i]) < 0:
continue
else:
reqname = aspectNames[req]
# requirements don't need to link if the theme already will
if onlyTheme is None:
reqs += ['[[%s|%s]]'%(aspectNames[themes[i]],reqname)]
else:
reqs += ['%s'%(reqname)]
# join requirements into our string
if len(reqs) > 0:
reqStr += ': '
reqStr += ' or '.join(reqs)
string += '| %s\n'%(reqStr)
# add blurb
blurb = aspectBlurbs[entry['id']]
string += '| %s\n|-\n'%parse(blurb)
# close table
string += '|}\n'
string += '<small>Last updated [[%s]] using [[/Script|this script]].</small>\n'%version[:-1]
# write the final string
with open('abilityTable.txt', 'w') as f:
f.write(string)
|