def parse_item_folder(folders, zip_file, pak_id): for fold in folders: prop_path = 'items/' + fold + '/properties.txt' editor_path = 'items/' + fold + '/editoritems.txt' config_path = 'items/' + fold + '/vbsp_config.cfg' try: with zip_file.open(prop_path, 'r') as prop_file: props = Property.parse( prop_file, pak_id + ':' + prop_path, ).find_key('Properties') with zip_file.open(editor_path, 'r') as editor_file: editor = Property.parse(editor_file, pak_id + ':' + editor_path) except KeyError as err: # Opening the files failed! raise IOError('"' + pak_id + ':items/' + fold + '" not valid!' 'Folder likely missing! ') from err editor_iter = Property.find_all(editor, 'Item') folders[fold] = { 'auth': sep_values(props['authors', '']), 'tags': sep_values(props['tags', '']), 'desc': list(desc_parse(props)), 'ent': 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': props['all_icon', None], 'vbsp': Property(None, []), # The first Item block found 'editor': next(editor_iter), # Any extra blocks (offset catchers, extent items) 'editor_extra': list(editor_iter), } if LOG_ENT_COUNT and folders[fold]['ent'] == '??': print('Warning: "{}:{}" has missing entity count!'.format( pak_id, 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: print('Warning: "{}:{}" has incomplete grouping icon ' 'definition!'.format(pak_id, prop_path)) try: with zip_file.open(config_path, 'r') as vbsp_config: folders[fold]['vbsp'] = Property.parse( vbsp_config, pak_id + ':' + config_path, ) except KeyError: folders[fold]['vbsp'] = Property(None, [])
def build_instance_data(editoritems: Property): """Build a property tree listing all of the instances for each item. as well as another listing the input and output commands. VBSP uses this to reduce duplication in VBSP_config files. This additionally strips custom instance definitions from the original list. """ instance_locs = Property("AllInstances", []) cust_inst = Property("CustInstances", []) commands = Property("Connections", []) root_block = Property(None, [instance_locs, cust_inst, commands]) for item in editoritems.find_all("Item"): instance_block = Property(item['Type'], []) instance_locs.append(instance_block) comm_block = Property(item['Type'], []) for inst_block in item.find_all("Exporting", "instances"): for inst in inst_block.value[:]: # type: Property if inst.name.isdigit(): # Direct Portal 2 value instance_block.append( Property('Instance', inst['Name']) ) else: # It's a custom definition, remove from editoritems inst_block.value.remove(inst) cust_inst.set_key( (item['type'], inst.name), # Allow using either the normal block format, # or just providing the file - we don't use the # other values. inst['name'] if inst.has_children() else inst.value, ) # Look in the Inputs and Outputs blocks to find the io definitions. # Copy them to property names like 'Input_Activate'. for io_type in ('Inputs', 'Outputs'): for block in item.find_all('Exporting', io_type, CONN_NORM): for io_prop in block: comm_block[ io_type[:-1] + '_' + io_prop.real_name ] = io_prop.value # The funnel item type is special, having the additional input type. # Handle that specially. if item['type'] == 'item_tbeam': for block in item.find_all('Exporting', 'Inputs', CONN_FUNNEL): for io_prop in block: comm_block['TBEAM_' + io_prop.real_name] = io_prop.value # Only add the block if the item actually has IO. if comm_block.value: commands.append(comm_block) return root_block.export()
def parse_item_folder(folders, zip_file): for fold in folders: prop_path = 'items/' + fold + '/properties.txt' editor_path = 'items/' + fold + '/editoritems.txt' config_path = 'items/' + fold + '/vbsp_config.cfg' try: with zip_file.open(prop_path, 'r') as prop_file: props = Property.parse( prop_file, prop_path, ).find_key('Properties') with zip_file.open(editor_path, 'r') as editor_file: editor = Property.parse(editor_file, editor_path) except KeyError as err: # Opening the files failed! raise IOError( '"items/' + fold + '" not valid!' 'Folder likely missing! ' ) from err editor_iter = Property.find_all(editor, 'Item') folders[fold] = { 'auth': sep_values(props['authors', '']), 'tags': sep_values(props['tags', '']), 'desc': list(desc_parse(props)), 'ent': 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': props['all_icon', None], 'vbsp': Property(None, []), # The first Item block found 'editor': next(editor_iter), # Any extra blocks (offset catchers, extent items) 'editor_extra': list(editor_iter), } if LOG_ENT_COUNT and folders[fold]['ent'] == '??': print('Warning: "{}" has missing entity count!'.format(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: print( 'Warning: "{}" has incomplete grouping icon definition!'.format( prop_path) ) try: with zip_file.open(config_path, 'r') as vbsp_config: folders[fold]['vbsp'] = Property.parse(vbsp_config, config_path) except KeyError: folders[fold]['vbsp'] = Property(None, [])
def gen_sound_manifest(additional, excludes): """Generate a new game_sounds_manifest.txt file. This includes all the current scripts defined, plus any custom ones. Excludes is a list of scripts to remove from the listing - this allows overriding the sounds without VPK overrides. """ if not additional: return # Don't pack, there aren't any new sounds.. orig_manifest = os.path.join( '..', SOUND_MAN_FOLDER.get(CONF['game_id', ''], 'portal2'), 'scripts', 'game_sounds_manifest.txt', ) try: with open(orig_manifest) as f: props = Property.parse(f, orig_manifest).find_key( 'game_sounds_manifest', [], ) except FileNotFoundError: # Assume no sounds props = Property('game_sounds_manifest', []) scripts = [prop.value for prop in props.find_all('precache_file')] for script in additional: scripts.append(script) # For our packed scripts, force the game to load them # (we know they're used). scripts.append('!' + script) for script in excludes: try: scripts.remove(script) except ValueError: LOGGER.warning( '"{}" should be excluded, but it\'s' ' not in the manifest already!', script, ) # Build and unbuild it to strip other things out - Valve includes a bogus # 'new_sound_scripts_must_go_below_here' entry.. new_props = Property('game_sounds_manifest', [ Property('precache_file', file) for file in scripts ]) inject_loc = os.path.join('bee2', 'inject', 'soundscript_manifest.txt') with open(inject_loc, 'w') as f: for line in new_props.export(): f.write(line) LOGGER.info('Written new soundscripts_manifest..')
def build_instance_data(editoritems: Property): """Build a property tree listing all of the instances for each item. as well as another listing the input and output commands. VBSP uses this to reduce duplication in VBSP_config files. """ instance_locs = Property("AllInstances", []) commands = Property("Connections", []) root_block = Property(None, [instance_locs, commands]) for item in editoritems.find_all("Item"): instance_block = Property(item['Type'], []) instance_locs.append(instance_block) comm_block = Property(item['Type'], []) for inst_block in item.find_all("Exporting", "instances"): for inst in inst_block: instance_block.append( Property('Instance', inst['Name']) ) # Look in the Inputs and Outputs blocks to find the io definitions. # Copy them to property names like 'Input_Activate'. for io_type in ('Inputs', 'Outputs'): for block in item.find_all('Exporting', io_type, CONN_NORM): for io_prop in block: comm_block[ io_type[:-1] + '_' + io_prop.real_name ] = io_prop.value # The funnel item type is special, having the additional input type. # Handle that specially. if item['type'] == 'item_tbeam': for block in item.find_all('Exporting', 'Inputs', CONN_FUNNEL): for io_prop in block: comm_block['TBEAM_' + io_prop.real_name] = io_prop.value # Only add the block if the item actually has IO. if comm_block.value: commands.append(comm_block) return root_block.export()
def gen_part_manifest(additional): """Generate a new particle system manifest file. This includes all the current ones defined, plus any custom ones. """ if not additional: return # Don't pack, there aren't any new particles.. orig_manifest = os.path.join( '..', GAME_FOLDER.get(CONF['game_id', ''], 'portal2'), 'particles', 'particles_manifest.txt', ) try: with open(orig_manifest) as f: props = Property.parse(f, orig_manifest).find_key( 'particles_manifest', [], ) except FileNotFoundError: # Assume no particles props = Property('particles_manifest', []) parts = [prop.value for prop in props.find_all('file')] for particle in additional: parts.append(particle) # Build and unbuild it to strip comments and similar lines. new_props = Property('particles_manifest', [ Property('file', file) for file in parts ]) inject_loc = os.path.join('bee2', 'inject', 'particles_manifest.txt') with open(inject_loc, 'w') as f: for line in new_props.export(): f.write(line) LOGGER.info('Written new particles_manifest..')
def gen_sound_manifest(additional, has_music=False): """Generate a new game_sounds_manifest.txt file. This includes all the current scripts defined, plus any custom ones. """ orig_manifest = os.path.join( '..', SOUND_MAN_FOLDER.get(CONF['game_id', ''], 'portal2'), 'scripts', 'game_sounds_manifest.txt', ) try: with open(orig_manifest) as f: props = Property.parse(f, orig_manifest).find_key( 'game_sounds_manifest', [], ) except FileNotFoundError: # Assume no sounds props = Property('game_sounds_manifest', []) scripts = [prop.value for prop in props.find_all('precache_file')] for script in additional: scripts.append(script) # Build and unbuild it to strip other things out - Valve includes a bogus # 'new_sound_scripts_must_go_below_here' entry.. new_props = Property('game_sounds_manifest', [ Property('precache_file', file) for file in scripts ]) inject_loc = os.path.join('bee2', 'inject', 'soundscript_manifest.txt') with open(inject_loc, 'w') as f: for line in new_props.export(): f.write(line) LOGGER.info('Written new soundscripts_manifest..')
def export( self, style, all_items, music, skybox, voice, style_vars, elevator, ): """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up - We unlock the mandatory items if specified - """ print('--------------------') print('Exporting Items and Style for "' + self.name + '"!') print('Style =', style) print('Music =', music) print('Voice =', voice) print('Skybox =', skybox) print('Elevator = ', elevator) print('Style Vars:\n {') for key, val in style_vars.items(): print(' {} = {!s}'.format(key, val)) print(' }') vbsp_config = style.config.copy() # Editoritems.txt is composed of a "ItemData" block, holding "Item" and # "Renderables" sections. editoritems = Property("ItemData", *style.editor.find_all('Item')) for item in sorted(all_items): item_block, editor_parts, config_part = all_items[item].export() editoritems += item_block editoritems += editor_parts vbsp_config += config_part if voice is not None: vbsp_config += voice.config if skybox is not None: vbsp_config.set_key( ('Textures', 'Special', 'Sky'), skybox.material, ) vbsp_config += skybox.config if music is not None: if music.sound is not None: vbsp_config.set_key( ('Options', 'music_SoundScript'), music.sound, ) if music.inst is not None: vbsp_config.set_key( ('Options', 'music_instance'), music.inst, ) vbsp_config.set_key(('Options', 'music_ID'), music.id) vbsp_config += music.config vbsp_config.set_key(('Options', 'BEE2_loc'), os.path.dirname(os.getcwd()) # Go up one dir to our actual location ) # If there are multiple of these blocks, merge them together vbsp_config.merge_children('Conditions', 'InstanceFiles', 'Options', 'StyleVars', 'Textures') vbsp_config.ensure_exists('StyleVars') vbsp_config['StyleVars'] += [ Property(key, utils.bool_as_int(val)) for key, val in style_vars.items() ] for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + '_original' + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): print('Backing up original ' + name + '!') shutil.copy(item_path, backup_path) # This is the connections "heart" icon and "error" icon editoritems += style.editor.find_key("Renderables", []) # Build a property tree listing all of the instances for each item all_instances = Property("AllInstances", []) for item in editoritems.find_all("Item"): item_prop = Property(item['Type'], []) all_instances.append(item_prop) for inst_block in item.find_all("Exporting", "instances"): for inst in inst_block: item_prop.append( Property('Instance', inst['Name']) ) if style_vars.get('UnlockDefault', False): print('Unlocking Items!') for item in editoritems.find_all('Item'): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable if item['type', ''] in _UNLOCK_ITEMS: for prop in item.find_key("Editor", []): if prop.name == 'deletable' or prop.name == 'copyable': prop.value = '1' print('Editing Gameinfo!') self.edit_gameinfo(True) print('Writing Editoritems!') os.makedirs(self.abs_path('portal2_dlc2/scripts/'), exist_ok=True) with open(self.abs_path( 'portal2_dlc2/scripts/editoritems.txt'), 'w') as editor_file: for line in editoritems.export(): editor_file.write(line) print('Writing VBSP Config!') os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w') as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) print('Writing instance list!') with open(self.abs_path('bin/bee2/instances.cfg'), 'w') as inst_file: for line in all_instances.export(): inst_file.write(line) print('Copying Custom Compiler!') for file in os.listdir('../compiler'): print('\t* compiler/{0} -> bin/{0}'.format(file)) shutil.copy( os.path.join('../compiler', file), self.abs_path('bin/') ) for prefix, pretty in VOICE_PATHS: path = 'config/voice/{}_{}.cfg'.format(prefix, voice.id) if os.path.isfile(path): shutil.copy( path, self.abs_path('bin/bee2/{}.cfg'.format(prefix)) ) print('Written "{}.cfg"'.format(prefix)) else: print('No ' + pretty + ' voice config!')
def export( self, style, all_items, music, skybox, voice, style_vars, elevator, pack_list, editor_sounds, should_refresh=False, ): """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up. - We unlock the mandatory items if specified. - """ LOGGER.info('-' * 20) LOGGER.info('Exporting Items and Style for "{}"!', self.name) LOGGER.info('Style = {}', style) LOGGER.info('Music = {}', music) LOGGER.info('Voice = {}', voice) LOGGER.info('Skybox = {}', skybox) LOGGER.info('Elevator = {}', elevator) LOGGER.info('Style Vars:') LOGGER.info(' {') for key, val in style_vars.items(): LOGGER.info(' {} = {!s}', key, val) LOGGER.info(' }') LOGGER.info('{} Pack Lists!', len(pack_list)) LOGGER.info('{} Editor Sounds!', len(editor_sounds)) LOGGER.info('-' * 20) # VBSP, VRAD, editoritems export_screen.set_length('BACK', len(FILES_TO_BACKUP)) export_screen.set_length( 'CONF', # VBSP_conf, Editoritems, instances, gameinfo, pack_lists, # editor_sounds, template VMF 7 + # Don't add the voicelines to the progress bar if not selected (0 if voice is None else len(VOICE_PATHS)), ) # files in compiler/ export_screen.set_length('COMP', len(os.listdir('../compiler'))) if should_refresh: export_screen.set_length('RES', extract_packages.res_count) else: export_screen.skip_stage('RES') export_screen.show() export_screen.grab_set_global() # Stop interaction with other windows vbsp_config = style.config.copy() # Editoritems.txt is composed of a "ItemData" block, holding "Item" and # "Renderables" sections. editoritems = Property("ItemData", list(style.editor.find_all('Item'))) for item in sorted(all_items): item_block, editor_parts, config_part = all_items[item].export() editoritems += item_block editoritems += editor_parts vbsp_config += config_part if voice is not None: vbsp_config += voice.config if skybox is not None: vbsp_config.set_key( ('Textures', 'Special', 'Sky'), skybox.material, ) vbsp_config += skybox.config if style.has_video: if elevator is None: # Use a randomised video vbsp_config.set_key( ('Elevator', 'type'), 'RAND', ) elif elevator.id == 'VALVE_BLUESCREEN': # This video gets a special script and handling vbsp_config.set_key( ('Elevator', 'type'), 'BSOD', ) else: # Use the particular selected video vbsp_config.set_key( ('Elevator', 'type'), 'FORCE', ) vbsp_config.set_key( ('Elevator', 'horiz'), elevator.horiz_video, ) vbsp_config.set_key( ('Elevator', 'vert'), elevator.vert_video, ) else: # No elevator video for this style vbsp_config.set_key( ('Elevator', 'type'), 'NONE', ) if music is not None: if music.sound is not None: vbsp_config.set_key( ('Options', 'music_SoundScript'), music.sound, ) if music.inst is not None: vbsp_config.set_key( ('Options', 'music_instance'), music.inst, ) if music.packfiles: vbsp_config.set_key( ('PackTriggers', 'Forced'), [ Property('File', file) for file in music.packfiles ], ) vbsp_config.set_key(('Options', 'music_ID'), music.id) vbsp_config += music.config if voice is not None: vbsp_config.set_key( ('Options', 'voice_pack'), voice.id, ) vbsp_config.set_key( ('Options', 'voice_char'), ','.join(voice.chars) ) if voice.cave_skin is not None: vbsp_config.set_key( ('Options', 'cave_port_skin'), voice.cave_skin, ) vbsp_config.set_key( ('Options', 'BEE2_loc'), os.path.dirname(os.getcwd()) # Go up one dir to our actual location ) vbsp_config.set_key( ('Options', 'Game_ID'), self.steamID, ) vbsp_config.ensure_exists('StyleVars') vbsp_config['StyleVars'] += [ Property(key, utils.bool_as_int(val)) for key, val in style_vars.items() ] pack_block = Property('PackList', []) # A list of materials which will casue a specific packlist to be used. pack_triggers = Property('PackTriggers', []) for key, pack in pack_list.items(): pack_block.append(Property( key, [ Property('File', file) for file in pack.files ] )) for trigger_mat in pack.trigger_mats: pack_triggers.append( Property('Material', [ Property('Texture', trigger_mat), Property('PackList', pack.id), ]) ) if pack_triggers.value: vbsp_config.append(pack_triggers) # If there are multiple of these blocks, merge them together # They will end up in this order. vbsp_config.merge_children( 'Textures', 'Fizzler', 'Options', 'StyleVars', 'Conditions', 'Voice', 'PackTriggers', ) for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + '_original' + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): LOGGER.info('Backing up original {}!', name) shutil.copy(item_path, backup_path) export_screen.step('BACK') # Backup puzzles, if desired backup.auto_backup(selected_game, export_screen) # This is the connections "heart" icon and "error" icon editoritems += style.editor.find_key("Renderables", []) if style_vars.get('UnlockDefault', False): LOGGER.info('Unlocking Items!') for item in editoritems.find_all('Item'): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable # Also add DESIRES_UP, so they place in the correct orientation if item['type', ''] in _UNLOCK_ITEMS: editor_section = item.find_key("Editor", []) editor_section['deletable'] = '1' editor_section['copyable'] = '1' editor_section['DesiredFacing'] = 'DESIRES_UP' LOGGER.info('Editing Gameinfo!') self.edit_gameinfo(True) export_screen.step('CONF') LOGGER.info('Writing Editoritems!') os.makedirs(self.abs_path('portal2_dlc2/scripts/'), exist_ok=True) with open(self.abs_path( 'portal2_dlc2/scripts/editoritems.txt'), 'w') as editor_file: for line in editoritems.export(): editor_file.write(line) export_screen.step('CONF') LOGGER.info('Writing VBSP Config!') os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w') as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) export_screen.step('CONF') LOGGER.info('Writing instance list!') with open(self.abs_path('bin/bee2/instances.cfg'), 'w') as inst_file: for line in self.build_instance_data(editoritems): inst_file.write(line) export_screen.step('CONF') LOGGER.info('Writing packing list!') with open(self.abs_path('bin/bee2/pack_list.cfg'), 'w') as pack_file: for line in pack_block.export(): pack_file.write(line) export_screen.step('CONF') LOGGER.info('Editing game_sounds!') self.add_editor_sounds(editor_sounds.values()) export_screen.step('CONF') LOGGER.info('Exporting {} templates!', len(packageLoader.data['BrushTemplate']) ) with open(self.abs_path('bin/bee2/templates.vmf'), 'w') as temp_file: packageLoader.TEMPLATE_FILE.export(temp_file) export_screen.step('CONF') if voice is not None: for prefix, dest, pretty in VOICE_PATHS: path = os.path.join( os.getcwd(), '..', 'config', 'voice', prefix + voice.id + '.cfg', ) LOGGER.info(path) if os.path.isfile(path): shutil.copy( path, self.abs_path('bin/bee2/{}voice.cfg'.format(dest)) ) LOGGER.info('Written "{}voice.cfg"', dest) else: LOGGER.info('No {} voice config!', pretty) export_screen.step('CONF') LOGGER.info('Copying Custom Compiler!') for file in os.listdir('../compiler'): LOGGER.info('\t* compiler/{0} -> bin/{0}', file) try: shutil.copy( os.path.join('../compiler', file), self.abs_path('bin/') ) except PermissionError: # We might not have permissions, if the compiler is currently # running. export_screen.grab_release() export_screen.reset() messagebox.showerror( title='BEE2 - Export Failed!', message='Copying compiler file {file} failed.' 'Ensure the {game} is not running.'.format( file=file, game=self.name, ), master=TK_ROOT, ) return False export_screen.step('COMP') if should_refresh: LOGGER.info('Copying Resources!') self.refresh_cache() export_screen.grab_release() export_screen.reset() # Hide loading screen, we're done return True
def export( self, style, all_items, music, skybox, voice, style_vars, elevator, pack_list, editor_sounds, should_refresh=False, ): """Generate the editoritems.txt and vbsp_config. - If no backup is present, the original editoritems is backed up - We unlock the mandatory items if specified - """ print('-' * 20) print('Exporting Items and Style for "' + self.name + '"!') print('Style =', style) print('Music =', music) print('Voice =', voice) print('Skybox =', skybox) print('Elevator = ', elevator) print('Style Vars:\n {') for key, val in style_vars.items(): print(' {} = {!s}'.format(key, val)) print(' }') print(len(pack_list), 'Pack Lists!') print(len(editor_sounds), 'Editor Sounds!') print('-' * 20) # VBSP, VRAD, editoritems export_screen.set_length('BACK', len(FILES_TO_BACKUP)) export_screen.set_length( 'CONF', # VBSP_conf, Editoritems, instances, gameinfo, pack_lists, # editor_sounds 6 + # Don't add the voicelines to the progress bar if not selected (0 if voice is None else len(VOICE_PATHS)), ) # files in compiler/ export_screen.set_length('COMP', len(os.listdir('../compiler'))) if should_refresh: export_screen.set_length('RES', extract_packages.res_count) else: export_screen.skip_stage('RES') export_screen.show() export_screen.grab_set_global() # Stop interaction with other windows vbsp_config = style.config.copy() # Editoritems.txt is composed of a "ItemData" block, holding "Item" and # "Renderables" sections. editoritems = Property("ItemData", list(style.editor.find_all('Item'))) for item in sorted(all_items): item_block, editor_parts, config_part = all_items[item].export() editoritems += item_block editoritems += editor_parts vbsp_config += config_part if voice is not None: vbsp_config += voice.config if skybox is not None: vbsp_config.set_key( ('Textures', 'Special', 'Sky'), skybox.material, ) vbsp_config += skybox.config if style.has_video: if elevator is None: # Use a randomised video vbsp_config.set_key( ('Elevator', 'type'), 'RAND', ) elif elevator.id == 'VALVE_BLUESCREEN': # This video gets a special script and handling vbsp_config.set_key( ('Elevator', 'type'), 'BSOD', ) else: # Use the particular selected video vbsp_config.set_key( ('Elevator', 'type'), 'FORCE', ) vbsp_config.set_key( ('Elevator', 'horiz'), elevator.horiz_video, ) vbsp_config.set_key( ('Elevator', 'vert'), elevator.vert_video, ) else: # No elevator video for this style vbsp_config.set_key( ('Elevator', 'type'), 'NONE', ) if music is not None: if music.sound is not None: vbsp_config.set_key( ('Options', 'music_SoundScript'), music.sound, ) if music.inst is not None: vbsp_config.set_key( ('Options', 'music_instance'), music.inst, ) vbsp_config.set_key(('Options', 'music_ID'), music.id) vbsp_config += music.config if voice is not None: vbsp_config.set_key( ('Options', 'voice_pack'), voice.id, ) vbsp_config.set_key(('Options', 'voice_char'), ','.join(voice.chars)) vbsp_config.set_key( ('Options', 'BEE2_loc'), os.path.dirname( os.getcwd()) # Go up one dir to our actual location ) vbsp_config.ensure_exists('StyleVars') vbsp_config['StyleVars'] += [ Property(key, utils.bool_as_int(val)) for key, val in style_vars.items() ] pack_block = Property('PackList', []) # A list of materials which will casue a specific packlist to be used. pack_triggers = Property('PackTriggers', []) for key, pack in pack_list.items(): pack_block.append( Property(key, [Property('File', file) for file in pack.files])) for trigger_mat in pack.trigger_mats: pack_triggers.append( Property('Material', [ Property('Texture', trigger_mat), Property('PackList', pack.id), ])) if pack_triggers.value: vbsp_config.append(pack_triggers) # If there are multiple of these blocks, merge them together # They will end up in this order. vbsp_config.merge_children( 'Textures', 'Fizzler', 'Options', 'StyleVars', 'Conditions', 'Voice', 'PackTriggers', ) for name, file, ext in FILES_TO_BACKUP: item_path = self.abs_path(file + ext) backup_path = self.abs_path(file + '_original' + ext) if os.path.isfile(item_path) and not os.path.isfile(backup_path): print('Backing up original ' + name + '!') shutil.copy(item_path, backup_path) export_screen.step('BACK') # This is the connections "heart" icon and "error" icon editoritems += style.editor.find_key("Renderables", []) # Build a property tree listing all of the instances for each item all_instances = Property("AllInstances", []) for item in editoritems.find_all("Item"): item_prop = Property(item['Type'], []) all_instances.append(item_prop) for inst_block in item.find_all("Exporting", "instances"): for inst in inst_block: item_prop.append(Property('Instance', inst['Name'])) if style_vars.get('UnlockDefault', False): print('Unlocking Items!') for item in editoritems.find_all('Item'): # If the Unlock Default Items stylevar is enabled, we # want to force the corridors and obs room to be # deletable and copyable # Also add DESIRES_UP, so they place in the correct orientation if item['type', ''] in _UNLOCK_ITEMS: editor_section = item.find_key("Editor", []) editor_section['deletable'] = '1' editor_section['copyable'] = '1' editor_section['DesiredFacing'] = 'DESIRES_UP' print('Editing Gameinfo!') self.edit_gameinfo(True) export_screen.step('CONF') print('Writing Editoritems!') os.makedirs(self.abs_path('portal2_dlc2/scripts/'), exist_ok=True) with open(self.abs_path('portal2_dlc2/scripts/editoritems.txt'), 'w') as editor_file: for line in editoritems.export(): editor_file.write(line) export_screen.step('CONF') print('Writing VBSP Config!') os.makedirs(self.abs_path('bin/bee2/'), exist_ok=True) with open(self.abs_path('bin/bee2/vbsp_config.cfg'), 'w') as vbsp_file: for line in vbsp_config.export(): vbsp_file.write(line) export_screen.step('CONF') print('Writing instance list!') with open(self.abs_path('bin/bee2/instances.cfg'), 'w') as inst_file: for line in all_instances.export(): inst_file.write(line) export_screen.step('CONF') print('Writing packing list!') with open(self.abs_path('bin/bee2/pack_list.cfg'), 'w') as pack_file: for line in pack_block.export(): pack_file.write(line) export_screen.step('CONF') print('Editing game_sounds!') self.add_editor_sounds(editor_sounds.values()) export_screen.step('CONF') if voice is not None: for prefix, dest, pretty in VOICE_PATHS: path = os.path.join( os.getcwd(), '..', 'config', 'voice', prefix + voice.id + '.cfg', ) print(path) if os.path.isfile(path): shutil.copy( path, self.abs_path('bin/bee2/{}voice.cfg'.format(dest))) print('Written "{}voice.cfg"'.format(dest)) else: print('No ' + pretty + ' voice config!') export_screen.step('CONF') print('Copying Custom Compiler!') for file in os.listdir('../compiler'): print('\t* compiler/{0} -> bin/{0}'.format(file)) shutil.copy(os.path.join('../compiler', file), self.abs_path('bin/')) export_screen.step('COMP') if should_refresh: print('Copying Resources!') self.refresh_cache() export_screen.grab_release() export_screen.reset() # Hide loading screen, we're done