Esempio n. 1
0
def generate_resp_script(file, allow_dings):
    """Write the responses section into a file."""
    use_dings = allow_dings

    config = ConfigFile('resp_voice.cfg', root='bee2')
    file.write("BEE2_RESPONSES <- {\n")
    for section in QUOTE_DATA.find_key('CoopResponses', []):
        if not section.has_children() and section.name == 'use_dings':
            # Allow overriding specifically for the response script
            use_dings = utils.conv_bool(section.value, allow_dings)
            continue

        voice_attr = RESP_HAS_NAMES.get(section.name, '')
        if voice_attr and not map_attr[voice_attr]:
            continue
            # This response catagory isn't present

        section_data = ['\t{} = [\n'.format(section.name)]
        for index, line in enumerate(section):
            if not config.getboolean(section.name, "line_" + str(index), True):
                # It's disabled!
                continue
            section_data.append(
                '\t\tCreateSceneEntity("{}"),\n'.format(line['choreo'])
            )
        if len(section_data) != 1:
            for line in section_data:
                file.write(line)
            file.write('\t],\n')
    file.write('}\n')

    file.write('BEE2_PLAY_DING = {};\n'.format(
        'true' if use_dings else 'false'
    ))
Esempio n. 2
0
def generate_resp_script(file, allow_dings):
    """Write the responses section into a file."""
    use_dings = allow_dings

    config = ConfigFile('resp_voice.cfg', root='bee2')
    file.write("BEE2_RESPONSES <- {\n")
    for section in QUOTE_DATA.find_key('CoopResponses', []):
        if not section.has_children() and section.name == 'use_dings':
            # Allow overriding specifically for the response script
            use_dings = srctools.conv_bool(section.value, allow_dings)
            continue

        voice_attr = RESP_HAS_NAMES.get(section.name, '')
        if voice_attr and not map_attr[voice_attr]:
            continue
            # This response catagory isn't present

        section_data = ['\t{} = [\n'.format(section.name)]
        for index, line in enumerate(section):
            if not config.getboolean(section.name, "line_" + str(index), True):
                # It's disabled!
                continue
            section_data.append('\t\tCreateSceneEntity("{}"),\n'.format(
                line['choreo']))
        if len(section_data) != 1:
            for line in section_data:
                file.write(line)
            file.write('\t],\n')
    file.write('}\n')

    file.write(
        'BEE2_PLAY_DING = {};\n'.format('true' if use_dings else 'false'))
Esempio n. 3
0
    def parse(cls, gm_id, config: ConfigFile):
        steam_id = config.get_val(gm_id, "SteamID", "<none>")
        if not steam_id.isdigit():
            raise ValueError("Game {} has invalid Steam ID: {}".format(gm_id, steam_id))

        folder = config.get_val(gm_id, "Dir", "")
        if not folder:
            raise ValueError("Game {} has no folder!".format(gm_id))

        mod_time = config.get_int(gm_id, "ModTime", 0)

        return cls(gm_id, steam_id, folder, mod_time)
Esempio n. 4
0
    def parse(cls, gm_id, config: ConfigFile):
        steam_id = config.get_val(gm_id, 'SteamID', '<none>')
        if not steam_id.isdigit():
            raise ValueError('Game {} has invalid Steam ID: {}'.format(
                gm_id, steam_id))

        folder = config.get_val(gm_id, 'Dir', '')
        if not folder:
            raise ValueError('Game {} has no folder!'.format(gm_id))

        mod_time = config.get_int(gm_id, 'ModTime', 0)

        return cls(gm_id, steam_id, folder, mod_time)
Esempio n. 5
0
    def parse(cls, gm_id, config: ConfigFile):
        steam_id = config.get_val(gm_id, 'SteamID', '<none>')
        if not steam_id.isdigit():
            raise ValueError(
                'Game {} has invalid Steam ID: {}'.format(gm_id, steam_id)
            )

        folder = config.get_val(gm_id, 'Dir', '')
        if not folder:
            raise ValueError(
                'Game {} has no folder!'.format(gm_id)
            )

        mod_time = config.get_int(gm_id, 'ModTime', 0)

        return cls(gm_id, steam_id, folder, mod_time)
Esempio n. 6
0
    def parse(cls, gm_id, config: ConfigFile):
        steam_id = config.get_val(gm_id, 'SteamID', '<none>')
        if not steam_id.isdigit():
            raise ValueError('Game {} has invalid Steam ID: {}'.format(
                gm_id, steam_id))

        folder = config.get_val(gm_id, 'Dir', '')
        if not folder:
            raise ValueError('Game {} has no folder!'.format(gm_id))
        mod_times = {}

        for name, value in config.items(gm_id):
            if name.startswith('pack_mod_'):
                mod_times[name[9:].casefold()] = srctools.conv_int(value)

        return cls(gm_id, steam_id, folder, mod_times)
Esempio n. 7
0
def generate_resp_script(file):
    """Write the responses section into a file."""
    config = ConfigFile('resp_voice.cfg', root='bee2')
    file.write("BEE2_RESPONSES <- {\n")
    for section in QUOTE_DATA.find_key('CoopResponses', []):
        section_data = ['\t{} = [\n'.format(section.name)]
        for index, line in enumerate(section):
            if not config.getboolean(section.name, "line_" + str(index), True):
                # It's disabled!
                continue
            section_data.append(
                '\t\tCreateSceneEntity("{}"),\n'.format(line['choreo'])
            )
        if len(section_data) != 1:
            for line in section_data:
                file.write(line)
            file.write('\t],\n')
    file.write('}\n')
Esempio n. 8
0
def show(quote_pack):
    """Display the editing window."""
    global voice_item, config, config_mid
    voice_item = quote_pack

    win.title('BEE2 - Configure "' + voice_item.selitem_data.name + '"')
    notebook = UI['tabs']

    quote_data = quote_pack.config

    os.makedirs('config/voice', exist_ok=True)
    config = ConfigFile('voice/' + quote_pack.id + '.cfg')
    config_mid = ConfigFile('voice/MID_' + quote_pack.id + '.cfg')

    # Clear the transcript textbox
    text = UI['trans']
    text['state'] = 'normal'
    text.delete(1.0, END)
    text['state'] = 'disabled'

    # Destroy all the old tabs
    for tab in TABS.values():
        try:
            notebook.forget(tab)
        except TclError as e:
            pass
        tab.destroy()

    TABS.clear()

    for group in quote_data.find_all('quotes', 'group'):
        make_tab(
            group,
            config,
            is_mid=False,
            )

    mid_quotes = list(quote_data.find_all('quotes', 'midchamber'))
    if len(mid_quotes) > 0:
        frame = make_tab(
            mid_quotes[0],
            config_mid,
            is_mid=True,
            )
        frame.nb_text = ''

    config.save()
    config_mid.save()

    add_tabs()

    win.deiconify()
    win.lift(win.winfo_parent())
    utils.center_win(win)  # Center inside the parent
