Ejemplo n.º 1
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)
Ejemplo n.º 2
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)
Ejemplo 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_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)
Ejemplo 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)
Ejemplo n.º 5
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)
Ejemplo n.º 6
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', '')
cust_file_loc_var = StringVar(value='')

count_brush = IntVar(value=0)
count_ents = IntVar(value=0)
count_overlay = IntVar(value=0)
Ejemplo n.º 7
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)
Ejemplo n.º 8
0
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'
))

VOICE_PRIORITY_VAR = IntVar(
    value=COMPILE_CFG.get_bool('General', 'use_voice_priority', True)
)
Ejemplo n.º 9
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!")