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, )
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, )
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), )
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, )
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, )
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, )
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()})
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
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
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, )