Esempio n. 9
0
    def parse(cls, gm_id: str, config: ConfigFile) -> 'Game':
        """Parse out the given game ID from the config file."""
        steam_id = config.get_val(gm_id, 'SteamID', '<none>')
        if not steam_id.isdigit():
            raise ValueError(f'Game {gm_id} has invalid Steam ID: {steam_id}')

        folder = config.get_val(gm_id, 'Dir', '')
        if not folder:
            raise ValueError(f'Game {gm_id} has no folder!')

        if not os.path.exists(folder):
            raise ValueError(
                f'Folder {folder} does not exist for game {gm_id}!')

        mod_times = {}

        for name, value in config.items(gm_id):
            if name.startswith('pack_mod_'):
                mod_times[name[9:].casefold()] = srctools.conv_int(value)

        return cls(gm_id, steam_id, folder, mod_times)
Esempio n. 10
0
def encode_coop_responses(vmf: VMF, pos: Vec, allow_dings: bool,
                          voice_attrs: dict) -> None:
    """Write the coop responses information into the map."""
    config = ConfigFile('bee2/resp_voice.cfg', in_conf_folder=False)
    response_block = QUOTE_DATA.find_key('CoopResponses', [])

    # Pass in whether to include dings or not.
    vmf.create_ent(
        'comp_scriptvar_setter',
        origin=pos,
        target='@glados',
        variable='BEE2_PLAY_DING',
        mode='const',
        # Allow overriding specifically for the response script.
        const=response_block.bool('use_dings', allow_dings),
    )

    for section in response_block:
        if not section.has_children():
            continue

        voice_attr = RESP_HAS_NAMES.get(section.name, '')
        if voice_attr and not voice_attrs[voice_attr]:
            # This response category isn't present.
            continue

        # Use a custom entity to encode our information.
        ent = vmf.create_ent(
            'bee2_coop_response',
            origin=pos,
            type=section.name,
        )

        # section_data = []
        for index, line in enumerate(section):
            if not config.getboolean(section.name, "line_" + str(index), True):
                # It's disabled!
                continue
            ent[f'choreo{index:02}'] = line['choreo']
Esempio n. 11
0
def main(argv: List[str]) -> None:
    """Main VRAD script."""
    LOGGER.info(
        "BEE{} VRAD hook initiallised, srctools v{}, Hammer Addons v{}",
        utils.BEE_VERSION, srctools.__version__, version_haddons,
    )

    # Warn if srctools Cython code isn't installed.
    utils.check_cython(LOGGER.warning)

    args = " ".join(argv)
    fast_args = argv[1:]
    full_args = argv[1:]

    if not fast_args:
        # No arguments!
        LOGGER.info(
            'No arguments!\n'
            "The BEE2 VRAD takes all the regular VRAD's "
            'arguments, with some extra arguments:\n'
            '-force_peti: Force enabling map conversion. \n'
            "-force_hammer: Don't convert the map at all.\n"
            "If not specified, the map name must be \"preview.bsp\" to be "
            "treated as PeTI."
        )
        sys.exit()

    # The path is the last argument to vrad
    # P2 adds wrong slashes sometimes, so fix that.
    fast_args[-1] = path = os.path.normpath(argv[-1])

    LOGGER.info("Map path is " + path)

    LOGGER.info('Loading Settings...')
    config = ConfigFile('compile.cfg')

    for a in fast_args[:]:
        folded_a = a.casefold()
        if folded_a.casefold() in (
                "-final",
                "-staticproplighting",
                "-staticproppolys",
                "-textureshadows",
                ):
            # remove final parameters from the modified arguments
            fast_args.remove(a)
        elif folded_a == '-both':
            # LDR Portal 2 isn't actually usable, so there's not much
            # point compiling for it.
            pos = fast_args.index(a)
            fast_args[pos] = full_args[pos] = '-hdr'
        elif a in ('-force_peti', '-force_hammer', '-no_pack'):
            # we need to strip these out, otherwise VRAD will get confused
            fast_args.remove(a)
            full_args.remove(a)

    fast_args = ['-bounce', '2', '-noextra'] + fast_args

    # Fast args: -bounce 2 -noextra -game $gamedir $path\$file
    # Final args: -both -final -staticproplighting -StaticPropPolys
    # -textureshadows  -game $gamedir $path\$file

    if not path.endswith(".bsp"):
        path += ".bsp"

    if not os.path.exists(path):
        raise ValueError('"{}" does not exist!'.format(path))
    if not os.path.isfile(path):
        raise ValueError('"{}" is not a file!'.format(path))

    LOGGER.info('Reading BSP')
    bsp_file = BSP(path)

    # If VBSP marked it as Hammer, trust that.
    if srctools.conv_bool(bsp_file.ents.spawn['BEE2_is_peti']):
        is_peti = True
        # Detect preview via knowing the bsp name. If we are in preview,
        # check the config file to see what was specified there.
        if os.path.basename(path) == "preview.bsp":
            edit_args = not config.get_bool('General', 'vrad_force_full')
            # If shift is held, reverse.
            if utils.check_shift():
                LOGGER.info('Shift held, inverting configured lighting option!')
                edit_args = not edit_args
        else:
            # publishing - always force full lighting.
            edit_args = False
    else:
        is_peti = edit_args = False

    if '-force_peti' in args or '-force_hammer' in args:
        # we have override commands!
        if '-force_peti' in args:
            LOGGER.warning('OVERRIDE: Applying cheap lighting!')
            is_peti = edit_args = True
        else:
            LOGGER.warning('OVERRIDE: Preserving args!')
            is_peti = edit_args = False

    LOGGER.info('Final status: is_peti={}, edit_args={}', is_peti, edit_args)
    if not is_peti:
        # Skip everything, if the user wants these features install the Hammer Addons postcompiler.
        LOGGER.info("Hammer map detected! Skipping all transforms.")
        run_vrad(full_args)
        return

    # Grab the currently mounted filesystems in P2.
    game = find_gameinfo(argv)
    root_folder = game.path.parent
    fsys = game.get_filesystem()

    # Special case - move the BEE2 filesystem FIRST, so we always pack files found there.
    for child_sys in fsys.systems[:]:
        if 'bee2' in child_sys[0].path.casefold():
            fsys.systems.remove(child_sys)
            fsys.systems.insert(0, child_sys)

    zip_data = BytesIO()
    zip_data.write(bsp_file.get_lump(BSP_LUMPS.PAKFILE))
    zipfile = ZipFile(zip_data)

    # Mount the existing packfile, so the cubemap files are recognised.
    fsys.add_sys(ZipFileSystem('<BSP pakfile>', zipfile))

    LOGGER.info('Done!')

    LOGGER.debug('Filesystems:')
    for child_sys in fsys.systems[:]:
        LOGGER.debug('- {}: {!r}', child_sys[1], child_sys[0])

    LOGGER.info('Reading our FGD files...')
    fgd = FGD.engine_dbase()

    packlist = PackList(fsys)
    LOGGER.info('Reading soundscripts...')
    packlist.load_soundscript_manifest(
        str(root_folder / 'bin/bee2/sndscript_cache.vdf')
    )

    # We need to add all soundscripts in scripts/bee2_snd/
    # This way we can pack those, if required.
    for soundscript in fsys.walk_folder('scripts/bee2_snd/'):
        if soundscript.path.endswith('.txt'):
            packlist.load_soundscript(soundscript, always_include=False)

    LOGGER.info('Reading particles....')
    packlist.load_particle_manifest()

    LOGGER.info('Loading transforms...')
    load_transforms()

    LOGGER.info('Checking for music:')
    music.generate(bsp_file.ents, packlist)

    LOGGER.info('Run transformations...')
    run_transformations(bsp_file.ents, fsys, packlist, bsp_file, game)

    LOGGER.info('Scanning map for files to pack:')
    packlist.pack_from_bsp(bsp_file)
    packlist.pack_fgd(bsp_file.ents, fgd)
    packlist.eval_dependencies()
    LOGGER.info('Done!')

    packlist.write_soundscript_manifest()
    packlist.write_particles_manifest(f'maps/{Path(path).stem}_particles.txt')

    # We need to disallow Valve folders.
    pack_whitelist: set[FileSystem] = set()
    pack_blacklist: set[FileSystem] = set()

    # Exclude absolutely everything except our folder.
    for child_sys, _ in fsys.systems:
        # Add 'bee2/' and 'bee2_dev/' only.
        if (
            isinstance(child_sys, RawFileSystem) and
            'bee2' in os.path.basename(child_sys.path).casefold()
        ):
            pack_whitelist.add(child_sys)
        else:
            pack_blacklist.add(child_sys)

    if config.get_bool('General', 'packfile_dump_enable'):
        dump_loc = Path(config.get_val(
            'General',
            'packfile_dump_dir',
            '../dump/'
        )).absolute()
    else:
        dump_loc = None

    if '-no_pack' not in args:
        # Cubemap files packed into the map already.
        existing = set(bsp_file.pakfile.namelist())

        LOGGER.info('Writing to BSP...')
        packlist.pack_into_zip(
            bsp_file,
            ignore_vpk=True,
            whitelist=pack_whitelist,
            blacklist=pack_blacklist,
            dump_loc=dump_loc,
        )

        LOGGER.info('Packed files:\n{}', '\n'.join(
            set(bsp_file.pakfile.namelist()) - existing
        ))

    LOGGER.info('Writing BSP...')
    bsp_file.save()
    LOGGER.info(' - BSP written!')

    screenshot.modify(config, game.path)

    if edit_args:
        LOGGER.info("Forcing Cheap Lighting!")
        run_vrad(fast_args)
    else:
        LOGGER.info("Publishing - Full lighting enabled! (or forced to do so)")
        run_vrad(full_args)

    LOGGER.info("BEE2 VRAD hook finished!")
