def parse_attributes(): logging.debug('Parsing attributes...') ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies_ability.ipf', 'ability.ies') with open(ies_path, 'rb') as ies_file: for row in csv.DictReader(ies_file, delimiter=',', quotechar='"'): obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Description'] = parser_translations.translate(row['Desc']).strip() + '{nl}' obj['Icon'] = parser_assets.parse_entity_icon(row['Icon']) obj['Name'] = parser_translations.translate(row['Name']) obj['IsToggleable'] = row['AlwaysActive'] == 'NO' obj['DescriptionRequired'] = None obj['LevelMax'] = -1 obj['Unlock'] = None obj['UnlockArgs'] = {} obj['UpgradePrice'] = [] obj['Link_Jobs'] = [] obj['Link_Skills'] = [skill for skill in row['SkillCategory'].split(';') if len(skill)] globals.attributes[obj['$ID']] = obj globals.attributes_by_name[obj['$ID_NAME']] = obj
def parse_maps(): logging.debug('Parsing Maps...') ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', 'map.ies') with open(ies_path, 'rb') as ies_file: for row in csv.DictReader(ies_file, delimiter=',', quotechar='"'): obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Icon'] = None obj['Name'] = parser_translations.translate(row['Name']) obj['HasChallengeMode'] = row['ChallengeMode'] == 'YES' obj['HasWarp'] = int(row['WarpCost']) > 0 obj['Level'] = int(row['QuestLevel']) obj['Prop_EliteMonsterCapacity'] = int(row['EliteMonsterCapacity']) obj['Prop_MaxHateCount'] = int(row['MaxHateCount']) obj['Prop_RewardEXPBM'] = float(row['MaxHateCount']) obj['Stars'] = int(row['MapRank']) obj['Type'] = TOSMapType.value_of(row['MapType']) obj['Warp'] = int(row['WarpCost']) obj['WorldMap'] = [int(coord) for coord in row['WorldMap'].split('/')] if row['WorldMap'] else None obj['Link_Items'] = [] obj['Link_Items_Exploration'] = [] obj['Link_Maps'] = [] obj['Link_Maps_Floors'] = [] obj['Link_NPCs'] = [] globals.maps[obj['$ID']] = obj globals.maps_by_name[obj['$ID_NAME']] = obj globals.maps_by_position['-'.join(row['WorldMap'].split('/')) if obj['WorldMap'] else ''] = obj
def parse(): parse_items('item.ies') parse_items('item_colorspray.ies') parse_items('item_gem.ies') parse_items('item_equip.ies') parse_items('item_equip_ep12.ies') parse_items('item_premium.ies') parse_items('item_quest.ies') parse_items('recipe.ies') # Hotfix: Insert 'Silver' as an Item obj = {} obj['$ID'] = -1 obj['$ID_NAME'] = 'Moneybag1' obj['Icon'] = parser_assets.parse_entity_icon('icon_item_silver') obj['Name'] = parser_translations.translate(u'실버') obj['Tradability'] = 'FFFF' obj['Type'] = TOSItemGroup.UNUSED for key in globals.items.values()[0]: if key not in obj: obj[key] = None globals.items[obj['$ID']] = obj globals.items_by_name[obj['$ID_NAME']] = obj
def parse_equipment_sets(file_name): logging.debug('Parsing equipment sets...') ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', file_name) ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Name'] = parser_translations.translate( row['Name']) if 'Name' in row else None obj['Link_Items'] = [] # Parse bonus obj['Bonus2'] = parser_translations.translate( row['EffectDesc_2']) if row['EffectDesc_2'] != '' else None obj['Bonus3'] = parser_translations.translate( row['EffectDesc_3']) if row['EffectDesc_3'] != '' else None obj['Bonus4'] = parser_translations.translate( row['EffectDesc_4']) if row['EffectDesc_4'] != '' else None obj['Bonus5'] = parser_translations.translate( row['EffectDesc_5']) if row['EffectDesc_5'] != '' else None obj['Bonus6'] = parser_translations.translate( row['EffectDesc_6']) if row['EffectDesc_6'] != '' else None obj['Bonus7'] = parser_translations.translate( row['EffectDesc_7']) if row['EffectDesc_7'] != '' else None globals.equipment_sets[obj['$ID']] = obj globals.equipment_sets_by_name[obj['$ID_NAME']] = obj
def parse_jobs(): logging.debug('Parsing Jobs...') ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', 'job.ies') with open(ies_path, 'rb') as ies_file: for row in csv.DictReader(ies_file, delimiter=',', quotechar='"'): obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Description'] = parser_translations.translate(row['Caption1']) obj['Icon'] = parser_assets.parse_entity_icon(row['Icon']) obj['Name'] = parser_translations.translate(row['Name']) obj['CircleMax'] = int(row['MaxCircle']) obj['JobDifficulty'] = TOSJobDifficulty.value_of( row['ControlDifficulty']) obj['JobTree'] = TOSJobTree.value_of(row['CtrlType']) obj['JobType'] = [ TOSJobType.value_of(v.strip()) for v in row['ControlType'].split(',') ] if len(row['ControlType']) else None obj['IsHidden'] = row['HiddenJob'] == 'YES' obj['IsSecret'] = obj['IsHidden'] and len(row['PreFunction']) > 0 obj['IsStarter'] = int(row['Rank']) == 1 obj['Rank'] = int(row['Rank']) obj['Stat_CON'] = int(row['CON']) obj['Stat_DEX'] = int(row['DEX']) obj['Stat_INT'] = int(row['INT']) obj['Stat_SPR'] = int(row['MNA']) obj['Stat_STR'] = int(row['STR']) obj['StatBase_CON'] = 0 obj['StatBase_DEX'] = 0 obj['StatBase_INT'] = 0 obj['StatBase_SPR'] = 0 obj['StatBase_STR'] = 0 obj['Link_Attributes'] = [] obj['Link_Skills'] = [] globals.jobs[obj['$ID']] = obj globals.jobs_by_name[obj['$ID_NAME']] = obj
def parse_books_dialog(): logging.debug('Parsing books dialog...') ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies_client.ipf', 'dialogtext.ies') ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: if row['ClassName'] not in globals.books_by_name: continue book = globals.books_by_name[row['ClassName']] book['Text'] = parser_translations.translate(row['Text']) ies_file.close()
def parse_gems_bonus(is_rebuild): logging.debug('Parsing gems bonus...') xml_path = os.path.join(constants.PATH_INPUT_DATA, 'xml.ipf', 'socket_property.xml') xml = ET.parse(xml_path).getroot() SLOTS = ['TopLeg', 'HandOrFoot', 'MainOrSubWeapon'] if is_rebuild else\ ['TopLeg', 'Foot', 'Hand', 'Weapon', 'SubWeapon'] # example: <Item Name="gem_circle_1"> for item in xml: gem = globals.gems_by_name[item.get('Name')] for level in item: if level.get('Level') == '0': continue for slot in SLOTS: bonus = level.get('PropList_' + slot) penalty = level.get('PropList_' + slot + '_Penalty') for slot in (slot.split('Or') if 'Or' in slot else [slot]): # support for Re:Build 2-in-1 slots for prop in [bonus, penalty]: if prop is not None and prop != 'None': if gem['TypeGem'] == TOSGemType.SKILL: gem['Bonus' + parse_gems_slot(slot)].append({ 'Stat': parser_translations.translate( prop).replace('OptDesc/', '') }) elif gem['TypeGem'] == TOSGemType.STATS: prop_slot = prop.split('/') stat = TOSEquipmentStat.value_of('ADD_' + prop_slot[0]) stat = TOSEquipmentStat.value_of( prop_slot[0]) if stat is None else stat gem['Bonus' + parse_gems_slot(slot)].append({ 'Stat': stat, 'Value': int(prop_slot[1]) })
def parse_maps(): logging.debug('Parsing maps...') ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', 'map.ies') ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Icon'] = '' obj['Name'] = parser_translations.translate(row['Name']) # TODO: parse remaining properties globals.maps[obj['$ID']] = obj globals.maps_by_name[obj['$ID_NAME']] = obj ies_file.close()
def parse_monsters(file_name): logging.debug('Parsing %s...', file_name) LUA = luautil.load_script('calc_property_monster.lua', [ 'GET_MON_STAT', 'SCR_Get_MON_STR', 'SCR_Get_MON_INT', 'SCR_Get_MON_CON', 'SCR_Get_MON_MNA', 'SCR_Get_MON_DEX', 'SCR_Get_MON_MHP', 'SCR_Get_MON_MSP', 'SCR_GET_MON_EXP', 'SCR_GET_MON_JOBEXP', 'SCR_Get_MON_DEF', 'SCR_Get_MON_MDEF', 'SCR_Get_MON_HR', 'SCR_Get_MON_DR', 'SCR_Get_MON_CRTHR', 'SCR_Get_MON_CRTDR', 'SCR_Get_MON_CRTATK', 'SCR_Get_MON_MINPATK', 'SCR_Get_MON_MAXPATK', 'SCR_Get_MON_MINMATK', 'SCR_Get_MON_MAXMATK', 'SCR_Get_MON_BLK', 'SCR_Get_MON_BLK_BREAK', 'SCR_MON_ITEM_ARMOR_CALC', 'SCR_MON_ITEM_ARMOR_DEF_CALC', 'SCR_MON_ITEM_ARMOR_MDEF_CALC', 'SCR_MON_ITEM_WEAPON_CALC', 'SCR_MON_ITEM_GRADE_RATE', 'SCR_MON_ITEM_REINFORCE_ARMOR_CALC', 'SCR_MON_ITEM_REINFORCE_WEAPON_CALC', 'SCR_MON_ITEM_TRANSCEND_CALC', 'SCR_RACE_TYPE_RATE', 'SCR_SIZE_TYPE_RATE', ]) ies_path = os.path.join(constants.PATH_INPUT_DATA, "ies.ipf", file_name.lower()) ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: if row['MonRank'].upper() not in MONSTER_RANK_WHITELIST: continue #logging.debug('Parsing monster: %s :: %s', row['ClassID'], row['ClassName']) # HotFix: these properties need to be calculated before the remaining ones row['Lv'] = int(row['Level']) if int(row['Level']) > 1 else 1 row['CON'] = LUA['SCR_Get_MON_CON'](row) row['DEX'] = LUA['SCR_Get_MON_DEX'](row) row['INT'] = LUA['SCR_Get_MON_INT'](row) row['MNA'] = LUA['SCR_Get_MON_MNA'](row) row['STR'] = LUA['SCR_Get_MON_STR'](row) obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Description'] = parser_translations.translate(row['Desc']) obj['Icon'] = parser_assets.parse_entity_icon(row['Icon']) obj['Name'] = parser_translations.translate(row['Name']) obj['Armor'] = TOSEquipmentMaterial.value_of(row['ArmorMaterial']) obj['Element'] = TOSElement.value_of(row['Attribute']) obj['Level'] = int(row['Lv']) obj['Race'] = TOSMonsterRace.value_of(row['RaceType']) obj['Rank'] = TOSMonsterRank.value_of(row['MonRank']) obj['Size'] = TOSMonsterSize.value_of(row['Size']) obj['EXP'] = int(LUA['SCR_GET_MON_EXP'](row)) if obj['Level'] < 999 else 0 obj['EXPClass'] = int(LUA['SCR_GET_MON_JOBEXP'](row)) if obj['Level'] < 999 else 0 obj['Stat_CON'] = int(row['CON']) obj['Stat_DEX'] = int(row['DEX']) obj['Stat_INT'] = int(row['INT']) obj['Stat_SPR'] = int(row['MNA']) obj['Stat_STR'] = int(row['STR']) obj['Stat_HP'] = int(LUA['SCR_Get_MON_MHP'](row)) obj['Stat_SP'] = int(LUA['SCR_Get_MON_MSP'](row)) obj['Stat_ATTACK_MAGICAL_MAX'] = int(LUA['SCR_Get_MON_MAXMATK'](row)) obj['Stat_ATTACK_MAGICAL_MIN'] = int(LUA['SCR_Get_MON_MINMATK'](row)) obj['Stat_ATTACK_PHYSICAL_MAX'] = int(LUA['SCR_Get_MON_MAXPATK'](row)) obj['Stat_ATTACK_PHYSICAL_MIN'] = int(LUA['SCR_Get_MON_MINPATK'](row)) obj['Stat_DEFENSE_MAGICAL'] = int(LUA['SCR_Get_MON_MDEF'](row)) obj['Stat_DEFENSE_PHYSICAL'] = int(LUA['SCR_Get_MON_DEF'](row)) obj['Stat_Accuracy'] = int(LUA['SCR_Get_MON_HR'](row)) obj['Stat_Evasion'] = int(LUA['SCR_Get_MON_DR'](row)) obj['Stat_CriticalDamage'] = int(LUA['SCR_Get_MON_CRTATK'](row)) obj['Stat_CriticalDefense'] = int(LUA['SCR_Get_MON_CRTDR'](row)) obj['Stat_CriticalRate'] = int(LUA['SCR_Get_MON_CRTHR'](row)) obj['Stat_BlockRate'] = int(LUA['SCR_Get_MON_BLK'](row)) obj['Stat_BlockPenetration'] = int(LUA['SCR_Get_MON_BLK_BREAK'](row)) obj['Link_Drops'] = [] obj['Link_Spawns'] = [] globals.monsters[obj['$ID']] = obj globals.monsters_by_name[obj['$ID_NAME']] = obj ies_file.close()
def parse_skills(is_rebuild): logging.debug('Parsing skills...') LUA = luautil.load_script('calc_property_skill.lua', '*', False) LUA_EMBEDDED = [ 'SCR_ABIL_ADD_SKILLFACTOR', 'SCR_ABIL_ADD_SKILLFACTOR_TOOLTIP', 'SCR_Get_SpendSP', 'SCR_REINFORCEABILITY_TOOLTIP', ] ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', 'skill.ies') with open(ies_path, 'rb') as ies_file: for row in csv.DictReader(ies_file, delimiter=',', quotechar='"'): # Ignore 'Common_' skills (e.g. Bokor's Summon abilities) if row['ClassName'].find('Common_') == 0: continue obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Description'] = parser_translations.translate(row['Caption']) obj['Icon'] = parser_assets.parse_entity_icon(row['Icon']) obj['Name'] = parser_translations.translate(row['Name']) obj['Effect'] = parser_translations.translate(row['Caption2']) obj['Element'] = TOSElement.value_of(row['Attribute']) obj['IsShinobi'] = row[ 'CoolDown'] == 'SCR_GET_SKL_COOLDOWN_BUNSIN' or ( row['CoolDown'] and 'Bunshin_Debuff' in LUA[row['CoolDown']]) obj['OverHeat'] = { 'Value': int(row['SklUseOverHeat']), 'Group': row['OverHeatGroup'] } if not is_rebuild else int( row['SklUseOverHeat'] ) # Re:Build overheat is now simpler to calculate obj['Prop_BasicCoolDown'] = int(row['BasicCoolDown']) obj['Prop_BasicPoison'] = int(row['BasicPoison']) obj['Prop_BasicSP'] = int(math.floor(float(row['BasicSP']))) obj['Prop_LvUpSpendPoison'] = int(row['LvUpSpendPoison']) obj['Prop_LvUpSpendSp'] = float(row['LvUpSpendSp']) obj['Prop_SklAtkAdd'] = float(row['SklAtkAdd']) obj['Prop_SklAtkAddByLevel'] = float(row['SklAtkAddByLevel']) obj['Prop_SklFactor'] = float(row['SklFactor']) obj['Prop_SklFactorByLevel'] = float(row['SklFactorByLevel']) obj['Prop_SklSR'] = float(row['SklSR']) obj['Prop_SpendItemBaseCount'] = int(row['SpendItemBaseCount']) obj['RequiredStance'] = row['ReqStance'] obj['RequiredStanceCompanion'] = TOSRequiredStanceCompanion.value_of( row['EnableCompanion']) obj['RequiredSubWeapon'] = row['UseSubweaponDamage'] == 'YES' obj['CoolDown'] = None obj['IsEnchanter'] = False obj['IsPardoner'] = False obj['IsRunecaster'] = False obj['Prop_LevelPerGrade'] = -1 # Remove when Re:Build goes global obj['Prop_MaxLevel'] = -1 obj['Prop_UnlockGrade'] = -1 # Remove when Re:Build goes global obj['Prop_UnlockClassLevel'] = -1 obj['SP'] = None obj['TypeAttack'] = [] obj['Link_Attributes'] = [] obj['Link_Gem'] = None obj['Link_Job'] = None # Parse TypeAttack if row['ValueType'] == 'Buff': obj['TypeAttack'].append(TOSAttackType.BUFF) if row['ClassType'] is not None: obj['TypeAttack'].append( TOSAttackType.value_of(row['ClassType'])) if row['AttackType'] is not None: obj['TypeAttack'].append( TOSAttackType.value_of(row['AttackType'])) obj['TypeAttack'] = list(set(obj['TypeAttack'])) obj['TypeAttack'] = [ attack for attack in obj['TypeAttack'] if attack is not None and attack != TOSAttackType.UNKNOWN ] # Add missing Description header if not re.match(r'{#.+}{ol}(\[.+?\]){\/}{\/}{nl}', obj['Description']): header = [ '[' + TOSAttackType.to_string(attack) + ']' for attack in obj['TypeAttack'] ] header_color = '' header_color = '993399' if TOSAttackType.MAGIC in obj[ 'TypeAttack'] else header_color header_color = 'DD5500' if TOSAttackType.MELEE in obj[ 'TypeAttack'] else header_color header_color = 'DD5500' if TOSAttackType.MISSILE in obj[ 'TypeAttack'] else header_color if obj['Element'] != TOSElement.MELEE: header.append('[' + TOSElement.to_string(obj['Element']) + ']') obj['Description'] = '{#' + header_color + '}{ol}' + ' - '.join( header) + '{/}{/}{nl}' + obj['Description'] # Parse effects for effect in re.findall(r'{(.*?)}', obj['Effect']): if effect in EFFECT_DEPRECATE: # Hotfix: sometimes IMC changes which effects are used, however they forgot to properly communicate to the translation team. # This code is responsible for fixing that and warning so the in-game translations can be fixed logging.warning('[%32s] Deprecated effect [%s] in Effect', obj['$ID_NAME'], effect) effect_deprecate = effect effect = EFFECT_DEPRECATE[effect] obj['Effect'] = re.sub( r'\b' + re.escape(effect_deprecate) + r'\b', effect, obj['Effect']) if effect in row: key = 'Effect_' + effect # HotFix: make sure all skills have the same Effect columns (1/2) if key not in EFFECTS: EFFECTS.append('Effect_' + effect) if row[effect] != 'ZERO': obj[key] = parse_skills_lua_source( LUA, LUA_EMBEDDED, row[effect]) obj[key] = parse_skills_lua_source_to_javascript( row, obj[key]) else: # Hotfix: similar to the hotfix above logging.warning( '[%32s] Deprecated effect [%s] in Effect', obj['$ID_NAME'], effect) obj[key] = None else: continue # Parse formulas if row['CoolDown']: obj['CoolDown'] = parse_skills_lua_source( LUA, LUA_EMBEDDED, row['CoolDown']) obj['CoolDown'] = parse_skills_lua_source_to_javascript( row, obj['CoolDown']) if row['SpendSP']: obj['SP'] = parse_skills_lua_source(LUA, LUA_EMBEDDED, row['SpendSP']) obj['SP'] = parse_skills_lua_source_to_javascript( row, obj['SP']) globals.skills[obj['$ID']] = obj globals.skills_by_name[obj['$ID_NAME']] = obj # HotFix: make sure all skills have the same Effect columns (2/2) for skill in globals.skills.values(): for effect in EFFECTS: if effect not in skill: skill[effect] = None
def parse_monsters(file_name): logging.debug('Parsing %s...', file_name) LUA_RUNTIME = luautil.LUA_RUNTIME LUA_SOURCE = luautil.LUA_SOURCE ies_path = os.path.join(constants.PATH_INPUT_DATA, "ies.ipf", file_name.lower()) ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: #logging.debug('Parsing monster: %s :: %s', row['ClassID'], row['ClassName']) # HotFix: these properties need to be calculated before the remaining ones row['Lv'] = int(row['Level']) if int(row['Level']) > 1 else 1 row['CON'] = LUA_RUNTIME['SCR_Get_MON_CON'](row) row['DEX'] = LUA_RUNTIME['SCR_Get_MON_DEX'](row) row['INT'] = LUA_RUNTIME['SCR_Get_MON_INT'](row) row['MNA'] = LUA_RUNTIME['SCR_Get_MON_MNA'](row) row['STR'] = LUA_RUNTIME['SCR_Get_MON_STR'](row) obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Description'] = parser_translations.translate(row['Desc']) obj['Icon'] = parser_assets.parse_entity_icon( row['Icon']) if row['Icon'] != 'ui_CreateMonster' else None obj['Name'] = parser_translations.translate(row['Name']) obj['Type'] = TOSMonsterType.value_of(row['GroupName']) if obj['Type'] == TOSMonsterType.MONSTER: obj['Armor'] = TOSEquipmentMaterial.value_of(row['ArmorMaterial']) obj['Element'] = TOSElement.value_of(row['Attribute']) obj['Level'] = int(row['Lv']) obj['Race'] = TOSMonsterRace.value_of(row['RaceType']) obj['Rank'] = TOSMonsterRank.value_of(row['MonRank']) obj['Size'] = TOSMonsterSize.value_of( row['Size']) if row['Size'] else None obj['EXP'] = int(LUA_RUNTIME['SCR_GET_MON_EXP']( row)) if obj['Level'] < 999 else 0 obj['EXPClass'] = int(LUA_RUNTIME['SCR_GET_MON_JOBEXP']( row)) if obj['Level'] < 999 else 0 obj['Stat_CON'] = int(row['CON']) obj['Stat_DEX'] = int(row['DEX']) obj['Stat_INT'] = int(row['INT']) obj['Stat_SPR'] = int(row['MNA']) obj['Stat_STR'] = int(row['STR']) obj['Stat_HP'] = int(LUA_RUNTIME['SCR_Get_MON_MHP'](row)) obj['Stat_SP'] = int(LUA_RUNTIME['SCR_Get_MON_MSP'](row)) obj['Stat_ATTACK_MAGICAL_MAX'] = int( LUA_RUNTIME['SCR_Get_MON_MAXMATK'](row)) obj['Stat_ATTACK_MAGICAL_MIN'] = int( LUA_RUNTIME['SCR_Get_MON_MINMATK'](row)) obj['Stat_ATTACK_PHYSICAL_MAX'] = int( LUA_RUNTIME['SCR_Get_MON_MAXPATK'](row)) obj['Stat_ATTACK_PHYSICAL_MIN'] = int( LUA_RUNTIME['SCR_Get_MON_MINPATK'](row)) obj['Stat_DEFENSE_MAGICAL'] = int( LUA_RUNTIME['SCR_Get_MON_MDEF'](row)) obj['Stat_DEFENSE_PHYSICAL'] = int( LUA_RUNTIME['SCR_Get_MON_DEF'](row)) obj['Stat_Accuracy'] = int(LUA_RUNTIME['SCR_Get_MON_HR'](row)) obj['Stat_Evasion'] = int(LUA_RUNTIME['SCR_Get_MON_DR'](row)) obj['Stat_CriticalDamage'] = int( LUA_RUNTIME['SCR_Get_MON_CRTATK'](row)) obj['Stat_CriticalDefense'] = int( LUA_RUNTIME['SCR_Get_MON_CRTDR'](row)) obj['Stat_CriticalRate'] = int( LUA_RUNTIME['SCR_Get_MON_CRTHR'](row)) obj['Stat_BlockRate'] = int(LUA_RUNTIME['SCR_Get_MON_BLK'](row)) obj['Stat_BlockPenetration'] = int( LUA_RUNTIME['SCR_Get_MON_BLK_BREAK'](row)) obj['Link_Items'] = [] obj['Link_Maps'] = [] globals.monsters[obj['$ID']] = obj globals.monsters_by_name[obj['$ID_NAME']] = obj elif obj['Type'] == TOSMonsterType.NPC: obj['Icon'] = parser_assets.parse_entity_icon( row['MinimapIcon']) if row['MinimapIcon'] else obj['Icon'] globals.npcs[obj['$ID']] = obj globals.npcs_by_name[obj['$ID_NAME']] = obj ies_file.close()
def parse_items(file_name): logging.debug('Parsing %s...', file_name) ies_path = os.path.join(constants.PATH_INPUT_DATA, "ies.ipf", file_name) ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: item_type = TOSItemGroup.RECIPE if file_name == 'recipe.ies' else TOSItemGroup.value_of( row['GroupName']) item_type_equipment = TOSEquipmentType.value_of( row['ClassType']) if 'ClassType' in row else None #logging.debug('Parsing item: %s :: %s', row['ClassID'], row['ClassName']) obj = {} obj['$ID'] = int(row['ClassID']) obj['$ID_NAME'] = row['ClassName'] obj['Description'] = parser_translations.translate( row['Desc']) if 'Desc' in row else None obj['Icon'] = parser_assets.parse_entity_icon(row['Icon']) obj['Name'] = parser_translations.translate( row['Name']) if 'Name' in row else None obj['Price'] = row['SellPrice'] obj['TimeCoolDown'] = float(int(row['ItemCoolDown']) / 1000) if 'ItemCoolDown' in row else None obj['TimeLifeTime'] = float(int( row['LifeTime'])) if 'LifeTime' in row else None obj['Tradability'] = '%s%s%s%s' % ( 'T' if row['MarketTrade'] == 'YES' else 'F', # Market 'T' if row['UserTrade'] == 'YES' else 'F', # Players 'T' if row['ShopTrade'] == 'YES' else 'F', # Shop 'T' if row['TeamTrade'] == 'YES' else 'F', # Team Storage ) obj['Type'] = item_type obj['Weight'] = float(row['Weight']) if 'Weight' in row else None obj['Link_Collections'] = [] obj['Link_Cubes'] = [] obj['Link_MonsterDrops'] = [] obj['Link_RecipeTarget'] = [] obj['Link_RecipeMaterial'] = [] if item_type == TOSItemGroup.BOOK: globals.books[obj['$ID']] = obj globals.books_by_name[obj['$ID_NAME']] = obj elif item_type == TOSItemGroup.CARD: globals.cards[obj['$ID']] = obj globals.cards_by_name[obj['$ID_NAME']] = obj elif item_type == TOSItemGroup.COLLECTION: globals.collections[obj['$ID']] = obj globals.collections_by_name[obj['$ID_NAME']] = obj elif item_type == TOSItemGroup.CUBE: globals.cubes[obj['$ID']] = obj globals.cubes_by_name[obj['$ID_NAME']] = obj globals.cubes_by_stringarg[row['StringArg']] = obj elif item_type == TOSItemGroup.GEM: globals.gems[obj['$ID']] = obj globals.gems_by_name[obj['$ID_NAME']] = obj elif item_type == TOSItemGroup.RECIPE: globals.recipes[obj['$ID']] = obj globals.recipes_by_name[obj['$ID_NAME']] = obj elif item_type in ITEM_GROUP_FASHION_WHITELIST\ or item_type_equipment in TYPE_EQUIPMENT_COSTUME_LIST\ or 'ClassType2' in row and row['ClassType2'] == 'Premium': globals.equipment[obj['$ID']] = obj globals.equipment_by_name[obj['$ID_NAME']] = obj elif item_type in ITEM_GROUP_ITEM_WHITELIST: globals.items[obj['$ID']] = obj globals.items_by_name[obj['$ID_NAME']] = obj elif item_type in ITEM_GROUP_EQUIPMENT_WHITELIST and item_type_equipment is not None: globals.equipment[obj['$ID']] = obj globals.equipment_by_name[obj['$ID_NAME']] = obj ies_file.close()
def parse_links_jobs(): logging.debug("Parsing attributes <> jobs...") LUA = luautil.load_script('ability_price.lua', '*') LUA_UNLOCK = luautil.load_script('ability_unlock.lua', '*', False) # Parse level, unlock and formula ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', 'job.ies') with open(ies_path, 'rb') as ies_file: for row in csv.DictReader(ies_file, delimiter=',', quotechar='"'): job = globals.jobs_by_name[row['ClassName']] ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies_ability.ipf', 'ability_' + row['EngName'].lower() + '.ies') # If this job is still under development, skip if not os.path.isfile(ies_path): continue with open(ies_path, 'rb') as ies_file: for row in csv.DictReader(ies_file, delimiter=',', quotechar='"'): attribute = globals.attributes_by_name[row['ClassName']] attribute['DescriptionRequired'] = attribute['DescriptionRequired'] if attribute['DescriptionRequired'] else '' attribute['DescriptionRequired'] = attribute['DescriptionRequired'] + '{nl}{b}' + parser_translations.translate(row['UnlockDesc']) + '{b}' attribute['LevelMax'] = int(row['MaxLevel']) # Parse attribute cost if row['ScrCalcPrice']: for lv in range(int(row['MaxLevel'])): attribute['UpgradePrice'].append(LUA[row['ScrCalcPrice']](None, row['ClassName'], lv + 1, attribute['LevelMax'])[0]) attribute['UpgradePrice'] = [value for value in attribute['UpgradePrice'] if value > 0] # Parse attribute skill (in case it is missing in the ability.ies) if not attribute['Link_Skills'] and row['UnlockArgStr'] in globals.skills_by_name: logging.debug('adding missing skill %s', row['UnlockArgStr']) skill = globals.skills_by_name[row['UnlockArgStr']] globals.link( attribute, 'Link_Skills', globals.get_attribute_link(attribute), skill, 'Link_Attributes', globals.get_skill_link(skill) ) # Parse attribute job if not attribute['Link_Skills'] or 'All' in attribute['Link_Skills']: globals.link( attribute, 'Link_Jobs', globals.get_attribute_link(attribute), job, 'Link_Attributes', globals.get_job_link(job) ) # Parse attribute unlock attribute['Unlock'] = luautil.lua_function_source_to_javascript( luautil.lua_function_source(LUA_UNLOCK[row['UnlockScr']])[1:-1] # remove 'function' and 'end' ) if not attribute['Unlock'] and row['UnlockScr'] else attribute['Unlock'] attribute['UnlockArgs'][job['$ID']] = { 'UnlockArgStr': row['UnlockArgStr'], 'UnlockArgNum': row['UnlockArgNum'], }
def parse_equipment(): logging.debug('Parsing equipment...') LUA = luautil.load_script('item_calculate.lua', [ 'GET_COMMON_PROP_LIST', 'GET_BASIC_ATK', 'GET_BASIC_MATK', 'INIT_ARMOR_PROP', 'INIT_WEAPON_PROP', 'SCR_GET_ITEM_GRADE_RATIO', 'SCR_REFRESH_ACC', 'SCR_REFRESH_ARMOR', 'SCR_REFRESH_HAIRACC', 'SCR_REFRESH_WEAPON', 'GET_REINFORCE_ADD_VALUE_ATK', 'GET_REINFORCE_ADD_VALUE', 'GET_REINFORCE_PRICE', ]) LUA_REINFORCE = luautil.load_script('lib_reinforce_131014.lua', ['GET_REINFORCE_PRICE']) LUA_TRANSCEND = luautil.load_script('item_transcend_shared.lua', ['GET_TRANSCEND_MATERIAL_COUNT']) ies_path = os.path.join(constants.PATH_INPUT_DATA, 'ies.ipf', 'item_equip.ies') ies_file = open(ies_path, 'rb') ies_reader = csv.DictReader(ies_file, delimiter=',', quotechar='"') for row in ies_reader: if int(row['ClassID']) not in globals.equipment: continue item_grade = equipment_grade_ratios[int(row['ItemGrade'])] item_type_equipment = TOSEquipmentType.value_of(row['ClassType'].upper()) obj = globals.equipment[int(row['ClassID'])] # Calculate all properties using in-game formulas tooltip_script = row['RefreshScp'] tooltip_script = 'SCR_REFRESH_ACC' if not tooltip_script and 'Accessory_' in row['MarketCategory'] else tooltip_script tooltip_script = 'SCR_REFRESH_ARMOR' if not tooltip_script and 'Armor_' in row['MarketCategory'] else tooltip_script tooltip_script = 'SCR_REFRESH_HAIRACC' if not tooltip_script and 'HairAcc_' in row['MarketCategory'] else tooltip_script tooltip_script = 'SCR_REFRESH_WEAPON' if not tooltip_script and ('Weapon_' in row['MarketCategory'] or 'ChangeEquip_' in row['MarketCategory']) else tooltip_script if tooltip_script: try: LUA[tooltip_script](row) except LuaError as error: if row['ClassID'] not in ['11130', '635061']: logging.error('LUA error when processing item ClassID: %s', row['ClassID']) raise error # Add additional fields obj['AnvilATK'] = [] obj['AnvilDEF'] = [] obj['AnvilPrice'] = [] obj['Bonus'] = [] obj['Durability'] = int(row['MaxDur']) / 100 obj['Durability'] = -1 if obj['Durability'] <= 0 else obj['Durability'] obj['Grade'] = TOSEquipmentGrade.value_of(int(row['ItemGrade'])) obj['Level'] = int(row['ItemLv']) if int(row['ItemLv']) > 0 else int(row['UseLv']) obj['Material'] = TOSEquipmentMaterial.value_of(row['Material']) obj['Potential'] = int(row['MaxPR']) obj['RequiredClass'] = '%s%s%s%s%s' % ( 'T' if any(j in row['UseJob'] for j in ['All', 'Char3']) else 'F', # Archer 'T' if any(j in row['UseJob'] for j in ['All', 'Char4']) else 'F', # Cleric 'T' if any(j in row['UseJob'] for j in ['All', 'Char5']) else 'F', # Scout 'T' if any(j in row['UseJob'] for j in ['All', 'Char1']) else 'F', # Swordsman 'T' if any(j in row['UseJob'] for j in ['All', 'Char2']) else 'F', # Wizard ) obj['RequiredLevel'] = int(row['UseLv']) obj['Sockets'] = int(row['BaseSocket']) obj['SocketsLimit'] = int(row['MaxSocket_COUNT']) obj['Stars'] = int(row['ItemStar']) obj['Stat_ATTACK_MAGICAL'] = int(row['MATK']) if 'MATK' in row else 0 obj['Stat_ATTACK_PHYSICAL_MIN'] = int(row['MINATK']) if 'MINATK' in row else 0 obj['Stat_ATTACK_PHYSICAL_MAX'] = int(row['MAXATK']) if 'MAXATK' in row else 0 obj['Stat_DEFENSE_MAGICAL'] = int(row['MDEF']) if 'MDEF' in row else 0 obj['Stat_DEFENSE_PHYSICAL'] = int(row['DEF']) if 'DEF' in row else 0 obj['TranscendPrice'] = [] obj['TypeAttack'] = TOSAttackType.value_of(row['AttackType']) obj['TypeEquipment'] = item_type_equipment obj['Unidentified'] = int(row['NeedAppraisal']) == 1 obj['UnidentifiedRandom'] = int(row['NeedRandomOption']) == 1 obj['Link_Set'] = None # HotFix: if it's a Rapier, use THRUST as the TypeAttack if obj['TypeEquipment'] == TOSEquipmentType.RAPIER: obj['TypeAttack'] = TOSAttackType.MELEE_THRUST # HotFix: in case it doesn't give physical nor magical defense (e.g. agny necklace) if 'ADD_FIRE' in row['BasicTooltipProp'].split(','): lv = obj['Level'] gradeRatio = (int(item_grade['BasicRatio']) / 100.0) row['ADD_FIRE'] = floor(lv * gradeRatio) # Anvil if any(prop in row['BasicTooltipProp'] for prop in ['ATK', 'DEF', 'MATK', 'MDEF']): for lv in range(40): row['Reinforce_2'] = lv if any(prop in row['BasicTooltipProp'] for prop in ['DEF', 'MDEF']): obj['AnvilDEF'].append(LUA['GET_REINFORCE_ADD_VALUE'](None, row, 0, 1)) obj['AnvilPrice'].append(LUA_REINFORCE['GET_REINFORCE_PRICE'](row, {}, None)) if any(prop in row['BasicTooltipProp'] for prop in ['ATK', 'MATK']): obj['AnvilATK'].append(LUA['GET_REINFORCE_ADD_VALUE_ATK'](row, 0, 1, None)) obj['AnvilPrice'].append(LUA_REINFORCE['GET_REINFORCE_PRICE'](row, {}, None)) obj['AnvilPrice'] = [value for value in obj['AnvilPrice'] if value > 0] obj['AnvilATK'] = [value for value in obj['AnvilATK'] if value > 0] if len(obj['AnvilPrice']) > 0 else None obj['AnvilDEF'] = [value for value in obj['AnvilDEF'] if value > 0] if len(obj['AnvilPrice']) > 0 else None # Bonus for stat in EQUIPMENT_STAT_COLUMNS: if stat in row: value = floor(float(row[stat])) if value != 0: obj['Bonus'].append([ TOSEquipmentStat.value_of(stat), # Stat value # Value ]) # More Bonus if 'OptDesc' in row and len(row['OptDesc']) > 0: for bonus in parser_translations.translate(row['OptDesc']).split('{nl}'): bonus = bonus.strip() bonus = bonus[bonus.index('-'):] if '-' in bonus else bonus obj['Bonus'].append([ TOSEquipmentStat.UNKNOWN, # Stat bonus.replace('- ', '').strip() # Value ]) # Transcendence for lv in range(10): row['Transcend'] = lv obj['TranscendPrice'].append(LUA_TRANSCEND['GET_TRANSCEND_MATERIAL_COUNT'](row, None)) obj['TranscendPrice'] = [value for value in obj['TranscendPrice'] if value > 0]