def kr_no_to_monster_id(kr_id: MonsterNo) -> MonsterId: if kr_id > 99999: sub_id = MonsterNo(kr_id % 100000) kr_id -= sub_id kr_id += _kr_no_to_monster_id(sub_id) else: kr_id = _kr_no_to_monster_id(kr_id) return MonsterId(kr_id)
def na_no_to_monster_id(na_id: MonsterNo) -> MonsterId: if na_id > 99999: sub_id = MonsterNo(na_id % 100000) na_id -= sub_id na_id += _na_no_to_monster_id(sub_id) else: na_id = _na_no_to_monster_id(na_id) return MonsterId(na_id)
def copy_media(args): base_dir = args.base_dir output_dir = args.output_dir jp_icon_input_dir = os.path.join(base_dir, 'jp', 'portrait', 'local') na_icon_input_dir = os.path.join(base_dir, 'na', 'portrait', 'local') jp_portrait_input_dir = os.path.join(base_dir, 'jp', 'full', 'corrected_data') na_portrait_input_dir = os.path.join(base_dir, 'na', 'full', 'corrected_data') hq_portrait_input_dir = os.path.join(base_dir, 'hq_images') animated_portrait_input_dir = os.path.join(base_dir, 'animated') icon_output_dir = os.path.join(output_dir, 'icons') portrait_output_dir = os.path.join(output_dir, 'portraits') hq_portrait_output_dir = os.path.join(output_dir, 'hq_portraits') animated_portrait_output_dir = os.path.join(output_dir, 'animated_portraits') for jp_id in range(1, 9000): monster_id = jp_id monster_id_filled = str(monster_id).zfill(5) do_copy(jp_icon_input_dir, '{}.png'.format(monster_id), icon_output_dir, '{}.png'.format(monster_id_filled)) do_copy(jp_portrait_input_dir, '{}.png'.format(monster_id), portrait_output_dir, '{}.png'.format(monster_id_filled)) do_copy(hq_portrait_input_dir, '{}.png'.format(monster_id), hq_portrait_output_dir, '{}.png'.format(monster_id_filled)) do_copy(animated_portrait_input_dir, '{}.mp4'.format(monster_id), animated_portrait_output_dir, '{}.mp4'.format(monster_id_filled)) do_copy(animated_portrait_input_dir, '{}.gif'.format(monster_id), animated_portrait_output_dir, '{}.gif'.format(monster_id_filled)) for na_id in range(1, 9000): monster_id = monster_id_mapping.na_no_to_monster_id(MonsterNo(na_id)) monster_id_filled = str(monster_id).zfill(5) do_copy(na_icon_input_dir, '{}.png'.format(na_id), icon_output_dir, '{}.png'.format(monster_id_filled)) do_copy(na_portrait_input_dir, '{}.png'.format(na_id), portrait_output_dir, '{}.png'.format(monster_id_filled))
def __init__(self, raw: List[str]): _unflatten(raw, 57, 3, replace=True) _unflatten(raw, 58, 1, replace=True) self.monster_no = MonsterNo(int(raw[0])) self.name = raw[1] self.attr_id = AttrId(int(raw[2])) self.sub_attr_id = AttrId(int(raw[3])) self.is_ult = bool(raw[4]) # True if ultimate, False if normal evo self.type_1_id = TypeId(int(raw[5])) self.type_2_id = TypeId(int(raw[6])) self.rarity = int(raw[7]) self.cost = int(raw[8]) # Appears to be related to the size of the monster. # If 5, the monster always spawns alone. Needs more research. self.unknown_009 = int(raw[9]) self.max_level = int(raw[10]) self.feed_xp_per_level = int(raw[11]) // 4 self.released_status = raw[12] == 100 self.sell_gold_per_level = int(raw[13]) // 10 self.min_hp = int(raw[14]) self.max_hp = int(raw[15]) self.hp_scale = float(raw[16]) self.min_atk = int(raw[17]) self.max_atk = int(raw[18]) self.atk_scale = float(raw[19]) self.min_rcv = int(raw[20]) self.max_rcv = int(raw[21]) self.rcv_scale = float(raw[22]) self.xp_max = int(raw[23]) self.xp_scale = float(raw[24]) self.active_skill_id = SkillId(int(raw[25])) self.leader_skill_id = SkillId(int(raw[26])) # Enemy turn timer for normal dungeons, and techs where enemy_turns_alt is not populated. self.enemy_turns = int(raw[27]) # Min = lvl 1 and Max = lvl 10 self.enemy_hp_min = int(raw[28]) self.enemy_hp_max = int(raw[29]) self.enemy_hp_scale = float(raw[30]) self.enemy_atk_min = int(raw[31]) self.enemy_atk_max = int(raw[32]) self.enemy_atk_scale = float(raw[33]) self.enemy_def_min = int(raw[34]) self.enemy_def_max = int(raw[35]) self.enemy_def_scale = float(raw[36]) self.enemy_max_level = int(raw[37]) self.enemy_coins_per_level = int(raw[38]) // 2 self.enemy_xp_per_level = int(raw[39]) // 2 self.ancestor_id = MonsterNo(int(raw[40])) self.evo_mat_id_1 = MonsterNo(int(raw[41])) self.evo_mat_id_2 = MonsterNo(int(raw[42])) self.evo_mat_id_3 = MonsterNo(int(raw[43])) self.evo_mat_id_4 = MonsterNo(int(raw[44])) self.evo_mat_id_5 = MonsterNo(int(raw[45])) self.un_evo_mat_1 = MonsterNo(int(raw[46])) self.un_evo_mat_2 = MonsterNo(int(raw[47])) self.un_evo_mat_3 = MonsterNo(int(raw[48])) self.un_evo_mat_4 = MonsterNo(int(raw[49])) self.un_evo_mat_5 = MonsterNo(int(raw[50])) # When >0, the enemy turn timer for technical dungeons. self.enemy_turns_alt = int(raw[51]) # Controls whether the monster uses the 'new' AI or the 'old' AI. # Monsters using the old AI only have support up to some limit of ES values. # One main difference between is behavior during preempts; old-AI monsters will # attack if they cannot execute a preempt, new-AI monsters will skip to the next. # (needs verification). self.use_new_ai = bool(raw[52]) # Each monster has an internal counter which starts at raw[53] and is decremented # each time a skill activates. If the counter is less than the action cost, it cannot # execute. # # Turn flow follows this order: # 1: pick action (possibly checking counter value) # 2: increment the counter up, capped at the max value # 3: decrement the counter based on the selected action value # # The starting and maximum value for the enemy skill action counter. self.enemy_skill_max_counter = int(raw[53]) # The amount to increment the counter each turn. # # The vast majority of these are 0/1. # Deus Ex Machina has 2, Kanna has 7. self.enemy_skill_counter_increment = int(raw[54]) # Boolean, unlikely to be anything useful, only populated for 495 and 111. self.unknown_055 = raw[55] # Unused self.unknown_056 = raw[56] self.enemy_skill_refs = [] # type: List[ESRef] es_data = list(map(int, raw[57])) for i in range(0, len(es_data) - 2, 3): self.enemy_skill_refs.append( ESRef(es_data[i], es_data[i + 1], es_data[i + 2])) self.awakenings = raw[58] # type: List[int] self.super_awakenings = list( map(int, filter(str.strip, raw[59].split(',')))) # List[int] self.base_id = MonsterNo(int(raw[60])) # ?? self.group_id = raw[61] # ?? self.type_3_id = TypeId(int(raw[62])) self.sell_mp = int(raw[63]) self.latent_on_feed = int(raw[64]) self.collab_id = int(raw[65]) # Bitmap with some random flag values, not sure what they all do. self.random_flags = int(raw[66]) self.inheritable = bool(self.random_flags & 1) self.is_collab = bool(self.random_flags & 4) self.furigana = str(raw[67]) # JP data only? self.limit_mult = int(raw[68]) # Number of the voice file, 1-indexed, 0 if no voice self.voice_id = int(raw[69]) # Number of the orb skin unlocked, 1-indexed, 0 if no orb skin self.orb_skin_id = int(raw[70]) self.other_fields = raw[71:]
def copy_media(args): base_dir = args.base_dir alt_base_dir = args.alt_base_dir output_dir = args.output_dir jp_icon_input_dir = os.path.join(base_dir, 'jp', 'portrait', 'local') na_icon_input_dir = os.path.join(base_dir, 'na', 'portrait', 'local') jp_portrait_input_dir = os.path.join(base_dir, 'jp', 'full', 'corrected_data') na_portrait_input_dir = os.path.join(base_dir, 'na', 'full', 'corrected_data') hq_portrait_input_dir = os.path.join(base_dir, 'hq_images') animated_portrait_input_dir = os.path.join(base_dir, 'animated') orb_skins_input_dir = os.path.join(alt_base_dir, 'orb_styles', 'extract', 'jp') jp_voice_input_dir = os.path.join(alt_base_dir, 'voices', 'fixed', 'jp') na_voice_input_dir = os.path.join(alt_base_dir, 'voices', 'fixed', 'na') icon_output_dir = os.path.join(output_dir, 'icons') portrait_output_dir = os.path.join(output_dir, 'portraits') hq_portrait_output_dir = os.path.join(output_dir, 'hq_portraits') animated_portrait_output_dir = os.path.join(output_dir, 'animated_portraits') orb_skins_output_dir = os.path.join(output_dir, 'orb_skins') jp_voice_output_dir = os.path.join(output_dir, 'voices', 'jp') na_voice_output_dir = os.path.join(output_dir, 'voices', 'na') for jp_id in range(1, 9000): monster_id = jp_id monster_id_filled = str(monster_id).zfill(5) do_copy(jp_icon_input_dir, '{}.png'.format(monster_id), icon_output_dir, '{}.png'.format(monster_id_filled)) do_copy(jp_portrait_input_dir, '{}.png'.format(monster_id), portrait_output_dir, '{}.png'.format(monster_id_filled)) do_copy(hq_portrait_input_dir, '{}.png'.format(monster_id), hq_portrait_output_dir, '{}.png'.format(monster_id_filled)) do_copy(animated_portrait_input_dir, '{}.mp4'.format(monster_id), animated_portrait_output_dir, '{}.mp4'.format(monster_id_filled)) do_copy(animated_portrait_input_dir, '{}.gif'.format(monster_id), animated_portrait_output_dir, '{}.gif'.format(monster_id_filled)) do_copy(jp_voice_input_dir, '{}.wav'.format(monster_id), jp_voice_output_dir, '{}.wav'.format(monster_id_filled)) for na_id in range(1, 9000): monster_id = monster_id_mapping.nakr_no_to_monster_id(MonsterNo(na_id)) monster_id_filled = str(monster_id).zfill(5) do_copy(na_icon_input_dir, '{}.png'.format(na_id), icon_output_dir, '{}.png'.format(monster_id_filled)) do_copy(na_portrait_input_dir, '{}.png'.format(na_id), portrait_output_dir, '{}.png'.format(monster_id_filled)) do_copy(na_voice_input_dir, '{}.wav'.format(na_id), na_voice_output_dir, '{}.wav'.format(monster_id_filled)) for file_name in os.listdir(orb_skins_input_dir): clean_file_name = file_name.lower().lstrip('block') do_copy(orb_skins_input_dir, file_name, orb_skins_output_dir, clean_file_name)
def __init__(self, raw: List): _unflatten(raw, 57, 3) _unflatten(raw, 58, 1) self.monster_no = MonsterNo(raw[0]) self.name: str = raw[1] self.attr_id = AttrId(raw[2]) self.sub_attr_id = AttrId(raw[3]) self.is_ult: bool = bool( raw[4]) # True if ultimate, False if normal evo self.type_1_id = TypeId(raw[5]) self.type_2_id = TypeId(raw[6]) self.rarity: int = raw[7] self.cost: int = raw[8] # Appears to be related to the size of the monster. # If 5, the monster always spawns alone. Needs more research. self.unknown_009: int = raw[9] self.max_level: int = raw[10] self.feed_xp_per_level = raw[11] / 4 self.released_status = raw[12] == 100 self.sell_gold_per_level = raw[13] / 10 self.min_hp: int = raw[14] self.max_hp: int = raw[15] self.hp_scale = float(raw[16]) self.min_atk: int = raw[17] self.max_atk: int = raw[18] self.atk_scale = float(raw[19]) self.min_rcv: int = raw[20] self.max_rcv: int = raw[21] self.rcv_scale = float(raw[22]) self.xp_max: int = raw[23] self.xp_scale = float(raw[24]) self.active_skill_id = SkillId(raw[25]) self.leader_skill_id = SkillId(raw[26]) # Enemy turn timer for normal dungeons, and techs where enemy_turns_alt is not populated. self.enemy_turns: int = raw[27] # Min = lvl 1 and Max = lvl 10 self.enemy_hp_min: int = raw[28] self.enemy_hp_max: int = raw[29] self.enemy_hp_scale = float(raw[30]) self.enemy_atk_min: int = raw[31] self.enemy_atk_max: int = raw[32] self.enemy_atk_scale = float(raw[33]) self.enemy_def_min: int = raw[34] self.enemy_def_max: int = raw[35] self.enemy_def_scale = float(raw[36]) self.enemy_max_level: int = raw[37] self.enemy_coins_per_level = raw[38] / 2 self.enemy_xp_per_level = raw[39] / 2 self.ancestor_id = MonsterNo(raw[40]) self.evo_mat_id_1 = MonsterNo(raw[41]) self.evo_mat_id_2 = MonsterNo(raw[42]) self.evo_mat_id_3 = MonsterNo(raw[43]) self.evo_mat_id_4 = MonsterNo(raw[44]) self.evo_mat_id_5 = MonsterNo(raw[45]) self.un_evo_mat_1 = MonsterNo(raw[46]) self.un_evo_mat_2 = MonsterNo(raw[47]) self.un_evo_mat_3 = MonsterNo(raw[48]) self.un_evo_mat_4 = MonsterNo(raw[49]) self.un_evo_mat_5 = MonsterNo(raw[50]) # When >0, the enemy turn timer for technical dungeons. self.enemy_turns_alt: int = raw[51] # Controls whether the monster uses the 'new' AI or the 'old' AI. # Monsters using the old AI only have support up to some limit of ES values. # One main difference between is behavior during preempts; old-AI monsters will # attack if they cannot execute a preempt, new-AI monsters will skip to the next. # (needs verification). self.use_new_ai = bool(raw[52]) # Each monster has an internal counter which starts at raw[53] and is decremented # each time a skill activates. If the counter is less than the action cost, it cannot # execute. # # Turn flow follows this order: # 1: pick action (possibly checking counter value) # 2: increment the counter up, capped at the max value # 3: decrement the counter based on the selected action value # # The starting and maximum value for the enemy skill action counter. self.enemy_skill_max_counter: int = raw[53] # The amount to increment the counter each turn. # # The vast majority of these are 0/1. # Deus Ex Machina has 2, Kanna has 7. self.enemy_skill_counter_increment: int = raw[54] # Boolean, unlikely to be anything useful, only populated for 495 (1) and 111 (1000). self.unknown_055: int = raw[55] # Unused self.unknown_056: Any = raw[56] self.enemy_skill_refs: List[ESRef] = [] es_data: List[int] = raw[57] for i in range(0, len(es_data) - 2, 3): self.enemy_skill_refs.append( ESRef(es_data[i], es_data[i + 1], es_data[i + 2])) self.awakenings: List[int] = raw[58] self.super_awakenings: List[int] = list( map(int, filter(str.strip, raw[59].split(',')))) self.base_id = MonsterNo(int(raw[60])) self.group_id: int = raw[61] self.type_3_id = TypeId(int(raw[62])) self.sell_mp: int = raw[63] self.latent_on_feed: int = raw[64] self.collab_id: int = raw[65] # Bitmap with some non-random flag values self.flags: int = raw[66] self.inheritable_flag = bool(self.flags & 1) self.take_assists_flag = bool(self.flags & 2) self.is_collab_flag = bool(self.flags & 4) self.unstackable_flag = bool(self.flags & 8) self.assist_only_flag = bool(self.flags & 16) self.latent_slot_unlock_flag = bool(self.flags & 32) # Composed with flags and other monster attributes self.inheritable = bool(self.inheritable_flag and self.active_skill_id) self.take_assists = bool(self.take_assists_flag and self.active_skill_id) self.is_stackable = bool(not self.unstackable_flag and self.type_1_id in [0, 12, 14]) self.ownable = self.monster_no < 100000 self.usable = bool(not self.assist_only_flag and self.ownable) self.search_strings: List[str] = raw[67].split('|') self.limit_mult: int = raw[68] # Number of the voice file, 1-indexed, 0 if no voice self.voice_id: int = raw[69] # Number of the orb skin unlocked, 1-indexed, 0 if no orb skin self.orb_skin_id: int = raw[70] # Seems like this could have multiple values. self.tags: str = raw[71] self.linked_monster_no: Optional[MonsterNo] = None # No longer used if self.tags: if 'link:' in self.tags: self.linked_monster_no = MonsterNo( int(self.tags[len('link:'):])) else: human_fix_logger.error('Unexpected tag value: %s', self.tags) # Remove this condition once it comes to NA self.ls_bitflag: int = raw[72] + (raw[73] << 32) self.other_fields: List = raw[74:] if self.other_fields: human_fix_logger.error('Unused monster values found.')