Example #1
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,
        )
Example #2
0
    def parse(cls, data: ParseData) -> 'QuotePack':
        """Parse a voice line definition."""
        selitem_data = SelitemData.parse(data.info)
        chars = {
            char.strip()
            for char in data.info['characters', ''].split(',') if char.strip()
        }

        # For Cave Johnson voicelines, this indicates what skin to use on the
        # portrait.
        port_skin = srctools.conv_int(data.info['caveSkin', None], None)

        try:
            monitor_data = data.info.find_key('monitor')
        except NoKeyError:
            mon_studio = mon_cam_loc = None
            mon_interrupt = mon_cam_pitch = mon_cam_yaw = 0
            mon_studio_actor = ''
            turret_hate = False
        else:
            mon_studio = monitor_data['studio']
            mon_studio_actor = monitor_data['studio_actor', '']
            mon_interrupt = monitor_data.float('interrupt_chance', 0)
            mon_cam_loc = monitor_data.vec('Cam_loc')
            mon_cam_pitch, mon_cam_yaw, _ = monitor_data.vec('Cam_angles')
            turret_hate = monitor_data.bool('TurretShoot')

        config = get_config(
            data.info,
            data.fsys,
            'voice',
            pak_id=data.pak_id,
            prop_name='file',
        )

        return cls(
            data.id,
            selitem_data,
            config,
            chars=chars,
            skin=port_skin,
            studio=mon_studio,
            studio_actor=mon_studio_actor,
            interrupt=mon_interrupt,
            cam_loc=mon_cam_loc,
            cam_pitch=mon_cam_pitch,
            cam_yaw=mon_cam_yaw,
            turret_hate=turret_hate,
        )
Example #3
0
 def parse(cls, data: ParseData):
     """Read templates from a package."""
     file = get_config(
         prop_block=data.info,
         fsys=data.fsys,
         folder='templates',
         pak_id=data.pak_id,
         prop_name='file',
         extension='.vmf',
     )
     file = VMF.parse(file)
     return cls(
         data.id,
         file,
         force=data.info['force', ''],
         keep_brushes=srctools.conv_bool(data.info['keep_brushes', '1'],
                                         True),
     )
Example #4
0
    async def parse(cls, data: ParseData):
        """Parse a skybox definition."""
        selitem_data = SelitemData.parse(data.info, data.pak_id)
        mat = data.info['material', 'sky_black']
        config = get_config(
            data.info,
            'skybox',
            pak_id=data.pak_id,
            source=f'Skybox <{data.id}>',
        )

        fog_opts = data.info.find_key("Fog", or_blank=True)

        return cls(
            data.id,
            selitem_data,
            config,
            fog_opts,
            mat,
        )
Example #5
0
    def parse(cls, data: ParseData):
        """Parse a skybox definition."""
        selitem_data = SelitemData.parse(data.info)
        mat = data.info['material', 'sky_black']
        config = get_config(
            data.info,
            data.fsys,
            'skybox',
            pak_id=data.pak_id,
        )

        fog_opts = data.info.find_key("Fog", [])

        return cls(
            data.id,
            selitem_data,
            config,
            fog_opts,
            mat,
        )
Example #6
0
    def parse(cls, data: ParseData):
        """Parse from config files."""
        filesystem = data.fsys  # type: FileSystem
        vers = {}

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

        with filesystem:
            for ver in data.info.find_all('Version'):  # type: Property
                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] = conf = filesystem.read_prop(
                            'items/' + style.value + '.cfg')

                        set_cond_source(
                            conf, "<ItemConfig {}:{} in '{}'>".format(
                                data.pak_id,
                                data.id,
                                style.real_name,
                            ))

        return cls(
            data.id,
            all_config,
            vers,
        )
