Skip to content

Instantly share code, notes, and snippets.

@shartte

shartte/protofixer.py Secret

Created Sep 11, 2019
Embed
What would you like to do?
import argparse
import re
import os.path
#
# Protofixer!
#
from typing import List
descriptions = dict()
# Take a space or vertical-tab separated list of flags and split them.
# Then clean the up and return them as a list.
def split_flags(value: str) -> List[str]:
return [x.strip() for x in re.split(r'\s', value) if len(x.strip()) > 0]
# Reconstruct the string for protos.tab for a list of flags
def join_flags(values: List[str]) -> str:
return " ".join(values)
def fix_monster_type_as_critter_flag(parts, flags, critter_flag, expected_monster_type=None, expected_monster_subtype=None):
proto_id = parts[0]
monster_type = None
if len(parts) > 163 and parts[163].strip() != '':
monster_type = parts[163]
monster_subtype = None
if len(parts) > 164 and parts[164].strip() != '':
monster_subtype = parts[164]
flag_removed = False
while critter_flag in flags:
flags.remove(critter_flag)
flag_removed = True
if flag_removed and expected_monster_type is not None and monster_type != expected_monster_type:
print(f"WARN: Proto {proto_id} is flagged as {critter_flag} (invalid), but has unrelated monster type {monster_type} (Expected {expected_monster_type})!")
if flag_removed and expected_monster_subtype is not None and monster_subtype != expected_monster_subtype:
print(f"WARN: Proto {proto_id} is flagged as {critter_flag} (invalid), but has unrelated monster subtype {monster_subtype} (Expected {expected_monster_subtype})!")
def fix_line(line):
parts = line.split('\t')
parts[-1] = parts[-1].rstrip('\n')
if len(parts) < 2:
return line
proto_id = int(parts[0])
proto_name = descriptions.get(proto_id, 'Unknown')
proto_desc = f'{proto_name} ({proto_id})'
obj_type = parts[1]
#
# Fix various broken item wear flags
#
if len(parts) > 61 and parts[61].strip() != '':
flags = split_flags(parts[61])
org_flags = flags.copy()
for flag in ["OIF_WEAR_WEAPON_PRIMARY", "OIF_WEAR_WEAPON_SECONDARY", "OIF_WEAR_RING_PRIMARY", "OIF_WEAR_RING_SECONDARY"]:
while flag in flags:
flags.remove(flag)
if flags != org_flags:
print(f"Proto {proto_desc}: Changing wear flags from {org_flags} to {flags}")
parts[61] = join_flags(flags)
## 99 is critter flags... some critter flags are used even though they dont exist
## interestingly they coincide with monster types and subtypes, so we add a check
## that prints a warning if it doesnt actually match up with the type
if len(parts) > 99 and parts[99].strip() != '':
flags = split_flags(parts[99])
org_flags = flags.copy()
fix_monster_type_as_critter_flag(parts, flags, 'OCF_ANIMAL', 'mc_type_animal')
fix_monster_type_as_critter_flag(parts, flags, 'OCF_UNDEAD', 'mc_type_undead')
fix_monster_type_as_critter_flag(parts, flags, 'OCF_PLANT', 'mc_type_plant')
fix_monster_type_as_critter_flag(parts, flags, 'OCF_AIR', None, 'mc_subtype_air')
fix_monster_type_as_critter_flag(parts, flags, 'OCF_EARTH', None, 'mc_subtype_earth')
fix_monster_type_as_critter_flag(parts, flags, 'OCF_FIRE', None, 'mc_subtype_fire')
fix_monster_type_as_critter_flag(parts, flags, 'OCF_WATER', None, 'mc_subtype_water')
if flags != org_flags:
print(f"Proto {proto_desc}: Changing critter flags from {org_flags} to {flags}")
parts[61] = join_flags(flags)
return '\t'.join(parts) + "\n"
def parse_mes(path):
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("input_file")
parser.add_argument("--descriptions", help='Path to description.mes', required=False)
args = parser.parse_args()
description_mes = args.descriptions
if description_mes is None:
description_mes = os.path.join(os.path.dirname(args.input_file), '../mes/description.mes')
if not os.path.isfile(description_mes):
print("If no description.mes is given, it must be at ../mes/description.mes relative to protos.tab. But couldn't find: " + description_mes)
print(f"Loading description.mes from {description_mes}")
with open(description_mes, 'rt') as fh:
for line in fh:
m = re.search(r'\{([^}]+)\}.*\{([^}]+)\}', line)
if m:
descriptions[int(m.group(1))] = m.group(2)
print(f"Loading protos.tab from {args.input_file}")
with open(args.input_file, 'rt') as fh:
lines = fh.readlines()
for i in range(0, len(lines)):
lines[i] = fix_line(lines[i])
#
# Write it back out
#
output_path = args.input_file + ".fixed"
print(f"Saving changes to {output_path}")
with open(output_path, 'wt') as fh:
fh.writelines(lines)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.