예제 #1
0
파일: dragdrop.py 프로젝트: BEEmod/BEE2.4
 def __init__(
     self,
     name: str,
     pak_id: str,
     icon: str,
     group: str=None,
     group_icon: str=None,
 ) -> None:
     self.name = name
     self.dnd_icon = img.Handle.parse_uri(utils.PackagePath(pak_id, ('items/clean/{}.png'.format(icon))), 64, 64)
     self.dnd_group = group
     if group_icon:
         self.dnd_group_icon = img.Handle.parse_uri(utils.PackagePath(pak_id, 'items/clean/{}.png'.format(group_icon)), 64, 64)
예제 #2
0
    async def parse(cls, data: ParseData) -> ItemConfig:
        """Parse from config files."""
        vers: dict[str, dict[str, lazy_conf.LazyConf]] = {}
        styles: dict[str, lazy_conf.LazyConf]

        all_config = get_config(
            data.info,
            'items',
            pak_id=data.pak_id,
            prop_name='all_conf',
            source=f'<ItemConfig {data.pak_id}:{data.id} all_conf>',
        )

        for ver in data.info.find_all('Version'):
            ver_id = ver['ID', 'VER_DEFAULT']
            vers[ver_id] = styles = {}
            for sty_block in ver.find_all('Styles'):
                for style in sty_block:
                    styles[style.real_name] = lazy_conf.from_file(
                        utils.PackagePath(data.pak_id,
                                          f'items/{style.value}.cfg'),
                        source=
                        f'<ItemConfig {data.pak_id}:{data.id} in "{style.real_name}">',
                    )

        return ItemConfig(
            data.id,
            all_config,
            vers,
        )
예제 #3
0
def get_config(
    prop_block: Property,
    folder: str,
    pak_id: str,
    prop_name: str = 'config',
    extension: str = '.cfg',
    source: str = '',
) -> lazy_conf.LazyConf:
    """Lazily extract a config file referred to by the given property block.

    Looks for the prop_name key in the given prop_block.
    If the keyvalue has a value of "", an empty tree is returned.
    If it has children, a copy of them will be returned.
    Otherwise the value is a filename in the zip which will be parsed.

    If source is supplied, set_cond_source() will be run.
    """
    prop_block = prop_block.find_key(prop_name, "")
    if prop_block.has_children():
        prop = prop_block.copy()
        prop.name = ""
        return lazy_conf.raw_prop(prop, source=source)

    if prop_block.value == '':
        return lazy_conf.BLANK

    # Zips must use '/' for the separator, even on Windows!
    path = f'{folder}/{prop_block.value}'
    if len(path) < 3 or path[-4] != '.':
        # Add extension
        path += extension
    return lazy_conf.from_file(utils.PackagePath(pak_id, path), source=source)
예제 #4
0
    async def parse(cls, data: ParseData) -> Signage:
        styles: dict[str, SignStyle] = {}
        for prop in data.info.find_children('styles'):
            sty_id = prop.name.upper()

            if not prop.has_children():
                # Style lookup.
                try:
                    prop = next(data.info.find_all('styles', prop.value))
                except StopIteration:
                    raise ValueError('No style <{}>!'.format(prop.value))

            world_tex = prop['world', '']
            overlay_tex = prop['overlay', '']

            # Don't warn, we don't actually need this yet.

            # if not world_tex:
            #     LOGGER.warning(
            #         '"{}" signage has no "world" value '
            #         'for the "{}" style!',
            #         data.id,
            #         sty_id
            #     )
            if not overlay_tex:
                raise ValueError(
                    f'"{data.id}"" signage has no "overlay" value '
                    f'option for the "{sty_id}" style!'
                )

            if 'icon' in prop:
                img = ImgHandle.parse(prop, data.pak_id, 64, 64, subkey='icon')
            else:
                # Use the overlay texture.
                overlay_path = overlay_tex
                if not overlay_path.casefold().endswith('.vtf'):
                    overlay_path += '.vtf'
                img = ImgHandle.file(utils.PackagePath(data.pak_id, overlay_path), 64, 64)

            styles[sty_id] = SignStyle(
                world_tex,
                overlay_tex,
                img,
                prop['type', 'square']
            )
        return cls(
            data.id,
            styles,
            data.info['name'],
            data.info['primary', None],
            data.info['secondary', None],
            data.info.bool('hidden'),
        )