Example #7
0
    async def parse(cls, data: ParseData):
        """Parse an item definition."""
        versions: dict[str, Version] = {}
        def_version: Version | None = None
        # The folders we parse for this - we don't want to parse the same
        # one twice.
        folders_to_parse: set[str] = set()
        unstyled = data.info.bool('unstyled')

        glob_desc = desc_parse(data.info, 'global:' + data.id, data.pak_id)
        desc_last = data.info.bool('AllDescLast')

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

        for ver in data.info.find_all('version'):
            ver_name = ver['name', 'Regular']
            ver_id = ver['ID', 'VER_DEFAULT']
            styles: dict[str, ItemVariant] = {}
            ver_isolate = ver.bool('isolated')
            def_style = None

            for style in ver.find_children('styles'):
                if style.has_children():
                    folder = UnParsedItemVariant(
                        data.pak_id,
                        data.fsys,
                        folder=style['folder', None],
                        style=style['Base', ''],
                        config=style,
                    )

                elif style.value.startswith('<') and style.value.endswith('>'):
                    # Reusing another style unaltered using <>.
                    folder = UnParsedItemVariant(
                        data.pak_id,
                        data.fsys,
                        style=style.value[1:-1],
                        folder=None,
                        config=None,
                    )
                else:
                    # Reference to the actual folder...
                    folder = UnParsedItemVariant(
                        data.pak_id,
                        data.fsys,
                        folder=style.value,
                        style=None,
                        config=None,
                    )
                # We need to parse the folder now if set.
                if folder.folder:
                    folders_to_parse.add(folder.folder)

                # The first style is considered the 'default', and is used
                # if not otherwise present.
                # We set it to the name, then lookup later in setup_style_tree()
                if def_style is None:
                    def_style = style.real_name
                # It'll only be UnParsed during our parsing.
                styles[style.real_name] = cast(ItemVariant, folder)

                if style.real_name == folder.style:
                    raise ValueError(
                        f'Item "{data.id}"\'s "{style.real_name}" style '
                        "can't inherit from itself!")
            versions[ver_id] = version = Version(
                ver_id,
                ver_name,
                ver_isolate,
                styles,
                def_style,
            )

            # The first version is the 'default',
            # so non-isolated versions will fallback to it.
            # But the default is isolated itself.
            if def_version is None:
                def_version = version
                version.isolate = True

        if def_version is None:
            raise ValueError(f'Item "{data.id}" has no versions!')

        # Fill out the folders dict with the actual data
        parsed_folders = parse_item_folder(folders_to_parse, data.fsys,
                                           data.pak_id)

        # We want to ensure the number of visible subtypes doesn't change.
        subtype_counts = {
            tuple([
                i for i, subtype in enumerate(folder.editor.subtypes, 1)
                if subtype.pal_pos or subtype.pal_name
            ])
            for folder in parsed_folders.values()
        }
        if len(subtype_counts) > 1:
            raise ValueError(
                f'Item "{data.id}" has different '
                f'visible subtypes in its styles: {", ".join(map(str, subtype_counts))}'
            )

        # Then copy over to the styles values
        for ver in versions.values():
            if isinstance(ver.def_style, str):
                try:
                    ver.def_style = parsed_folders[ver.def_style]
                except KeyError:
                    pass
            for sty, fold in ver.styles.items():
                if isinstance(fold, str):
                    ver.styles[sty] = parsed_folders[fold]

        return cls(
            data.id,
            versions=versions,
            def_version=def_version,
            needs_unlock=data.info.bool('needsUnlock'),
            isolate_versions=data.info.bool('isolate_versions'),
            all_conf=all_config,
            unstyled=unstyled,
            glob_desc=glob_desc,
            desc_last=desc_last,
            # Add filesystem to individualise this to the package.
            folders={(data.fsys, folder): item_variant
                     for folder, item_variant in parsed_folders.items()})
Example #8
0
    async def modify(self, pak_id: str, props: Property,
                     source: str) -> ItemVariant:
        """Apply a config to this item variant.

        This produces a copy with various modifications - switching
        out palette or instance values, changing the config, etc.
        """
        vbsp_config: lazy_conf.LazyConf
        if 'config' in props:
            # Item.parse() has resolved this to the actual config.
            vbsp_config = get_config(
                props,
                'items',
                pak_id,
            )
        else:
            vbsp_config = self.vbsp_config

        if 'replace' in props:
            # Replace property values in the config via regex.
            vbsp_config = lazy_conf.replace(
                vbsp_config,
                [(re.compile(prop.real_name, re.IGNORECASE), prop.value)
                 for prop in props.find_children('Replace')])

        vbsp_config = lazy_conf.concat(
            vbsp_config,
            get_config(
                props,
                'items',
                pak_id,
                prop_name='append',
            ))

        if 'description' in props:
            desc = desc_parse(props, source, pak_id)
        else:
            desc = self.desc.copy()

        if 'appenddesc' in props:
            desc = tkMarkdown.join(
                desc,
                desc_parse(props, source, pak_id, prop_name='appenddesc'),
            )

        if 'authors' in props:
            authors = sep_values(props['authors', ''])
        else:
            authors = self.authors

        if 'tags' in props:
            tags = sep_values(props['tags', ''])
        else:
            tags = self.tags.copy()

        variant = ItemVariant(
            pak_id,
            self.editor,
            vbsp_config,
            self.editor_extra.copy(),
            authors=authors,
            tags=tags,
            desc=desc,
            icons=self.icons.copy(),
            ent_count=props['ent_count', self.ent_count],
            url=props['url', self.url],
            all_name=self.all_name,
            all_icon=self.all_icon,
            source=f'{source} from {self.source}',
        )
        [variant.editor] = variant._modify_editoritems(
            props,
            [variant.editor],
            pak_id,
            source,
            is_extra=False,
        )

        if 'extra' in props:
            variant.editor_extra = variant._modify_editoritems(
                props.find_key('extra'),
                variant.editor_extra,
                pak_id,
                source,
                is_extra=True)

        return variant
