def __init__(self, raw: Dict[str, Any], server: str, gtype: int): self.name = str(raw['name']) self.server = server self.clean_name = pad_util.strip_colors(self.name) # Start time as gungho time string self.start_time_str = str(raw['start']) self.start_timestamp = pad_util.gh_to_timestamp( self.start_time_str, server) # End time as gungho time string self.end_time_str = str(raw['end']) self.end_timestamp = pad_util.gh_to_timestamp(self.end_time_str, server) # TODO: extra egg machine parser needs to pull out comment self.comment = str(raw.get('comment', '')) self.clean_comment = pad_util.strip_colors(self.comment) # The egg machine ID used in the API call param grow self.egg_machine_row = int(raw['row']) # The egg machine ID used in the API call param gtype # Corresponds to the ordering of the item in egatya3 self.egg_machine_type = gtype # Not sure exactly how this is used self.alt_egg_machine_type = int(raw['type']) # Stone or pal point cost self.cost = int(raw['pri']) # Monster ID to % self.contents = {}
def __init__(self, raw: Dict[str, Any], server: Server): self.name = str(raw['name']) self.clean_name = pad_util.strip_colors(self.name) # Start time as gungho time string self.start_time_str = str(raw.get('start', raw.get('start_time_str'))) self.start_timestamp = pad_util.gh_to_timestamp_2(self.start_time_str, server) # End time as gungho time string self.end_time_str = str(raw.get('end', raw.get('end_time_str'))) self.end_timestamp = pad_util.gh_to_timestamp_2(self.end_time_str, server) # TODO: extra egg machine parser needs to pull out comment self.comment = str(raw.get('comment', '')) self.clean_comment = pad_util.strip_colors(self.comment) # The egg machine ID used in the API call param grow self.egg_machine_row = int(raw.get('row', raw.get('egg_machine_row'))) # The egg machine ID used in the API call param gtype # Corresponds to the ordering of the item in egatya3 self.egg_machine_type = int(raw['egg_machine_type']) # Stone or pal point cost self.cost = int(raw.get('pri', raw.get('cost'))) # Monster ID to % contents = raw.get('contents', {}) self.contents = {int(k): v for k, v in contents.items()}
def _compare_named(override, dest): """Tries to determine if we should replace dest with override. First compares the null-ness of each, and then compares the quality of the 'name' property. """ if override and not dest: return override if override \ and dest and is_bad_name(pad_util.strip_colors(dest.name)) \ and not is_bad_name(pad_util.strip_colors(override.name)): return override return dest
def __init__(self, skill_id: int, raw: List[str]): self.skill_id = SkillId(skill_id) # Skill name text. self.name = raw[0] # Skill description text (may include formatting). self.description = raw[1] # Skill description text (no formatting). self.clean_description = pad_util.strip_colors( self.description).replace('\n', ' ').replace('^p', '') # Encodes the type of skill (requires parsing other_fields). self.skill_type = int(raw[2]) # If an active skill, number of levels to max. self.levels = int(raw[3]) or None # If an active skill, maximum cooldown. self.cooldown_turns_max = int(raw[4]) if self.levels else None # If an active skill, minimum cooldown. self.cooldown_turns_min = self.cooldown_turns_max - ( self.levels - 1) if self.levels else None # Unknown field. self.unknown_005 = raw[5] # Fields used in coordination with skill_type. self.data = raw[6:]
def __init__(self, raw: List[str], server: Server): self.server = server self.unknown_000 = str(raw[0]) # Seems to always be 'A' # Seems to be the unique ID for the trade? self.trade_id = int(raw[1]) # Seems to be an order field, with lower values towards the top? self.display_order = int(raw[2]) # 1-indexed menu this appears in self.menu_idx = int(raw[3]) # Trade monster ID self.monster_id = int(raw[4]) # Usually 1 and 0 # Except for the PAD Appreciation/Spring Fest Medal trades which are 99 and 3 self.unknown_005 = int(raw[5]) self.unknown_006 = int(raw[6]) # Trade availability start time string self.start_time_str = str(raw[7]) self.start_timestamp = pad_util.gh_to_timestamp_2( self.start_time_str, server) # Trade availability end time string self.end_time_str = str(raw[8]) self.end_timestamp = pad_util.gh_to_timestamp_2( self.end_time_str, server) # Start time string for the announcement text, probably? self.announcement_start_time_str = str(raw[9]) self.announcement_start_timestamp = pad_util.gh_to_timestamp_2( self.announcement_start_time_str, server) if self.announcement_start_time_str else '' # End time string for the announcement text, probably? self.announcement_end_time_str = str(raw[10]) self.announcement_end_timestamp = pad_util.gh_to_timestamp_2( self.announcement_end_time_str, server) if self.announcement_end_time_str else '' # Optional text that appears above monster name, for limited time events self.announcement_text = str(raw[11]) # Clean version of the announcement text without formatting self.announcement_text_clean = pad_util.strip_colors( self.announcement_text) # Number of required monsters for the trade self.required_count = int(raw[12]) # Seems to be the 'flag' type, e.g. 'restricted'? # If so, 0=No Flag, 2='Restricted'. self.flag_type = int(raw[13]) self.restricted = (self.flag_type == 2) # Options for trading the monster self.required_monsters = list(map(int, raw[14:]))
def __init__(self, raw: List[str], server: Server): self.server = server self.unknown_000 = str(raw[0]) # Seems to always be 'A' # Seems to be the unique ID for the trade? self.trade_id = int(raw[1]) # Seems to be an order field, with lower values towards the top? self.display_order = int(raw[2]) # 1-indexed menu this appears in self.menu_idx = int(raw[3]) # Trade monster ID self.monster_id = int(raw[4]) # Trade monster info self.monster_level = int(raw[5]) monster_flags = int(raw[6]) self.monster_max_skill = bool(monster_flags & 1) self.monster_max_awoken = bool(monster_flags & 2) # Trade availability start time string self.start_time_str = str(raw[7]) self.start_timestamp = pad_util.gh_to_timestamp_2(self.start_time_str, server) # Trade availability end time string self.end_time_str = str(raw[8]) self.end_timestamp = pad_util.gh_to_timestamp_2(self.end_time_str, server) # Start time string for the announcement text, probably? self.announcement_start_time_str = str(raw[9]) self.announcement_start_timestamp = pad_util.gh_to_timestamp_2( self.announcement_start_time_str, server) if self.announcement_start_time_str else '' # End time string for the announcement text, probably? self.announcement_end_time_str = str(raw[10]) self.announcement_end_timestamp = pad_util.gh_to_timestamp_2( self.announcement_end_time_str, server) if self.announcement_end_time_str else '' # Optional text that appears above monster name, for limited time events self.announcement_text = str(raw[11]) # Clean version of the announcement text without formatting self.announcement_text_clean = pad_util.strip_colors(self.announcement_text) # Number of required monsters for the trade self.required_count = int(raw[12]) # Flags, e.g. restricted self.flag_type = int(raw[13]) self.no_dupes = bool(self.flag_type & 1) self.restricted = bool(self.flag_type & 2) self.multi_exchange = bool(self.flag_type & 4) # Options for trading the monster self.required_monsters = list(map(int, raw[14:]))
def __init__(self, raw: Dict[str, str], server: Server): if not set(raw) <= set(Bonus.keys): raise ValueError('Unexpected keys: ' + str(set(raw) - set(Bonus.keys))) # Start time as gungho time string self.start_time_str = str(raw['s']) self.start_timestamp = pad_util.gh_to_timestamp_2( self.start_time_str, server) # End time as gungho time string self.end_time_str = str(raw['e']) self.end_timestamp = pad_util.gh_to_timestamp_2( self.end_time_str, server) # Optional DungeonId self.dungeon_id = None # type: Optional[DungeonId] if 'd' in raw: self.dungeon_id = DungeonId(int(raw['d'])) # Optional DungeonFloorId # Stuff like rewards text in monthly quests self.sub_dungeon_id = None # type: Optional[SubDungeonId] if 'f' in raw: self.sub_dungeon_id = SubDungeonId(int(raw['f'])) # If REM/PEM, the ID of the machine self.egg_machine_id = None # type: Optional[int] if 'i' in raw: self.egg_machine_id = int(raw['i']) # Optional human-readable message (with formatting) self.message = None # type: Optional[str] # Optional human-readable message (no formatting) self.clean_message = None # type: Optional[str] if 'm' in raw: self.message = str(raw['m']) self.clean_message = pad_util.strip_colors(self.message) bonus_id = int(raw['b']) self.bonus_info = TYPES_MAP.get(bonus_id, UNKNOWN_TYPE) # Bonus value, if provided, optionally processed self.bonus_value = None # type: Optional[Union[float, int]] if 'a' in raw: self.bonus_value = raw['a'] if self.bonus_info.mod_fn: self.bonus_value = self.bonus_info.mod_fn(self.bonus_value) # Human readable name for the bonus self.bonus_name = self.bonus_info.bonus_type.name if self.bonus_info.bonus_type == BonusType.unknown: self.bonus_name += '({})'.format(bonus_id) self.bonus_id = bonus_id
def __init__(self, raw: Dict[str, Any], server: Server): if not set(raw) <= set(Bonus.keys): raise ValueError('Unexpected keys: ' + str(set(raw) - set(Bonus.keys))) self.raw = raw # Start time as gungho time string self.start_time_str = str(raw['s']) self.start_timestamp = pad_util.gh_to_timestamp_2( self.start_time_str, server) # End time as gungho time string self.end_time_str = str(raw['e']) self.end_timestamp = pad_util.gh_to_timestamp_2( self.end_time_str, server) # Optional DungeonId self.dungeon_id = None # type: Optional[DungeonId] if 'd' in raw: self.dungeon_id = DungeonId(int(raw['d'])) # Optional DungeonFloorId # Stuff like rewards text in monthly quests self.sub_dungeon_id = None # type: Optional[SubDungeonId] if 'f' in raw: self.sub_dungeon_id = SubDungeonId(int(raw['f'])) # If REM/PEM, the ID of the machine self.egg_machine_id = None # type: Optional[int] if 'i' in raw: self.egg_machine_id = int(raw['i']) # Optional human-readable message (with formatting) self.message = None # type: Optional[str] # Optional human-readable message (no formatting) self.clean_message = None # type: Optional[str] # Optional URL in message self.url = None # type: Optional[str] if 'm' in raw: self.message = str(raw['m']) self.clean_message = pad_util.strip_colors(self.message) if (match := re.search(r'https?:.+?(?=\|)', self.message)): self.url = match.group()
def __init__(self, raw: List[Any]): self.sub_dungeons = [] # type: List[SubDungeon] self.dungeon_id = DungeonId(int(raw[0])) self.name = str(raw[1]) self.bitmap_2 = int(raw[2]) self.one_time = self.bitmap_2 & 1 > 0 self.bg_id = self.bitmap_2 >> 4 self.clean_name = pad_util.strip_colors(self.name) # Basic dungeon type computed by scanning the name for flags. self.dungeon_type = None # type: Optional[str] # A more detailed dungeon type. self.full_dungeon_type = dungeon_types.RawDungeonType(int(raw[3])) # This will be a day of the week, or an empty string if it doesn't repeat regularly self.repeat_day = dungeon_types.RawRepeatDay(int(raw[4])) # Seems to relate to dungeon type? self._unknown_5 = int(raw[5]) # Might have to do with the 'badge' that is shown, e.g. 102 == 'collab' self._unknown_6 = int(raw[6]) # Seems related to the ordering of dungeons, but only within their 'sub group'? self.order = int(raw[7]) if raw[7] else None self.remaining_fields = raw[8:] for prefix, dungeon_type in prefix_to_dungeontype.items(): if self.clean_name.startswith(prefix): self.dungeon_type = dungeon_type self.clean_name = self.clean_name[len(prefix):] break
def __init__(self, dungeon_id: DungeonId, raw: List[Any]): self.sub_dungeon_id = SubDungeonId(dungeon_id * 1000 + int(raw[0])) self.simple_sub_dungeon_id = int(raw[0]) self.raw_name = raw[1] self.clean_name = pad_util.strip_colors(self.raw_name) self.floors = int(raw[2]) self.rflags1 = int(raw[3]) self.stamina = raw[4] self.bgm1 = raw[5] self.bgm2 = raw[6] self.rflags2 = int(raw[7]) # If monsters can use skills in this dungeon. self.technical = self.rflags1 & 0x80 > 0 # This next loop runs through the elements from raw[8] until it hits a 0. The 0 indicates the end of the list # of drops for the floor, the following segments are the dungeon modifiers pos = 8 while int(raw[pos]) != 0: pos += 1 pos += 1 self.flags = int(raw[pos]) self.remaining_fields = raw[pos + 1:] # Modifiers parsing doesn't seem to always work # Hacked up version for dungeon modifiers, needed for # enemy parsing. self.hp_mult = 1.0 self.atk_mult = 1.0 self.def_mult = 1.0 for field in self.remaining_fields: if 'hp:' in field or 'at:' in field or 'df:' in field: for mod in field.split('|'): if mod.startswith('hp:'): self.hp_mult = float(mod[3:]) / 10000 elif mod.startswith('at:'): self.atk_mult = float(mod[3:]) / 10000 elif mod.startswith('df:'): self.def_mult = float(mod[3:]) / 10000 break # Modifiers parsing also seems to skip fixed teams sometimes. # Hacked up version for just that here. self.fixed_team = {} for field in self.remaining_fields: if not 'fc1' in field: continue else: # TODO: this broke, look into re-enabling it continue for sub_field in field.split('|'): if not sub_field.startswith('fc'): continue idx = int(sub_field[2]) contents = sub_field[4:] details = contents.split(';') full_record = len(details) > 1 self.fixed_team[idx] = { 'monster_id': details[0], 'hp_plus': details[1] if full_record else 0, 'atk_plus': details[2] if full_record else 0, 'rcv_plus': details[3] if full_record else 0, 'awakening_count': details[4] if full_record else 0, 'skill_level': details[5] if full_record else 0, } # This code imported from Rikuu, need to clean it up and merge # with the other modifiers parsing code. For now just importing # the score parsing, needed for dungeon loading. self.score = None i = 0 if (self.flags & 0x1) != 0: i += 2 # self.requirement = { # dungeonId: Number(self.remaining_fields[i++]), # floorId: Number(self.remaining_fields[i++]) # }; if (self.flags & 0x4) != 0: i += 1 # self.beginTime = fromPADTime(self.remaining_fields[i++]); if (self.flags & 0x8) != 0: self.score = int(self.remaining_fields[i]) i += 1 if (self.flags & 0x10) != 0: i += 1 # self.minRank = Number(self.remaining_fields[i++]); if (self.flags & 0x40) != 0: i += 1
def __init__(self, dungeon_id: DungeonId, raw: List[Any]): # https://github.com/TsubakiBotPad/pad-data-pipeline/wiki/Subdungeon-Arguments self.sub_dungeon_id = SubDungeonId(dungeon_id * 1000 + int(raw[0])) self.simple_sub_dungeon_id = int(raw[0]) self.raw_name = self.name = raw[1] self.clean_name = pad_util.strip_colors(self.raw_name) self.floors = int(raw[2]) self.rflags1 = int(raw[3]) self.stamina = raw[4] self.bgm1 = raw[5] self.bgm2 = raw[6] self.disables = int(raw[7]) # If monsters can use skills in this dungeon. self.technical = self.rflags1 & 0x80 > 0 # This next loop runs through the elements from raw[8] until it hits a 0. The 0 indicates the end of the list # of drops for the floor, the following segments are the dungeon modifiers pos = 8 while int(raw[pos]) != 0: pos += 1 pos += 1 flags = int(raw[pos]) pos += 1 self.prev_dungeon_id = None self.prev_floor_id = None self.start_timestamp = None self.score = None self.unknown_f4 = None pipe_hell = "" self.end_timestamp = None if flags & 1 << 0: # Prev Floor self.prev_dungeon_id = int(raw[pos]) self.prev_floor_id = int(raw[pos + 1]) pos += 2 if flags & 1 << 2: # Start Timestamp self.start_timestamp = ghtime(raw[pos], 'utc') pos += 1 if flags & 1 << 3: # S-Rank Score self.score = int(raw[pos]) pos += 1 if flags & 1 << 4: # Unknown Value self.unknown_f4 = int(raw[pos]) pos += 1 if flags & 1 << 6: # Pipe Hell pipe_hell = raw[pos] pos += 1 if flags & 1 << 7: # Start Timestamp self.end_timestamp = ghtime(raw[pos], 'utc') pos += 1 self.restriction_type = int(raw[pos]) self.restriction_args = raw[pos + 1:] modifiers = parse_modifiers(pipe_hell) self.hp_mult = default_int(modifiers, 'hp', 10000) / 10000 self.atk_mult = default_int(modifiers, 'at', 10000) / 10000 self.def_mult = default_int(modifiers, 'df', 10000) / 10000 self.fixed_monsters: Dict[int, FixedTeamMonster] = {} for idx in range(6): if None is (fc := modifiers.get(f'fc{idx + 1}')): # Not all dungeons have fixed cards in all slots continue self.fixed_monsters[idx] = FixedTeamMonster(fc)