예제 #5
0
 def sprite(cls, path: str, width: int = 0, height: int = 0) -> Handle[utils.PackagePath]:
     """Shortcut for getting a handle to a builtin UI image, but with nearest-neighbour rescaling."""
     return cls._get(TYP_BUILTIN_SPR, utils.PackagePath('<bee2>', path + '.png'), width, height)
예제 #6
0
 def builtin(cls, path: str, width: int = 0, height: int = 0) -> Handle[utils.PackagePath]:
     """Shortcut for getting a handle to a builtin UI image."""
     return cls._get(TYP_BUILTIN, utils.PackagePath('<bee2>', path + '.png'), width, height)
예제 #7
0
        img = img_list.pop()
    except IndexError:
        img = ImageTk.PhotoImage('RGBA', (width, height))
    return img


def _discard_tk_img(img: ImageTk.PhotoImage | None) -> None:
    """Store an unused image so it can be reused."""
    if img is not None:
        # Use setdefault and append so each step is atomic.
        img_list = _unused_tk_img.setdefault((img.width(), img.height()), [])
        img_list.append(img)


# Special paths which map to various images.
PATH_BLANK = utils.PackagePath('<special>', 'blank')
PATH_ERROR = utils.PackagePath('<special>', 'error')
PATH_LOAD = utils.PackagePath('<special>', 'load')
PATH_NONE = utils.PackagePath('<special>', 'none')
PATH_BG = utils.PackagePath('color', PETI_ITEM_BG_HEX[1:])
PATH_BLACK = utils.PackagePath('<color>', '000')
PATH_WHITE = utils.PackagePath('<color>', 'fff')