Esempio n. 12
0
import utils
import srctools

from typing import List, Tuple

LOGGER = utils.getLogger(__name__)

all_games = []  # type: List[Game]
selected_game = None  # type: Game
selectedGame_radio = IntVar(value=0)
game_menu = None  # type: Menu

# Translated text from basemodui.txt.
TRANS_DATA = {}

CONFIG = ConfigFile('games.cfg')

FILES_TO_BACKUP = [
    ('Editoritems', 'portal2_dlc2/scripts/editoritems', '.txt'),
    ('VBSP',        'bin/vbsp',                         '.exe'),
    ('VRAD',        'bin/vrad',                         '.exe'),
    ('VBSP',        'bin/vbsp_osx',   ''),
    ('VRAD',        'bin/vrad_osx',   ''),
    ('VBSP',        'bin/vbsp_linux', ''),
    ('VRAD',        'bin/vrad_linux', ''),
]

_UNLOCK_ITEMS = [
    'ITEM_EXIT_DOOR',
    'ITEM_COOP_EXIT_DOOR',
    'ITEM_ENTRY_DOOR',
Esempio n. 13
0
        'brush': '0',
        'ent': '0',
        'overlay': '0',
    },
}

PLAYER_MODELS = {
    'ATLAS': 'ATLAS',
    'PBODY': 'P-Body',
    'SP': 'Chell',
    'PETI': 'Bendy',
}
PLAYER_MODEL_ORDER = ['Bendy', 'Chell', 'ATLAS', 'P-Body']
PLAYER_MODELS_REV = {value: key for key, value in PLAYER_MODELS.items()}

COMPILE_CFG = ConfigFile('compile.cfg')
COMPILE_CFG.set_defaults(COMPILE_DEFAULTS)
window = None
UI = {}

chosen_thumb = StringVar(
    value=COMPILE_CFG.get_val('Screenshot', 'Type', 'AUTO'))
tk_screenshot = None  # The preview image shown

# Location we copy custom screenshots to
SCREENSHOT_LOC = os.path.abspath(
    os.path.join(os.getcwd(), '..', 'config', 'screenshot.jpg'))