Example #9
0
    def modify(self, fsys: FileSystem, props: Property,
               source: str) -> 'ItemVariant':
        """Apply a config to this item variant.

        This produces a copy with various modifications - switching
        out palette or instance values, changing the config, etc.
        """
        if 'config' in props:
            # Item.parse() has resolved this to the actual config.
            vbsp_config = get_config(
                props,
                fsys,
                'items',
                pak_id=fsys.path,
            )
        else:
            vbsp_config = self.vbsp_config.copy()

        if 'replace' in props:
            # Replace property values in the config via regex.
            replace_vals = [(re.compile(prop.real_name,
                                        re.IGNORECASE), prop.value)
                            for prop in props.find_children('Replace')]
            for prop in vbsp_config.iter_tree():
                for regex, sub in replace_vals:
                    prop.name = regex.sub(sub, prop.real_name)
                    prop.value = regex.sub(sub, prop.value)

        vbsp_config += list(
            get_config(
                props,
                fsys,
                'items',
                prop_name='append',
                pak_id=fsys.path,
            ))

        if 'description' in props:
            desc = desc_parse(props, source)
        else:
            desc = self.desc.copy()

        if 'appenddesc' in props:
            desc = tkMarkdown.join(
                desc,
                desc_parse(props, source, prop_name='appenddesc'),
            )

        if 'authors' in props:
            authors = sep_values(props['authors', ''])
        else:
            authors = self.authors

        if 'tags' in props:
            tags = sep_values(props['tags', ''])
        else:
            tags = self.tags.copy()

        variant = ItemVariant(
            self.editor,
            vbsp_config,
            self.editor_extra.copy(),
            authors=authors,
            tags=tags,
            desc=desc,
            icons=self.icons.copy(),
            ent_count=props['ent_count', self.ent_count],
            url=props['url', self.url],
            all_name=self.all_name,
            all_icon=self.all_icon,
            source='{} from {}'.format(source, self.source),
        )
        [variant.editor] = variant._modify_editoritems(
            props,
            [variant.editor],
            source,
            is_extra=False,
        )

        if 'extra' in props:
            variant.editor_extra = variant._modify_editoritems(
                props.find_key('extra'),
                variant.editor_extra,
                source,
                is_extra=True)

        return variant
Example #10
0
    async def parse(cls, data: ParseData):
        """Parse a music definition."""
        selitem_data = SelitemData.parse(data.info, data.pak_id)
        inst = data.info['instance', None]
        sound = data.info.find_key('soundscript', or_blank=True)

        if sound.has_children():
            sounds = {}
            for channel in MusicChannel:
                sounds[channel] = channel_snd = []
                for prop in sound.find_all(channel.value):
                    channel_snd.extend(prop.as_array())

            synch_tbeam = sound.bool('sync_funnel')
        else:
            # Only base.
            sounds = {
                channel: []
                for channel in
                MusicChannel
            }
            sounds[MusicChannel.BASE] = [sound.value]
            synch_tbeam = False

        # The sample music file to play, if found.
        sample_block = data.info.find_key('sample', '')
        if sample_block.has_children():
            sample: dict[MusicChannel, str | None] = {}
            for channel in MusicChannel:
                chan_sample = sample[channel] = sample_block[channel.value, '']
                if chan_sample:
                    zip_sample = (
                        'resources/music_samp/' +
                        chan_sample
                    )
                    if zip_sample not in data.fsys:
                        LOGGER.warning(
                            'Music sample for <{}>{} does not exist in zip: "{}"',
                            data.id,
                            ('' if
                             channel is MusicChannel.BASE
                             else f' ({channel.value})'),
                            zip_sample,
                        )
                else:
                    sample[channel] = None
        else:
            # Single value, fill it into all channels.
            sample = {
                channel: sample_block.value
                for channel in MusicChannel
            }

        snd_length = data.info['loop_len', '0']
        if ':' in snd_length:
            # Allow specifying lengths as min:sec.
            minute, second = snd_length.split(':')
            snd_length = 60 * srctools.conv_int(minute) + srctools.conv_int(second)
        else:
            snd_length = srctools.conv_int(snd_length)

        packfiles = [
            prop.value
            for prop in
            data.info.find_all('pack')
        ]

        children_prop = data.info.find_block('children', or_blank=True)
        children = {
            channel: children_prop[channel.value, '']
            for channel in MusicChannel
            if channel is not MusicChannel.BASE
        }

        config = get_config(
            data.info,
            'music',
            pak_id=data.pak_id,
            source=f'Music <{data.id}>',
        )
        return cls(
            data.id,
            selitem_data,
            sounds,
            children,
            inst=inst,
            sample=sample,
            config=config,
            pack=packfiles,
            loop_len=snd_length,
            synch_tbeam=synch_tbeam,
        )