class ImageType(Generic[ArgT]):
    """Represents a kind of image that can be loaded or generated.

    This contains callables for generating a PIL image from a specified
    arg type, width and height.
    """
    def __init__(
예제 #8
0
def parse_item_folder(
    folders_to_parse: set[str],
    filesystem: FileSystem,
    pak_id: str,
) -> dict[str, ItemVariant]:
    """Parse through the data in item/ folders.

    folders is a dict, with the keys set to the folder names we want.
    The values will be filled in with itemVariant values
    """
    folders: dict[str, ItemVariant] = {}
    for fold in folders_to_parse:
        prop_path = 'items/' + fold + '/properties.txt'
        editor_path = 'items/' + fold + '/editoritems.txt'
        config_path = 'items/' + fold + '/vbsp_config.cfg'

        first_item: EditorItem | None = None
        extra_items: list[EditorItem] = []
        try:
            props = filesystem.read_prop(prop_path).find_key('Properties')
            f = filesystem[editor_path].open_str()
        except FileNotFoundError as err:
            raise IOError('"' + pak_id + ':items/' + fold + '" not valid! '
                          'Folder likely missing! ') from err
        with f:
            tok = Tokenizer(f, editor_path)
            for tok_type, tok_value in tok:
                if tok_type is Token.STRING:
                    if tok_value.casefold() != 'item':
                        raise tok.error('Unknown item option "{}"!', tok_value)
                    if first_item is None:
                        first_item = EditorItem.parse_one(tok)
                    else:
                        extra_items.append(EditorItem.parse_one(tok))
                elif tok_type is not Token.NEWLINE:
                    raise tok.error(tok_type)

        if first_item is None:
            raise ValueError(f'"{pak_id}:items/{fold}/editoritems.txt has no '
                             '"Item" block!')

        try:
            editor_vmf = VMF.parse(
                filesystem.read_prop(editor_path[:-3] + 'vmf'))
        except FileNotFoundError:
            pass
        else:
            editoritems_vmf.load(first_item, editor_vmf)
        first_item.generate_collisions()

        # extra_items is any extra blocks (offset catchers, extent items).
        # These must not have a palette section - it'll override any the user
        # chooses.
        for extra_item in extra_items:
            extra_item.generate_collisions()
            for subtype in extra_item.subtypes:
                if subtype.pal_pos is not None:
                    LOGGER.warning(
                        f'"{pak_id}:items/{fold}/editoritems.txt has '
                        f'palette set for extra item blocks. Deleting.')
                    subtype.pal_icon = subtype.pal_pos = subtype.pal_name = None

        # In files this is specified as PNG, but it's always really VTF.
        try:
            all_icon = FSPath(props['all_icon']).with_suffix('.vtf')
        except LookupError:
            all_icon = None

        folders[fold] = ItemVariant(
            editoritems=first_item,
            editor_extra=extra_items,

            # Add the folder the item definition comes from,
            # so we can trace it later for debug messages.
            source=f'<{pak_id}>/items/{fold}',
            pak_id=pak_id,
            vbsp_config=lazy_conf.BLANK,
            authors=sep_values(props['authors', '']),
            tags=sep_values(props['tags', '']),
            desc=desc_parse(props, f'{pak_id}:{prop_path}', pak_id),
            ent_count=props['ent_count', ''],
            url=props['infoURL', None],
            icons={
                prop.name: img.Handle.parse(
                    prop,
                    pak_id,
                    64,
                    64,
                    subfolder='items',
                )
                for prop in props.find_children('icon')
            },
            all_name=props['all_name', None],
            all_icon=all_icon,
        )

        if Item.log_ent_count and not folders[fold].ent_count:
            LOGGER.warning(
                '"{id}:{path}" has missing entity count!',
                id=pak_id,
                path=prop_path,
            )

        # If we have one of the grouping icon definitions but not both required
        # ones then notify the author.
        has_name = folders[fold].all_name is not None
        has_icon = folders[fold].all_icon is not None
        if (has_name or has_icon or 'all'
                in folders[fold].icons) and (not has_name or not has_icon):
            LOGGER.warning(
                'Warning: "{id}:{path}" has incomplete grouping icon '
                'definition!',
                id=pak_id,
                path=prop_path,
            )
        folders[fold].vbsp_config = lazy_conf.from_file(
            utils.PackagePath(pak_id, config_path),
            missing_ok=True,
            source=folders[fold].source,
        )
    return folders
예제 #9
0
    async def parse(cls, data: ParseData):
        """Parse a style definition."""
        info = data.info
        selitem_data = SelitemData.parse(info, data.pak_id)
        base = info['base', '']
        has_video = srctools.conv_bool(
            info['has_video', ''],
            not data.is_override,  # Assume no video for override
        )
        vpk_name = info['vpk_name', ''].casefold()

        sugg: dict[str, set[str]] = {
            'quote': set(),
            'music': set(),
            'skybox': set(),
            'elev': set(),
        }
        for prop in info.find_children('suggested'):
            try:
                sugg[prop.name].add(prop.value)
            except KeyError:
                LOGGER.warning('Unknown suggestion types for style {}: {}',
                               data.id, prop.name)

        sugg_tup = (
            sugg['quote'],
            sugg['music'],
            sugg['skybox'],
            sugg['elev'],
        )

        corr_conf = info.find_key('corridors', or_blank=True)
        corridors = {}

        icon_folder = corr_conf['icon_folder', '']

        for group, length in CORRIDOR_COUNTS.items():
            group_prop = corr_conf.find_key(group, or_blank=True)
            for i in range(1, length + 1):
                prop = group_prop.find_key(str(i), '')

                if icon_folder:
                    icon = utils.PackagePath(
                        data.pak_id,
                        'corr/{}/{}/{}.jpg'.format(icon_folder, group, i))
                else:
                    icon = img.PATH_BLANK

                if prop.has_children():
                    corridors[group, i] = CorrDesc(
                        name=prop['name', ''],
                        icon=prop['icon', icon],
                        desc=prop['Desc', ''],
                    )
                else:
                    corridors[group, i] = CorrDesc(
                        name=prop.value,
                        icon=icon,
                        desc='',
                    )

        if base == '':
            base = None
        try:
            folder = 'styles/' + info['folder']
        except LookupError:
            # It's OK for override styles to be missing their 'folder'
            # value.
            if data.is_override:
                items = []
                renderables = {}
                vbsp = lazy_conf.BLANK
            else:
                raise ValueError(
                    f'Style "{data.id}" missing configuration folder!')
        else:
            with data.fsys[folder + '/items.txt'].open_str() as f:
                items, renderables = await trio.to_thread.run_sync(
                    EditorItem.parse, f)
            vbsp = lazy_conf.from_file(
                utils.PackagePath(data.pak_id, folder + '/vbsp_config.cfg'),
                missing_ok=True,
                source=f'Style <{data.id}>',
            )

        return cls(
            style_id=data.id,
            selitem_data=selitem_data,
            items=items,
            renderables=renderables,
            config=vbsp,
            base_style=base,
            suggested=sugg_tup,
            has_video=has_video,
            corridors=corridors,
            vpk_name=vpk_name,
        )