player_model_var = StringVar(value=PLAYER_MODELS.get(
    COMPILE_CFG.get_val('General', 'player_model', 'PETI'),
    PLAYER_MODELS['PETI'],
Esempio n. 14
0
def make_tab(group, config: ConfigFile, tab_type):
    """Create all the widgets for a tab."""
    if tab_type is TabTypes.MIDCHAMBER:
        # Mid-chamber voice lines have predefined values.
        group_name = 'Mid - Chamber'
        group_id = 'MIDCHAMBER'
        group_desc = (
            'Lines played during the actual chamber, '
            'after specific events have occurred.'
        )
    elif tab_type is TabTypes.RESPONSE:
        group_name = 'Responses'
        group_id = None
        group_desc = (
            'Lines played in response to certain events in Coop.'
        )
    elif tab_type is TabTypes.NORM:
        group_name = group['name', 'No Name!']
        group_id = group_name.upper()
        group_desc = group['desc', ''] + ':'
    else:
        raise ValueError('Invalid tab type!')

    # This is just to hold the canvas and scrollbar
    outer_frame = ttk.Frame(UI['tabs'])
    outer_frame.columnconfigure(0, weight=1)
    outer_frame.rowconfigure(0, weight=1)

    TABS[group_name] = outer_frame
    # We add this attribute so the refresh() method knows all the
    # tab names
    outer_frame.nb_text = group_name
    outer_frame.nb_type = tab_type

    # We need a canvas to make the list scrollable.
    canv = Canvas(
        outer_frame,
        highlightthickness=0,
        )
    scroll = tk_tools.HidingScroll(
        outer_frame,
        orient=VERTICAL,
        command=canv.yview,
        )
    canv['yscrollcommand'] = scroll.set
    canv.grid(row=0, column=0, sticky='NSEW')
    scroll.grid(row=0, column=1, sticky='NS')

    UI['tabs'].add(outer_frame)

    # This holds the actual elements
    frame = ttk.Frame(
        canv,
        )
    frame.columnconfigure(0, weight=1)
    canv.create_window(0, 0, window=frame, anchor="nw")

    # We do this so we can adjust the scrollregion later in
    # <Configure>.
    canv.frame = frame

    ttk.Label(
        frame,
        text=group_name,
        anchor='center',
        font='tkHeadingFont',
        ).grid(
            row=0,
            column=0,
            sticky='EW',
            )

    ttk.Label(
        frame,
        text=group_desc,
        ).grid(
            row=1,
            column=0,
            sticky='EW',
            )

    ttk.Separator(frame, orient=HORIZONTAL).grid(
        row=2,
        column=0,
        sticky='EW',
    )

    if tab_type is TabTypes.RESPONSE:
        sorted_quotes = sorted(
            group,
            key=lambda prop: prop.real_name
        )
    else:
        sorted_quotes = sorted(
            group.find_all('Quote'),
            key=quote_sort_func,
            reverse=True,
        )

    for quote in sorted_quotes:  # type: Property
        if not quote.has_children():
            continue # Skip over config commands..

        if tab_type is TabTypes.RESPONSE:
            try:
                name = RESPONSE_NAMES[quote.name]
            except KeyError:
                # Convert channels of the form 'death_goo' into 'Death - Goo'.
                channel, ch_arg = quote.name.split('_', 1)
                name = channel.title() + ' - ' + ch_arg.title()
                del channel, ch_arg

            group_id = quote.name
        else:
            name = quote['name', 'No Name!']

        ttk.Label(
            frame,
            text=name,
            font=QUOTE_FONT,
        ).grid(
            column=0,
            sticky=W,
        )

        if tab_type is TabTypes.RESPONSE:
            line_iter = find_resp_lines(quote)
        else:
            line_iter = find_lines(quote)

        for badge, line, line_id in line_iter:
            if line_id is None:
                line_id = line['id', line['name']]
            check = ttk.Checkbutton(
                frame,
                text=line['name', 'No Name?'],
                compound=LEFT,
                image=badge,
            )

            check.quote_var = IntVar(
                value=config.get_bool(group_id, line_id, True),
            )

            check['variable'] = check.quote_var

            check['command'] = functools.partial(
                check_toggled,
                var=check.quote_var,
                config_section=config[group_id],
                quote_id=line_id,
            )

            check.transcript = list(get_trans_lines(line))
            check.grid(
                column=0,
                padx=(10, 0),
                sticky=W,
            )
            check.bind("<Enter>", show_trans)
    canv.bind('<Configure>', configure_canv)

    return outer_frame
Esempio n. 15
0
from query_dialogs import ask_string
from BEE2_config import ConfigFile
from property_parser import Property
import utils
import UI
import loadScreen
import extract_packages

all_games = []
selected_game = None
selectedGame_radio = IntVar(value=0)
game_menu = None

trans_data = {}

config = ConfigFile('games.cfg')

FILES_TO_BACKUP = [
    ('Editoritems', 'portal2_dlc2/scripts/editoritems', '.txt'),
    ('VBSP', 'bin/vbsp', '.exe'),
    ('VRAD', 'bin/vrad', '.exe'),
    ('VBSP', 'bin/vbsp_osx', ''),
    ('VRAD', 'bin/vrad_osx', ''),
    ('VBSP', 'bin/vbsp_linux', ''),
    ('VRAD', 'bin/vrad_linux', ''),
]

VOICE_PATHS = [
    ('', '', 'normal'),
    ('MID_', 'mid_', 'MidChamber'),
]
Esempio n. 16
0
def add_voice(
    voice_data,
    has_items,
    style_vars_,
    vmf_file,
    map_seed,
    mode='SP',
):
    """Add a voice line to the map."""
    global ALLOW_MID_VOICES, VMF, map_attr, style_vars, GAME_MODE
    utils.con_log('Adding Voice Lines!')

    if len(voice_data.value) == 0:
        utils.con_log('Error - No Voice Line Data!')
        return

    VMF = vmf_file
    map_attr = has_items
    style_vars = style_vars_
    GAME_MODE = mode

    norm_config = ConfigFile('voice.cfg', root='bee2')
    mid_config = ConfigFile('mid_voice.cfg', root='bee2')

    quote_base = voice_data['base', False]
    quote_loc = voice_data['quote_loc', '-10000 0 0']
    if quote_base:
        print('Adding Base instance!')
        VMF.create_ent(
            classname='func_instance',
            targetname='voice',
            file=INST_PREFIX + quote_base,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    ALLOW_MID_VOICES = not style_vars.get('NoMidVoices', False)

    mid_quotes = []

    for group in itertools.chain(
            voice_data.find_all('group'),
            voice_data.find_all('midinst'),
    ):

        quote_targetname = group['Choreo_Name', '@choreo']

        possible_quotes = sorted(
            find_group_quotes(
                group,
                mid_quotes,
                conf=mid_config if group.name == 'midinst' else norm_config,
            ),
            key=sort_func,
            reverse=True,
        )

        if possible_quotes:

            choreo_loc = group['choreo_loc', quote_loc]

            chosen = possible_quotes[0][1]

            utils.con_log('Chosen:', '\n'.join(map(repr, chosen)))

            # Join the IDs for the voice lines to the map seed,
            # so each quote block will chose different lines.
            random.seed(map_seed + '-VOICE_' + '|'.join(prop['id', 'ID']
                                                        for prop in chosen))
            # Add one of the associated quotes
            add_quote(random.choice(chosen), quote_targetname, choreo_loc)

    print('Mid quotes: ', mid_quotes)
    for mid_item in mid_quotes:
        # Add all the mid quotes
        target = mid_item['target', '']
        for prop in mid_item:
            add_quote(prop, target, quote_loc)

    utils.con_log('Done!')
Esempio n. 17
0
def modify(conf: ConfigFile, game_folder: Path) -> None:
    """Modify the map's screenshot."""
    mod_type = conf.get_val('Screenshot', 'type', 'PETI').lower()

    if mod_type == 'cust':
        LOGGER.info('Using custom screenshot!')
        scr_loc = str(utils.conf_location('screenshot.jpg'))
    elif mod_type == 'auto':
        LOGGER.info('Using automatic screenshot!')
        scr_loc = None
        # The automatic screenshots are found at this location:
        auto_path = os.path.join(game_folder, 'screenshots')
        # We need to find the most recent one. If it's named
        # "previewcomplete", we want to ignore it - it's a flag
        # to indicate the map was playtested correctly.
        try:
            screens = [
                os.path.join(auto_path, path) for path in os.listdir(auto_path)
            ]
        except FileNotFoundError:
            # The screenshot folder doesn't exist!
            screens = []
        screens.sort(
            key=os.path.getmtime,
            reverse=True,
            # Go from most recent to least
        )
        playtested = False
        for scr_shot in screens:
            filename = os.path.basename(scr_shot)
            if filename.startswith('bee2_playtest_flag'):
                # Previewcomplete is a flag to indicate the map's
                # been playtested. It must be newer than the screenshot
                playtested = True
                continue
            elif filename.startswith('bee2_screenshot'):
                continue  # Ignore other screenshots

            # We have a screenshot. Check to see if it's
            # not too old. (Old is > 2 hours)
            date = datetime.fromtimestamp(os.path.getmtime(scr_shot))
            diff = datetime.now() - date
            if diff.total_seconds() > 2 * 3600:
                LOGGER.info(
                    'Screenshot "{scr}" too old ({diff!s})',
                    scr=scr_shot,
                    diff=diff,
                )
                continue

            # If we got here, it's a good screenshot!
            LOGGER.info('Chosen "{}"', scr_shot)
            LOGGER.info('Map Playtested: {}', playtested)
            scr_loc = scr_shot
            break
        else:
            # If we get to the end, we failed to find an automatic
            # screenshot!
            LOGGER.info('No Auto Screenshot found!')
            mod_type = 'peti'  # Suppress the "None not found" error

        if conf.get_bool('Screenshot', 'del_old'):
            LOGGER.info('Cleaning up screenshots...')
            # Clean up this folder - otherwise users will get thousands of
            # pics in there!
            for screen in screens:
                if screen != scr_loc and os.path.isfile(screen):
                    os.remove(screen)
            LOGGER.info('Done!')
    else:
        # PeTI type, or something else
        scr_loc = None

    if scr_loc is not None and os.path.isfile(scr_loc):
        # We should use a screenshot!
        for screen in find():
            LOGGER.info('Replacing "{}"...', screen)
            # Allow us to edit the file...
            utils.unset_readonly(screen)
            shutil.copy(scr_loc, screen)
            # Make the screenshot readonly, so P2 can't replace it.
            # Then it'll use our own
            utils.set_readonly(screen)

    else:
        if mod_type != 'peti':
            # Error if we were looking for a screenshot
            LOGGER.warning('"{}" not found!', scr_loc)
        LOGGER.info('Using PeTI screenshot!')
        for screen in find():
            # Make the screenshot writeable, so P2 will replace it
            LOGGER.info('Making "{}" replaceable...', screen)
            utils.unset_readonly(screen)
Esempio n. 18
0
from tkinter import ttk
from tkinter import messagebox
import tkinter as tk
from app import TK_ROOT

from app.CheckDetails import CheckDetails, Item as CheckItem
from BEE2_config import ConfigFile
import packages
import utils

window = tk.Toplevel(TK_ROOT)
window.withdraw()

UI = {}

PACK_CONFIG = ConfigFile('packages.cfg')

pack_items = {}

HEADERS = ['Name']


def show():
    """Show the manager window."""
    window.deiconify()
    window.lift(TK_ROOT)
    window.grab_set()
    utils.center_win(window, TK_ROOT)
    window.update()
    UI['details'].refresh()
Esempio n. 19
0
def show(quote_pack):
    """Display the editing window."""
    global voice_item, config_sp, config_coop, config_mid_sp, config_mid_coop
    voice_item = quote_pack

    win.title('BEE2 - Configure "' + voice_item.name + '"')
    notebook = UI['tabs']

    quote_data = quote_pack.config

    os.makedirs('config/voice', exist_ok=True)
    config_sp = ConfigFile('voice/SP_' + quote_pack.id + '.cfg')
    config_coop = ConfigFile('voice/COOP_' + quote_pack.id + '.cfg')
    config_mid_sp = ConfigFile('voice/MID_SP_' + quote_pack.id + '.cfg')
    config_mid_coop = ConfigFile('voice/MID_COOP_' + quote_pack.id + '.cfg')

    # Clear the transcript textbox
    text = UI['trans']
    text['state'] = 'normal'
    text.delete(1.0, END)
    text['state'] = 'disabled'

    # Destroy all the old tabs
    for tab in itertools.chain(
            TABS_SP.values(),
            TABS_COOP.values(),
            ):
        try:
            notebook.forget(tab)
        except TclError:
            pass
        tab.destroy()

    TABS_SP.clear()
    TABS_COOP.clear()

    add_tabs(quote_data, 'quotes_sp', TABS_SP, config_sp)
    add_tabs(quote_data, 'quotes_coop', TABS_COOP, config_coop)

    config_sp.save()
    config_coop.save()
    config_mid_sp.save()
    config_mid_coop.save()

    refresh()
    win.deiconify()
    win.lift(win.winfo_parent())
    utils.center_win(win)  # Center inside the parent
Esempio n. 20
0
def make_tab(group, config: ConfigFile, tab_type):
    """Create all the widgets for a tab."""
    if tab_type is TabTypes.MIDCHAMBER:
        # Mid-chamber voice lines have predefined values.
        group_name = _('Mid - Chamber')
        group_id = 'MIDCHAMBER'
        group_desc = _('Lines played during the actual chamber, '
                       'after specific events have occurred.')
    elif tab_type is TabTypes.RESPONSE:
        # Note: 'Response' tab header, and description
        group_name = _('Responses')
        group_id = None
        group_desc = _('Lines played in response to certain events in Coop.')
    elif tab_type is TabTypes.NORM:
        group_name = group['name', 'No Name!']
        group_id = group_name.upper()
        group_desc = group['desc', ''] + ':'
    else:
        raise ValueError('Invalid tab type!')

    # This is just to hold the canvas and scrollbar
    outer_frame = ttk.Frame(UI['tabs'])
    outer_frame.columnconfigure(0, weight=1)
    outer_frame.rowconfigure(0, weight=1)

    TABS[group_name] = outer_frame
    # We add this attribute so the refresh() method knows all the
    # tab names
    outer_frame.nb_text = group_name
    outer_frame.nb_type = tab_type

    # We need a canvas to make the list scrollable.
    canv = Canvas(
        outer_frame,
        highlightthickness=0,
    )
    scroll = tk_tools.HidingScroll(
        outer_frame,
        orient=VERTICAL,
        command=canv.yview,
    )
    canv['yscrollcommand'] = scroll.set
    canv.grid(row=0, column=0, sticky='NSEW')
    scroll.grid(row=0, column=1, sticky='NS')

    UI['tabs'].add(outer_frame)

    # This holds the actual elements
    frame = ttk.Frame(canv, )
    frame.columnconfigure(0, weight=1)
    canv.create_window(0, 0, window=frame, anchor="nw")

    # We do this so we can adjust the scrollregion later in
    # <Configure>.
    canv.frame = frame

    ttk.Label(
        frame,
        text=group_name,
        anchor='center',
        font='tkHeadingFont',
    ).grid(
        row=0,
        column=0,
        sticky='EW',
    )

    ttk.Label(
        frame,
        text=group_desc,
    ).grid(
        row=1,
        column=0,
        sticky='EW',
    )

    ttk.Separator(frame, orient=HORIZONTAL).grid(
        row=2,
        column=0,
        sticky='EW',
    )

    if tab_type is TabTypes.RESPONSE:
        sorted_quotes = sorted(group, key=lambda prop: prop.real_name)
    else:
        sorted_quotes = sorted(
            group.find_all('Quote'),
            key=quote_sort_func,
            reverse=True,
        )

    for quote in sorted_quotes:  # type: Property
        if not quote.has_children():
            continue  # Skip over config commands..

        if tab_type is TabTypes.RESPONSE:
            try:
                name = RESPONSE_NAMES[quote.name]
            except KeyError:
                # Convert channels of the form 'death_goo' into 'Death - Goo'.
                channel, ch_arg = quote.name.split('_', 1)
                name = channel.title() + ' - ' + ch_arg.title()
                del channel, ch_arg

            group_id = quote.name
        else:
            # note: default for quote names
            name = quote['name', _('No Name!')]

        ttk.Label(
            frame,
            text=name,
            font=QUOTE_FONT,
        ).grid(
            column=0,
            sticky=W,
        )

        if tab_type is TabTypes.RESPONSE:
            line_iter = find_resp_lines(quote)
        else:
            line_iter = find_lines(quote)

        for badge, line, line_id in line_iter:
            if line_id is None:
                line_id = line['id', line['name']]
            check = ttk.Checkbutton(
                frame,
                # note: default voice line name next to checkbox.
                text=line['name', _('No Name?')],
                compound=LEFT,
                image=badge,
            )

            check.quote_var = IntVar(value=config.get_bool(
                group_id, line_id, True), )

            check['variable'] = check.quote_var

            check['command'] = functools.partial(
                check_toggled,
                var=check.quote_var,
                config_section=config[group_id],
                quote_id=line_id,
            )

            check.transcript = list(get_trans_lines(line))
            check.grid(
                column=0,
                padx=(10, 0),
                sticky=W,
            )
            check.bind("<Enter>", show_trans)
    canv.bind('<Configure>', configure_canv)

    return outer_frame
Esempio n. 21
0
def show(quote_pack):
    """Display the editing window."""
    global voice_item, config, config_mid, config_resp
    if voice_item is not None:
        return

    voice_item = quote_pack

    win.title(_('BEE2 - Configure "{}"').format(voice_item.selitem_data.name))
    notebook = UI['tabs']

    quote_data = quote_pack.config

    os.makedirs('config/voice', exist_ok=True)
    config = ConfigFile('voice/' + quote_pack.id + '.cfg')
    config_mid = ConfigFile('voice/MID_' + quote_pack.id + '.cfg')
    config_resp = ConfigFile('voice/RESP_' + quote_pack.id + '.cfg')

    # Clear the transcript textbox
    text = UI['trans']
    text['state'] = 'normal'
    text.delete(1.0, END)
    text['state'] = 'disabled'

    # Destroy all the old tabs
    for tab in TABS.values():
        try:
            notebook.forget(tab)
        except TclError:
            pass
        tab.destroy()

    TABS.clear()

    for group in quote_data.find_all('quotes', 'group'):
        make_tab(group, config, TabTypes.NORM)

    # Merge all blocks into one
    mid_quotes = Property(
        'midChamber',
        list(
            itertools.chain.from_iterable(
                quote_data.find_all('quotes', 'midChamber'))))

    if len(mid_quotes):
        make_tab(
            mid_quotes,
            config_mid,
            TabTypes.MIDCHAMBER,
        )

    responses = Property(
        'CoopResponses',
        list(
            itertools.chain.from_iterable(
                quote_data.find_all('quotes', 'CoopResponses'))),
    )

    if len(responses):
        make_tab(
            responses,
            config_resp,
            TabTypes.RESPONSE,
        )

    config.save()
    config_mid.save()
    config_resp.save()

    add_tabs()

    win.deiconify()
    utils.center_win(win)  # Center inside the parent
    win.lift()
Esempio n. 22
0
def add_voice(
    has_items: dict,
    style_vars_: dict,
    vmf_file_: VMF,
    map_seed: str,
    use_priority=True,
):
    """Add a voice line to the map."""
    global ALLOW_MID_VOICES, vmf_file, map_attr, style_vars
    LOGGER.info('Adding Voice Lines!')

    vmf_file = vmf_file_
    map_attr = has_items
    style_vars = style_vars_

    norm_config = ConfigFile('voice.cfg', root='bee2')
    mid_config = ConfigFile('mid_voice.cfg', root='bee2')

    quote_base = QUOTE_DATA['base', False]
    quote_loc = Vec.from_str(QUOTE_DATA['quote_loc', '-10000 0 0'], x=-10000)
    if quote_base:
        LOGGER.info('Adding Base instance!')
        vmf_file.create_ent(
            classname='func_instance',
            targetname='voice',
            file=INST_PREFIX + quote_base,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    # Either box in with nodraw, or place the voiceline studio.
    has_studio = conditions.monitor.make_voice_studio(vmf_file, quote_loc)

    bullsye_actor = vbsp_options.get(str, 'voice_studio_actor')
    if bullsye_actor and has_studio:
        ADDED_BULLSEYES.add(bullsye_actor)

    global_bullseye = QUOTE_DATA['bullseye', '']
    if global_bullseye:
        add_bullseye(quote_loc, global_bullseye)

    ALLOW_MID_VOICES = not style_vars.get('nomidvoices', False)

    mid_quotes = []

    # Enable using the beep before and after choreo lines.
    allow_dings = srctools.conv_bool(QUOTE_DATA['use_dings', '0'])
    if allow_dings:
        vmf_file.create_ent(
            classname='logic_choreographed_scene',
            targetname='@ding_on',
            origin=quote_loc + (-8, -16, 0),
            scenefile='scenes/npc/glados_manual/ding_on.vcd',
            busyactor="1",  # Wait for actor to stop talking
            onplayerdeath='0',
        )
        vmf_file.create_ent(
            classname='logic_choreographed_scene',
            targetname='@ding_off',
            origin=quote_loc + (8, -16, 0),
            scenefile='scenes/npc/glados_manual/ding_off.vcd',
            busyactor="1",  # Wait for actor to stop talking
            onplayerdeath='0',
        )

    # QuoteEvents allows specifiying an instance for particular items,
    # so a voice line can be played at a certain time. It's only active
    # in certain styles, but uses the default if not set.
    for event in QUOTE_DATA.find_all('QuoteEvents', 'Event'):
        event_id = event['id', ''].casefold()
        # We ignore the config if no result was executed.
        if event_id and event_id in QUOTE_EVENTS:
            # Instances from the voiceline config are in this subfolder,
            # but not the default item - that's set from the conditions
            QUOTE_EVENTS[event_id] = INST_PREFIX + event['file']

    LOGGER.info('Quote events: {}', list(QUOTE_EVENTS.keys()))

    if has_responses():
        LOGGER.info('Generating responses data..')
        with open(RESP_LOC, 'w') as f:
            generate_resp_script(f, allow_dings)
    else:
        LOGGER.info('No responses data..')
        try:
            os.remove(RESP_LOC)
        except FileNotFoundError:
            pass

    for ind, file in enumerate(QUOTE_EVENTS.values()):
        if not file:
            continue
        vmf_file.create_ent(
            classname='func_instance',
            targetname='voice_event_' + str(ind),
            file=file,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    # For each group, locate the voice lines.
    for group in itertools.chain(
            QUOTE_DATA.find_all('group'),
            QUOTE_DATA.find_all('midchamber'),
    ):

        quote_targetname = group['Choreo_Name', '@choreo']
        use_dings = srctools.conv_bool(group['use_dings', ''], allow_dings)

        possible_quotes = sorted(
            find_group_quotes(
                group,
                mid_quotes,
                use_dings,
                conf=mid_config if group.name == 'midchamber' else norm_config,
                mid_name=quote_targetname,
            ),
            key=sort_func,
            reverse=True,
        )

        if possible_quotes:

            choreo_loc = Vec.from_str(group['choreo_loc', quote_loc])

            if use_priority:
                chosen = possible_quotes[0].lines
            else:
                # Chose one of the quote blocks..
                random.seed('{}-VOICE_QUOTE_{}'.format(
                    map_seed,
                    len(possible_quotes),
                ))
                chosen = random.choice(possible_quotes).lines

            # Join the IDs for
            # the voice lines to the map seed,
            # so each quote block will chose different lines.
            random.seed(map_seed + '-VOICE_LINE_' +
                        '|'.join(prop['id', 'ID'] for prop in chosen))

            # Add one of the associated quotes
            add_quote(
                random.choice(chosen),
                quote_targetname,
                choreo_loc,
                use_dings,
            )

    if ADDED_BULLSEYES or srctools.conv_bool(QUOTE_DATA['UseMicrophones', '']):
        # Add microphones that broadcast audio directly at players.
        # This ensures it is heard regardless of location.
        # This is used for Cave and core Wheatley.
        LOGGER.info('Using microphones...')
        if vbsp.GAME_MODE == 'SP':
            vmf_file.create_ent(
                classname='env_microphone',
                targetname='player_speaker_sp',
                speakername='!player',
                maxRange='386',
                origin=quote_loc,
            )
        else:
            vmf_file.create_ent(
                classname='env_microphone',
                targetname='player_speaker_blue',
                speakername='!player_blue',
                maxRange='386',
                origin=quote_loc,
            )
            vmf_file.create_ent(
                classname='env_microphone',
                targetname='player_speaker_orange',
                speakername='!player_orange',
                maxRange='386',
                origin=quote_loc,
            )

    LOGGER.info('{} Mid quotes', len(mid_quotes))
    for mid_lines in mid_quotes:
        line = random.choice(mid_lines)
        mid_item, use_ding, mid_name = line
        add_quote(mid_item, mid_name, quote_loc, use_ding)

    LOGGER.info('Done!')
Esempio n. 23
0
from enum import Enum, EnumMeta

import inspect

from srctools import Property, Vec, parse_vec_str
from BEE2_config import ConfigFile
import srctools.logger

from typing import Union, Tuple, TypeVar, Type, Optional, Iterator, Any, TextIO

LOGGER = srctools.logger.get_logger(__name__)

SETTINGS = {}

# Overwritten by VBSP to get the actual values.
ITEM_CONFIG = ConfigFile(None)


class TYPE(Enum):
    """The types arguments can have."""
    STR = str
    INT = int
    FLOAT = float
    BOOL = bool
    VEC = Vec

    def convert(self, value: str) -> Any:
        """Convert a string to the desired argument type."""
        return self.value(value)

Esempio n. 24
0
        'max_overlay': '512',
        'max_entity': '2048',
    },
    'CorridorNames': {},
}

PLAYER_MODELS = {
    'ATLAS': _('ATLAS'),
    'PBODY': _('P-Body'),
    'SP': _('Chell'),
    'PETI': _('Bendy'),
}
PLAYER_MODEL_ORDER = ['PETI', 'SP', 'ATLAS', 'PBODY']
PLAYER_MODELS_REV = {value: key for key, value in PLAYER_MODELS.items()}

COMPILE_CFG = ConfigFile('compile.cfg')
COMPILE_CFG.set_defaults(COMPILE_DEFAULTS)
window = None
UI = {}

chosen_thumb = StringVar(
    value=COMPILE_CFG.get_val('Screenshot', 'Type', 'AUTO')
)
tk_screenshot = None  # The preview image shown

# Location we copy custom screenshots to
SCREENSHOT_LOC = os.path.abspath(os.path.join(
    os.getcwd(),
    '..',
    'config',
    'screenshot.jpg'
Esempio n. 25
0
import inspect
import io

from srctools import Property, Vec, parse_vec_str
from BEE2_config import ConfigFile
import srctools
import utils

from typing import Union, Tuple, TypeVar, Type, Optional, Iterator

LOGGER = utils.getLogger(__name__)

SETTINGS = {}

# Overwritten by VBSP to get the actual values.
ITEM_CONFIG = ConfigFile('', root='', auto_load=False)


class TYPE(Enum):
    """The types arguments can have."""
    STR = str
    INT = int
    FLOAT = float
    BOOL = bool
    VEC = Vec


TYPE_NAMES = {
    TYPE.STR: 'Text',
    TYPE.INT: 'Whole Number',
    TYPE.FLOAT: 'Decimal Number',
Esempio n. 26
0
def add_voice(
    voice_attrs: dict,
    style_vars: dict,
    vmf: VMF,
    map_seed: str,
    use_priority=True,
) -> None:
    """Add a voice line to the map."""
    from precomp.conditions.monitor import make_voice_studio
    LOGGER.info('Adding Voice Lines!')

    norm_config = ConfigFile('bee2/voice.cfg', in_conf_folder=False)
    mid_config = ConfigFile('bee2/mid_voice.cfg', in_conf_folder=False)

    quote_base = QUOTE_DATA['base', False]
    quote_loc = get_studio_loc()
    if quote_base:
        LOGGER.info('Adding Base instance!')
        vmf.create_ent(
            classname='func_instance',
            targetname='voice',
            file=INST_PREFIX + quote_base,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    # Either box in with nodraw, or place the voiceline studio.
    has_studio = make_voice_studio(vmf)

    bullsye_actor = vbsp_options.get(str, 'voice_studio_actor')
    if bullsye_actor and has_studio:
        ADDED_BULLSEYES.add(bullsye_actor)

    global_bullseye = QUOTE_DATA['bullseye', '']
    if global_bullseye:
        add_bullseye(vmf, quote_loc, global_bullseye)

    allow_mid_voices = not style_vars.get('nomidvoices', False)

    mid_quotes = []

    # Enable using the beep before and after choreo lines.
    allow_dings = srctools.conv_bool(QUOTE_DATA['use_dings', '0'])
    if allow_dings:
        vmf.create_ent(
            classname='logic_choreographed_scene',
            targetname='@ding_on',
            origin=quote_loc + (-8, -16, 0),
            scenefile='scenes/npc/glados_manual/ding_on.vcd',
            busyactor="1",  # Wait for actor to stop talking
            onplayerdeath='0',
        )
        vmf.create_ent(
            classname='logic_choreographed_scene',
            targetname='@ding_off',
            origin=quote_loc + (8, -16, 0),
            scenefile='scenes/npc/glados_manual/ding_off.vcd',
            busyactor="1",  # Wait for actor to stop talking
            onplayerdeath='0',
        )

    # QuoteEvents allows specifying an instance for particular items,
    # so a voice line can be played at a certain time. It's only active
    # in certain styles, but uses the default if not set.
    for event in QUOTE_DATA.find_all('QuoteEvents', 'Event'):
        event_id = event['id', ''].casefold()
        # We ignore the config if no result was executed.
        if event_id and event_id in QUOTE_EVENTS:
            # Instances from the voiceline config are in this subfolder,
            # but not the default item - that's set from the conditions
            QUOTE_EVENTS[event_id] = INST_PREFIX + event['file']

    LOGGER.info('Quote events: {}', list(QUOTE_EVENTS.keys()))

    if has_responses():
        LOGGER.info('Generating responses data..')
        encode_coop_responses(vmf, quote_loc, allow_dings, voice_attrs)

    for ind, file in enumerate(QUOTE_EVENTS.values()):
        if not file:
            continue
        vmf.create_ent(
            classname='func_instance',
            targetname='voice_event_' + str(ind),
            file=file,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    # Determine the flags that enable/disable specific lines based on which
    # players are used.
    player_model = vbsp.BEE2_config.get_val(
        'General',
        'player_model',
        'PETI',
    ).casefold()

    is_coop = (vbsp.GAME_MODE == 'COOP')
    is_sp = (vbsp.GAME_MODE == 'SP')

    player_flags = {
        'sp': is_sp,
        'coop': is_coop,
        'atlas': is_coop or player_model == 'atlas',
        'pbody': is_coop or player_model == 'pbody',
        'bendy': is_sp and player_model == 'peti',
        'chell': is_sp and player_model == 'sp',
        'human': is_sp and player_model in ('peti', 'sp'),
        'robot': is_coop or player_model in ('atlas', 'pbody'),
    }
    # All which are True.
    player_flag_set = {val for val, flag in player_flags.items() if flag}

    # For each group, locate the voice lines.
    for group in itertools.chain(
            QUOTE_DATA.find_all('group'),
            QUOTE_DATA.find_all('midchamber'),
    ):  # type: Property

        quote_targetname = group['Choreo_Name', '@choreo']
        use_dings = group.bool('use_dings', allow_dings)

        possible_quotes = sorted(
            find_group_quotes(
                vmf,
                group,
                mid_quotes,
                use_dings=use_dings,
                allow_mid_voices=allow_mid_voices,
                conf=mid_config if group.name == 'midchamber' else norm_config,
                mid_name=quote_targetname,
                player_flag_set=player_flag_set,
            ),
            key=sort_func,
            reverse=True,
        )

        LOGGER.debug('Possible {}quotes:',
                     'mid ' if group.name == 'midchamber' else '')
        for quot in possible_quotes:
            LOGGER.debug('- {}', quot)

        if possible_quotes:
            choreo_loc = group.vec('choreo_loc', *quote_loc)

            if use_priority:
                chosen = possible_quotes[0].lines
            else:
                # Chose one of the quote blocks..
                random.seed('{}-VOICE_QUOTE_{}'.format(
                    map_seed,
                    len(possible_quotes),
                ))
                chosen = random.choice(possible_quotes).lines

            # Join the IDs for
            # the voice lines to the map seed,
            # so each quote block will chose different lines.
            random.seed(map_seed + '-VOICE_LINE_' +
                        '|'.join(prop['id', 'ID'] for prop in chosen))

            # Add one of the associated quotes
            add_quote(
                vmf,
                random.choice(chosen),
                quote_targetname,
                choreo_loc,
                style_vars,
                use_dings,
            )

    if ADDED_BULLSEYES or QUOTE_DATA.bool('UseMicrophones'):
        # Add microphones that broadcast audio directly at players.
        # This ensures it is heard regardless of location.
        # This is used for Cave and core Wheatley.
        LOGGER.info('Using microphones...')
        if vbsp.GAME_MODE == 'SP':
            vmf.create_ent(
                classname='env_microphone',
                targetname='player_speaker_sp',
                speakername='!player',
                maxRange='386',
                origin=quote_loc,
            )
        else:
            vmf.create_ent(
                classname='env_microphone',
                targetname='player_speaker_blue',
                speakername='!player_blue',
                maxRange='386',
                origin=quote_loc,
            )
            vmf.create_ent(
                classname='env_microphone',
                targetname='player_speaker_orange',
                speakername='!player_orange',
                maxRange='386',
                origin=quote_loc,
            )

    LOGGER.info('{} Mid quotes', len(mid_quotes))
    for mid_lines in mid_quotes:
        line = random.choice(mid_lines)
        mid_item, use_ding, mid_name = line
        add_quote(vmf, mid_item, mid_name, quote_loc, style_vars, use_ding)

    LOGGER.info('Done!')
Esempio n. 27
0
import math
from enum import Enum, EnumMeta

import inspect

from srctools import Property, Vec, parse_vec_str
from BEE2_config import ConfigFile
import srctools.logger

from typing import Union, Tuple, TypeVar, Type, Optional, Iterator, Any, TextIO

LOGGER = srctools.logger.get_logger(__name__)

SETTINGS = {}

ITEM_CONFIG = ConfigFile('item_cust_configs.cfg')


class TYPE(Enum):
    """The types arguments can have."""
    STR = str
    INT = int
    FLOAT = float
    BOOL = bool
    VEC = Vec

    def convert(self, value: str) -> Any:
        """Convert a string to the desired argument type."""
        return self.value(value)

Esempio n. 28
0
def show(quote_pack):
    """Display the editing window."""
    global voice_item, config, config_mid, config_resp
    voice_item = quote_pack

    win.title('BEE2 - Configure "' + voice_item.selitem_data.name + '"')
    notebook = UI['tabs']

    quote_data = quote_pack.config

    os.makedirs('config/voice', exist_ok=True)
    config = ConfigFile('voice/' + quote_pack.id + '.cfg')
    config_mid = ConfigFile('voice/MID_' + quote_pack.id + '.cfg')
    config_resp = ConfigFile('voice/RESP_' + quote_pack.id + '.cfg')

    # Clear the transcript textbox
    text = UI['trans']
    text['state'] = 'normal'
    text.delete(1.0, END)
    text['state'] = 'disabled'

    # Destroy all the old tabs
    for tab in TABS.values():
        try:
            notebook.forget(tab)
        except TclError:
            pass
        tab.destroy()

    TABS.clear()

    for group in quote_data.find_all('quotes', 'group'):
        make_tab(
            group,
            config,
            TabTypes.NORM
        )

    # Merge all blocks into one
    mid_quotes = Property(
        'midChamber',
        list(itertools.chain.from_iterable(
            quote_data.find_all('quotes', 'midChamber')
        ))
    )

    if len(mid_quotes):
        make_tab(
            mid_quotes,
            config_mid,
            TabTypes.MIDCHAMBER,
        )

    responses = Property(
        'CoopResponses',
        list(itertools.chain.from_iterable(
            quote_data.find_all('quotes', 'CoopResponses')
        )),
    )

    if len(responses):
        make_tab(
            responses,
            config_resp,
            TabTypes.RESPONSE,
        )

    config.save()
    config_mid.save()
    config_resp.save()

    add_tabs()

    win.deiconify()
    win.lift(win.winfo_parent())
    utils.center_win(win)  # Center inside the parent
Esempio n. 29
0
        'brush': '0',
        'ent': '0',
        'overlay': '0',
        },
    }

PLAYER_MODELS = {
    'ATLAS': 'ATLAS',
    'PBODY': 'P-Body',
    'SP': 'Chell',
    'PETI': 'Bendy',
}
PLAYER_MODEL_ORDER = ['Bendy', 'Chell', 'ATLAS', 'P-Body']
PLAYER_MODELS_REV = {value: key for key, value in PLAYER_MODELS.items()}

COMPILE_CFG = ConfigFile('compile.cfg')
COMPILE_CFG.set_defaults(COMPILE_DEFAULTS)
window = None
UI = {}

chosen_thumb = StringVar(
    value=COMPILE_CFG.get_val('Screenshot', 'Type', 'AUTO')
)
player_model_var = StringVar(
    value=PLAYER_MODELS.get(
        COMPILE_CFG.get_val('General', 'player_model', 'PETI'),
        PLAYER_MODELS['PETI'],
    )
)
start_in_elev = IntVar(value=0)
cust_file_loc = COMPILE_CFG.get_val('Screenshot', 'Loc', '')
Esempio n. 30
0
        'brush': '0',
        'ent': '0',
        'overlay': '0',
        },
    }

PLAYER_MODELS = {
    'ATLAS': 'ATLAS',
    'PBODY': 'P-Body',
    'SP': 'Chell',
    'PETI': 'Bendy',
}
PLAYER_MODEL_ORDER = ['Bendy', 'Chell', 'ATLAS', 'P-Body']
PLAYER_MODELS_REV = {value: key for key, value in PLAYER_MODELS.items()}

COMPILE_CFG = ConfigFile('compile.cfg')
COMPILE_CFG.set_defaults(COMPILE_DEFAULTS)
window = None
UI = {}

chosen_thumb = StringVar(
    value=COMPILE_CFG.get_val('Screenshot', 'Type', 'AUTO')
)
tk_screenshot = None # The preview image shown

# Location we copy custom screenshots to
SCREENSHOT_LOC = os.path.abspath(os.path.join(
        os.getcwd(),
        '..',
        'config',
        'screenshot.jpg'