def __init__( self, quote_id, selitem_data: 'SelitemData', config: Property, chars: Optional[Set[str]] = None, skin: Optional[int] = None, studio: str = None, studio_actor: str = '', cam_loc: Vec = None, turret_hate: bool = False, interrupt: float = 0.0, cam_pitch: float = 0.0, cam_yaw: float = 0.0, ) -> None: self.id = quote_id self.selitem_data = selitem_data self.cave_skin = skin self.config = config set_cond_source(config, 'QuotePack <{}>'.format(quote_id)) self.chars = chars or {'??'} self.studio = studio self.studio_actor = studio_actor self.cam_loc = cam_loc self.inter_chance = interrupt self.cam_pitch = cam_pitch self.cam_yaw = cam_yaw self.turret_hate = turret_hate
def __init__( self, music_id, selitem_data: 'SelitemData', sound: Dict[MusicChannel, List[str]], children: Dict[MusicChannel, str], config: Property = None, inst=None, sample: Dict[MusicChannel, Optional[str]] = None, pack=(), loop_len=0, synch_tbeam=False, ): self.id = music_id self.config = config or Property(None, []) self.children = children set_cond_source(config, 'Music <{}>'.format(music_id)) self.inst = inst self.sound = sound self.packfiles = list(pack) self.len = loop_len self.sample = sample self.selitem_data = selitem_data self.has_synced_tbeam = synch_tbeam
def loader() -> Property: """Load and parse the specified file when called.""" try: with file.open_str() as f: props = Property.parse(f) except (KeyValError, FileNotFoundError, UnicodeDecodeError): LOGGER.exception('Unable to read "{}"', path) raise if source: packages.set_cond_source(props, source) return props
def __init__( self, sky_id, selitem_data: SelitemData, config: Property, fog_opts: Property, mat, ) -> None: self.id = sky_id self.selitem_data = selitem_data self.material = mat self.config = config set_cond_source(config, 'Skybox <{}>'.format(sky_id)) self.fog_opts = fog_opts # Extract this for selector windows to easily display self.fog_color = fog_opts.vec('primarycolor', 255, 255, 255)
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, )
def copier() -> Property: """Copy the config, then apply the source.""" copy = block.copy() packages.set_cond_source(copy, source) return copy
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: Optional[Item] = None extra_items: List[EditorItem] = [] with filesystem: 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('"{}:items/{}/editoritems.txt has no ' '"Item" block!'.format(pak_id, fold)) # 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: for subtype in extra_item.subtypes: if subtype.pal_pos is not None: LOGGER.warning( '"{}:items/{}/editoritems.txt has palette set for extra' ' item blocks. Deleting.'.format(pak_id, fold)) subtype.pal_icon = subtype.pal_pos = subtype.pal_name = None try: all_icon = FSPath(props['all_icon']) 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='<{}>/items/{}'.format(pak_id, fold), vbsp_config=Property(None, []), authors=sep_values(props['authors', '']), tags=sep_values(props['tags', '']), desc=desc_parse(props, pak_id + ':' + prop_path), ent_count=props['ent_count', ''], url=props['infoURL', None], icons={p.name: p.value for p in props['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 at least 1, but not all of the grouping icon # definitions then notify the author. num_group_parts = ((folders[fold].all_name is not None) + (folders[fold].all_icon is not None) + ('all' in folders[fold].icons)) if 0 < num_group_parts < 3: LOGGER.warning( 'Warning: "{id}:{path}" has incomplete grouping icon ' 'definition!', id=pak_id, path=prop_path, ) try: with filesystem: folders[fold].vbsp_config = conf = filesystem.read_prop( config_path, ) except FileNotFoundError: folders[fold].vbsp_config = conf = Property(None, []) set_cond_source(conf, folders[fold].source) return folders
def parse(cls, data: ParseData): """Parse an item definition.""" versions: Dict[str, Version] = {} def_version: Optional[Version] = 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) desc_last = data.info.bool('AllDescLast') all_config = get_config( data.info, data.fsys, 'items', pak_id=data.pak_id, prop_name='all_conf', ) set_cond_source(all_config, '<Item {} all_conf>'.format(data.id, )) 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.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.fsys, style=style.value[1:-1], folder=None, config=None, ) else: # Reference to the actual folder... folder = UnParsedItemVariant( 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('Item "{}"\'s "{}" style ' 'can\'t inherit from itself!'.format( data.id, style.real_name, )) 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()})