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 = {}
Beispiel #2
0
    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()}
Beispiel #3
0
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:]
Beispiel #5
0
    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:]))
Beispiel #6
0
    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:]))
Beispiel #7
0
    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
Beispiel #8
0
    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()
Beispiel #9
0
    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
Beispiel #10
0
    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)