예제 #1
0
def find_group_quotes(group, mid_quotes, conf):
    """Scan through a group, looking for applicable quote options."""
    is_mid = group.name == 'midinst'
    group_id = group['name']

    for quote in group.find_all('quote'):
        valid_quote = True
        for flag in quote:
            name = flag.name
            if name in ('priority', 'name', 'line', 'line_sp', 'line_coop'):
                # Not flags!
                continue
            if not conditions.check_flag(flag, fake_inst):
                valid_quote = False
                break

        quote_id = quote['id', quote['name', '']]

        utils.con_log(quote_id, valid_quote)

        if valid_quote:
            # Check if the ID is enabled!
            if conf.get_bool(group_id, quote_id, True):
                if ALLOW_MID_VOICES and is_mid:
                    mid_quotes.extend(mode_quotes(quote))
                else:
                    inst_list = list(mode_quotes(quote))
                    if inst_list:
                        yield (
                            quote['priority', '0'],
                            inst_list,
                        )
예제 #2
0
    def test(self, inst, remove_vmf=True):
        """Try to satisfy this condition on the given instance."""
        if not self.valid:
            return
        success = True
        for flag in self.flags:
            # utils.con_log('Flag: ' + repr(flag))
            if not check_flag(flag, inst):
                success = False
                break
            # utils.con_log(success)
        if remove_vmf:
            # our suffixes won't touch the .vmf extension
            inst['file'] = inst['file', ''][:-4]

        results = self.results if success else self.else_results
        for res in results:
            try:
                func = RESULT_LOOKUP[res.name]
            except KeyError:
                utils.con_log(
                    '"{}" is not a valid condition result!'.format(
                        res.real_name,
                    )
                )
            else:
                func(inst, res)

        if remove_vmf and not inst['file'].endswith('vmf'):
            inst['file'] += '.vmf'
예제 #3
0
def variant_weight(var):
    """Read variant commands from settings and create the weight list."""
    count = var["number", ""]
    if count.isdecimal():
        count = int(count)
        weight = var["weights", ""]
        if weight == "" or "," not in weight:
            utils.con_log("Invalid weight! (" + weight + ")")
            weight = [str(i) for i in range(1, count + 1)]
        else:
            # Parse the weight
            vals = weight.split(",")
            weight = []
            if len(vals) == count:
                for i, val in enumerate(vals):
                    val = val.strip()
                    if val.isdecimal():
                        # repeat the index the correct number of times
                        weight.extend(str(i + 1) for _ in range(1, int(val) + 1))
                    else:
                        # Abandon parsing
                        break
            if len(weight) == 0:
                utils.con_log("Failed parsing weight! ({!s})".format(weight))
                weight = [str(i) for i in range(1, count + 1)]
        # random.choice(weight) will now give an index with the correct
        # probabilities.
        return weight
    else:
        return [""]  # This won't append anything to the file
예제 #4
0
def find_group_quotes(group, mid_quotes, conf):
    """Scan through a group, looking for applicable quote options."""
    is_mid = group.name == 'midinst'
    group_id = group['name']

    for quote in group.find_all('quote'):
        valid_quote = True
        for flag in quote:
            name = flag.name
            if name in ('priority', 'name', 'line', 'line_sp', 'line_coop'):
                # Not flags!
                continue
            if not conditions.check_flag(flag, fake_inst):
                valid_quote = False
                break

        quote_id = quote['id', quote['name', '']]

        utils.con_log(quote_id, valid_quote)

        if valid_quote:
            # Check if the ID is enabled!
            if conf.get_bool(group_id, quote_id, True):
                if ALLOW_MID_VOICES and is_mid:
                    mid_quotes.extend(mode_quotes(quote))
                else:
                    inst_list = list(mode_quotes(quote))
                    if inst_list:
                        yield (
                            quote['priority', '0'],
                            inst_list,
                            )
예제 #5
0
def res_fizzler_pair(begin_inst, res):
    """Modify the instance of a fizzler to link with its pair."""
    orig_target = begin_inst['targetname']

    if 'modelEnd' in orig_target:
        return  # We only execute starting from the start side.

    orig_target = orig_target[:-11]  # remove "_modelStart"
    end_name = orig_target + '_modelEnd'  # What we search for

    # The name all these instances get
    pair_name = orig_target + '-model' + str(begin_inst.id)

    orig_file = begin_inst['file']

    begin_file = res['StartInst', orig_file]
    end_file = res['EndInst', orig_file]
    mid_file = res['MidInst', '']

    begin_inst['file'] = begin_file
    begin_inst['targetname'] = pair_name

    angles = Vec.from_str(begin_inst['angles'])
    # We round it to get rid of 0.00001 inprecision from the calculations.
    direction = round(Vec(0, 0, 1).rotate(angles.x, angles.y, angles.z))
    ':type direction: utils.Vec'
    print(end_name, direction)

    begin_pos = Vec.from_str(begin_inst['origin'])
    axis_1, axis_2, main_axis = PAIR_AXES[direction.as_tuple()]
    for end_inst in VMF.by_class['func_instance']:
        if end_inst['targetname', ''] != end_name:
            # Only examine this barrier hazard's instances!
            continue
        end_pos = Vec.from_str(end_inst['origin'])
        if (
                begin_pos[axis_1] == end_pos[axis_1] and
                begin_pos[axis_2] == end_pos[axis_2]
                ):
            length = int(end_pos[main_axis] - begin_pos[main_axis])
            break
    else:
        utils.con_log('No matching pair for {}!!'.format(orig_target))
        return
    end_inst['targetname'] = pair_name
    end_inst['file'] = end_file

    if mid_file != '':
        # Go 64 from each side, and always have at least 1 section
        # A 128 gap will have length = 0
        for dis in range(0, abs(length) + 1, 128):
            new_pos = begin_pos + direction*dis
            VMF.create_ent(
                classname='func_instance',
                targetname=pair_name,
                angles=begin_inst['angles'],
                file=mid_file,
                origin=new_pos.join(' '),
            )
예제 #6
0
def load_config():
    global CONF
    utils.con_log('Loading Settings...')
    try:
        with open("bee2/vrad_config.cfg") as config:
            CONF = Property.parse(config, 'bee2/vrad_config.cfg').find_key(
                'Config', [])
    except FileNotFoundError:
        pass
    utils.con_log('Config Loaded!')
예제 #7
0
def debug_flag(inst, props):
    if props.has_children():
        utils.con_log("Debug:")
        utils.con_log(str(props))
        utils.con_log(str(inst))
    elif props.value.endswith("="):
        utils.con_log("Debug: {props}{inst!s}".format(inst=inst, props=props.value))
    else:
        utils.con_log("Debug: " + props.value)
    return True  # The flag is always true
예제 #8
0
def res_fizzler_pair(begin_inst, res):
    """Modify the instance of a fizzler to link with its pair."""
    orig_target = begin_inst["targetname"]

    if "modelEnd" in orig_target:
        return  # We only execute starting from the start side.

    orig_target = orig_target[:-11]  # remove "_modelStart"
    end_name = orig_target + "_modelEnd"  # What we search for

    # The name all these instances get
    pair_name = orig_target + "-model" + str(begin_inst.id)

    orig_file = begin_inst["file"]

    begin_file = res["StartInst", orig_file]
    end_file = res["EndInst", orig_file]
    mid_file = res["MidInst", ""]

    begin_inst["file"] = begin_file
    begin_inst["targetname"] = pair_name

    angles = Vec.from_str(begin_inst["angles"])
    # We round it to get rid of 0.00001 inprecision from the calculations.
    direction = Vec(0, 0, 1).rotate(angles.x, angles.y, angles.z)
    ":type direction: utils.Vec"

    begin_pos = Vec.from_str(begin_inst["origin"])
    axis_1, axis_2, main_axis = PAIR_AXES[direction.as_tuple()]
    for end_inst in VMF.by_class["func_instance"]:
        if end_inst["targetname", ""] != end_name:
            # Only examine this barrier hazard's instances!
            continue
        end_pos = Vec.from_str(end_inst["origin"])
        if begin_pos[axis_1] == end_pos[axis_1] and begin_pos[axis_2] == end_pos[axis_2]:
            length = int(end_pos[main_axis] - begin_pos[main_axis])
            break
    else:
        utils.con_log("No matching pair for {}!!".format(orig_target))
        return
    end_inst["targetname"] = pair_name
    end_inst["file"] = end_file

    if mid_file != "":
        # Go 64 from each side, and always have at least 1 section
        # A 128 gap will have length = 0
        for dis in range(0, abs(length) + 1, 128):
            new_pos = begin_pos + direction * dis
            VMF.create_ent(
                classname="func_instance",
                targetname=pair_name,
                angles=begin_inst["angles"],
                file=mid_file,
                origin=new_pos.join(" "),
            )
예제 #9
0
파일: vrad.py 프로젝트: Stendec-UA/BEE2.4
def load_config():
    global CONF
    utils.con_log('Loading Settings...')
    try:
        with open("bee2/vrad_config.cfg") as config:
            CONF = Property.parse(config, 'bee2/vrad_config.cfg').find_key(
                'Config', []
            )
    except FileNotFoundError:
        pass
    utils.con_log('Config Loaded!')
예제 #10
0
def pack_file(zipfile, filename):
    """Check multiple locations for a resource file.
    """
    for poss_path in RES_ROOT:
        full_path = os.path.normpath(os.path.join(poss_path, filename))
        if os.path.isfile(full_path):
            zipfile.write(
                filename=full_path,
                arcname=filename,
            )
            break
    else:
        utils.con_log('"bee2/' + filename + '" not found!')
예제 #11
0
def parse_package(zip_file, info, pak_id, disp_name):
    """Parse through the given package to find all the components."""
    for pre in Property.find_key(info, 'Prerequisites', []).value:
        if pre.value not in packages:
            utils.con_log(
                'Package "' +
                pre.value +
                '" required for "' +
                pak_id +
                '" - ignoring package!'
            )
            return False
    objects = 0
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in OBJ_TYPES:
        allow_dupes = OBJ_TYPES[comp_type].allow_mult
        # Look for overrides
        for obj in info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                ParseData(zip_file, obj_id, obj, pak_id)
            )

        for obj in info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                if allow_dupes:
                    # Pretend this is an override
                    obj_override[comp_type][obj_id].append(
                        ParseData(zip_file, obj_id, obj, pak_id)
                    )
                else:
                    raise Exception('ERROR! "' + obj_id + '" defined twice!')
            objects += 1
            all_obj[comp_type][obj_id] = ObjData(
                zip_file,
                obj,
                pak_id,
                disp_name,
            )

    img_count = 0
    img_loc = os.path.join('resources', 'bee2')
    for item in zip_names(zip_file):
        item = os.path.normcase(item).casefold()
        if item.startswith("resources"):
            extract_packages.res_count += 1
            if item.startswith(img_loc):
                img_count += 1
    return objects, img_count
예제 #12
0
def check_all():
    """Check all conditions."""
    utils.con_log('Checking Conditions...')
    for condition in conditions:
        if condition.valid:
            for inst in VMF.by_class['func_instance']:
                    try:
                        condition.test(inst)
                    except SkipCondition:
                        # This is raised to immediately stop running
                        # this condition, and skip to the next instance.
                        pass
                    if len(condition.results) == 0:
                        break

    remove_blank_inst()

    utils.con_log('Map has attributes: ', [
        key
        for key, value in
        VOICE_ATTR.items()
        if value
    ])
    utils.con_log('Style Vars:', dict(STYLE_VARS.items()))
    utils.con_log('Global instances: ', GLOBAL_INSTANCES)
예제 #13
0
def check_flag(flag, inst):
    # print('Checking {type} ({val!s} on {inst}'.format(
    #     type=flag.real_name,
    #     val=flag.value,
    #     inst=inst['file'],
    # ))
    try:
        func = FLAG_LOOKUP[flag.name]
    except KeyError:
        utils.con_log('"' + flag.name + '" is not a valid condition flag!')
        return False
    else:
        res = func(inst, flag)
        return res
예제 #14
0
def run_vrad(args):
    "Execute the original VRAD."

    if utils.MAC:
        os_suff = '_osx'
    elif utils.LINUX:
        os_suff = '_linux'
    else:
        os_suff = ''

    joined_args = (
        '"' + os.path.normpath(
            os.path.join(os.getcwd(), "vrad" + os_suff + "_original")) + '" ' +
        " ".join(
            # put quotes around args which contain spaces
            (quote(x) if " " in x else x) for x in args))
    utils.con_log("Calling original VRAD...")
    utils.con_log(joined_args)
    code = subprocess.call(
        joined_args,
        stdout=None,
        stderr=subprocess.PIPE,
        shell=True,
    )
    if code == 0:
        utils.con_log("Done!")
    else:
        utils.con_log("VRAD failed! (" + str(code) + ")")
        sys.exit(code)
예제 #15
0
파일: vrad.py 프로젝트: Stendec-UA/BEE2.4
def run_vrad(args):
    "Execute the original VRAD."

    if utils.MAC:
        os_suff = '_osx'
    elif utils.LINUX:
        os_suff = '_linux'
    else:
        os_suff = ''

    joined_args = (
        '"' + os.path.normpath(
            os.path.join(os.getcwd(), "vrad" + os_suff + "_original")
            ) +
        '" ' +
        " ".join(
            # put quotes around args which contain spaces
            (quote(x) if " " in x else x)
            for x in args
            )
        )
    utils.con_log("Calling original VRAD...")
    utils.con_log(joined_args)
    code = subprocess.call(
        joined_args,
        stdout=None,
        stderr=subprocess.PIPE,
        shell=True,
    )
    if code == 0:
        utils.con_log("Done!")
    else:
        utils.con_log("VRAD failed! (" + str(code) + ")")
        sys.exit(code)
예제 #16
0
파일: vrad.py 프로젝트: Stendec-UA/BEE2.4
def pack_file(zipfile, filename):
    """Check multiple locations for a resource file.
    """
    for poss_path in RES_ROOT:
        full_path = os.path.normpath(
            os.path.join(poss_path, filename)
        )
        if os.path.isfile(full_path):
            zipfile.write(
                filename=full_path,
                arcname=filename,
            )
            break
    else:
        utils.con_log('"bee2/' + filename + '" not found!')
예제 #17
0
def place_catwalk_connections(instances, point_a, point_b):
    """Place catwalk sections to connect two straight points."""
    diff = point_b - point_a

    # The horizontal unit vector in the direction we are placing catwalks
    direction = diff.copy()
    direction.z = 0
    distance = direction.len() - 128
    direction = direction.norm()

    if diff.z > 0:
        angle = INST_ANGLE[direction.as_tuple()]
        # We need to add stairs
        for stair_pos in range(0, int(diff.z), 128):
            # Move twice the vertical horizontally
            # plus 128 so we don't start in point A
            loc = point_a + (2 * stair_pos + 128) * direction
            # Do the vertical offset
            loc.z += stair_pos
            VMF.create_ent(classname="func_instance", origin=loc.join(" "), angles=angle, file=instances["stair"])
        # This is the location we start flat sections at
        point_a = loc + 128 * direction
        point_a.z += 128
    elif diff.z < 0:
        # We need to add downward stairs
        # They point opposite to normal ones
        utils.con_log("down from", point_a)
        angle = INST_ANGLE[(-direction).as_tuple()]
        for stair_pos in range(0, -int(diff.z), 128):
            utils.con_log(stair_pos)
            # Move twice the vertical horizontally
            loc = point_a + (2 * stair_pos + 256) * direction
            # Do the vertical offset plus additional 128 units
            # to account for the moved instance
            loc.z -= stair_pos + 128
            VMF.create_ent(classname="func_instance", origin=loc.join(" "), angles=angle, file=instances["stair"])
        # Adjust point A to be at the end of the catwalks
        point_a = loc
    # Remove the space the stairs take up from the horiz distance
    distance -= abs(diff.z) * 2

    # Now do straight sections
    utils.con_log("Stretching ", distance, direction)
    angle = INST_ANGLE[direction.as_tuple()]
    loc = point_a + (direction * 128)

    # Figure out the most efficent number of sections
    for segment_len in utils.fit(distance, [512, 256, 128]):
        VMF.create_ent(
            classname="func_instance",
            origin=loc.join(" "),
            angles=angle,
            file=instances["straight_" + str(segment_len)],
        )
        utils.con_log(loc)
        loc += segment_len * direction
예제 #18
0
 def test(self, inst):
     """Try to satisfy this condition on the given instance."""
     success = True
     for flag in self.flags:
         if not check_flag(flag, inst):
             success = False
             break
     results = self.results if success else self.else_results
     for res in results[:]:
         try:
             func = RESULT_LOOKUP[res.name]
         except KeyError:
             utils.con_log('"{}" is not a valid condition result!'.format(res.real_name))
         else:
             should_del = func(inst, res)
             if should_del is True:
                 results.remove(res)
예제 #19
0
def res_cust_output(inst, res):
    """Add an additional output to the instance with any values.

    Always points to the targeted item.
    """
    over_name = '@' + inst['targetname'] + '_indicator'
    for toggle in VMF.by_class['func_instance']:
        if toggle.fixup['indicator_name', ''] == over_name:
            toggle_name = toggle['targetname']
            break
    else:
        toggle_name = ''  # we want to ignore the toggle instance, if it exists

    # Make this a set to ignore repeated targetnames
    targets = {o.target for o in inst.outputs if o.target != toggle_name}

    kill_signs = utils.conv_bool(res["remIndSign", '0'], False)
    dec_con_count = utils.conv_bool(res["decConCount", '0'], False)
    targ_conditions = list(res.find_all("targCondition"))

    pan_files = resolve_inst('[indPan]')

    if kill_signs or dec_con_count or targ_conditions:
        for con_inst in VMF.by_class['func_instance']:
            if con_inst['targetname'] in targets:
                if kill_signs and con_inst in pan_files:
                    VMF.remove_ent(con_inst)
                if targ_conditions:
                    for cond in targ_conditions:
                        cond.value.test(con_inst)
                if dec_con_count and 'connectioncount' in con_inst.fixup:
                    # decrease ConnectionCount on the ents,
                    # so they can still process normal inputs
                    try:
                        val = int(con_inst.fixup['connectioncount'])
                        con_inst.fixup['connectioncount'] = str(val-1)
                    except ValueError:
                        # skip if it's invalid
                        utils.con_log(
                            con_inst['targetname'] +
                            ' has invalid ConnectionCount!'
                        )
    for targ in targets:
        for out in res.find_all('addOut'):
            add_output(inst, out, targ)
예제 #20
0
def parse_package(zip_file, info, pak_id, disp_name):
    """Parse through the given package to find all the components."""
    for pre in Property.find_key(info, 'Prerequisites', []).value:
        if pre.value not in packages:
            utils.con_log('Package "' + pre.value + '" required for "' +
                          pak_id + '" - ignoring package!')
            return False
    objects = 0
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in OBJ_TYPES:
        allow_dupes = OBJ_TYPES[comp_type].allow_mult
        # Look for overrides
        for obj in info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                ParseData(zip_file, obj_id, obj, pak_id))

        for obj in info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                if allow_dupes:
                    # Pretend this is an override
                    obj_override[comp_type][obj_id].append(
                        ParseData(zip_file, obj_id, obj, pak_id))
                else:
                    raise Exception('ERROR! "' + obj_id + '" defined twice!')
            objects += 1
            all_obj[comp_type][obj_id] = ObjData(
                zip_file,
                obj,
                pak_id,
                disp_name,
            )

    img_count = 0
    img_loc = os.path.join('resources', 'bee2')
    for item in zip_names(zip_file):
        item = os.path.normcase(item).casefold()
        if item.startswith("resources"):
            extract_packages.res_count += 1
            if item.startswith(img_loc):
                img_count += 1
    return objects, img_count
예제 #21
0
def parse_package(zip_file, info, pak_id, disp_name):
    """Parse through the given package to find all the components."""
    global res_count
    for pre in Property.find_key(info, 'Prerequisites', []).value:
        if pre.value not in packages:
            utils.con_log(
                'Package "' +
                pre.value +
                '" required for "' +
                pak_id +
                '" - ignoring package!'
            )
            return False
    objects = 0
    # First read through all the components we have, so we can match
    # overrides to the originals
    for comp_type in obj_types:
        # Look for overrides
        for obj in info.find_all("Overrides", comp_type):
            obj_id = obj['id']
            obj_override[comp_type][obj_id].append(
                (zip_file, obj)
            )

        for obj in info.find_all(comp_type):
            obj_id = obj['id']
            if obj_id in all_obj[comp_type]:
                raise Exception('ERROR! "' + obj_id + '" defined twice!')
            objects += 1
            all_obj[comp_type][obj_id] = ObjData(
                zip_file,
                obj,
                pak_id,
                disp_name,
            )

    if res_count != -1:
        for item in zip_names(zip_file):
            if item.startswith("resources"):
                res_count += 1
        loader.set_length("RES", res_count)
    return objects
예제 #22
0
def find_group_quotes(group, mid_quotes, conf):
    is_mid = group.name == 'midinst'
    group_id = group['name']

    for quote in group.find_all('quote'):
        valid_quote = True
        for flag in quote:
            name = flag.name
            if name == 'instance':
                continue
            # break out if a flag is unsatisfied
            if name == 'has' and map_attr[flag.value.casefold()] is False:
                valid_quote = False
                break
            elif name == 'nothas' and map_attr[flag.value.casefold()] is True:
                valid_quote = False
                break
            elif (name == 'stylevartrue' and
                    style_vars[flag.value.casefold()] is False):
                valid_quote = False
                break
            elif (name == 'stylevarfalse' and
                    style_vars[flag.value.casefold()] is True):
                valid_quote = False
                break

        quote_id = quote['id', quote['name', '']]

        utils.con_log(quote_id, valid_quote)

        if valid_quote:
            # Check if the ID is enabled!
            if conf.get_bool(group_id, quote_id, True):
                if ALLOW_MID_VOICES and is_mid:
                    mid_quotes.extend(quote.find_all('instance'))
                else:
                    inst_list = list(quote.find_all('instance'))
                    if inst_list:
                        yield (
                            quote['priority', '0'],
                            inst_list,
                            )
예제 #23
0
def check_all():
    """Check all conditions."""
    utils.con_log("Checking Conditions...")
    for condition in conditions:
        for inst in VMF.by_class["func_instance"]:
            try:
                condition.test(inst)
            except NextInstance:
                # This is raised to immediately stop running
                # this condition, and skip to the next instance.
                pass
            except EndCondition:
                # This is raised to immediately stop running
                # this condition, and skip to the next condtion.
                break
            if not condition.results and not condition.else_results:
                utils.con_log("Exiting empty condition!")
                break  # Condition has run out of results, quit early

    utils.con_log("Map has attributes: ", [key for key, value in VOICE_ATTR.items() if value])
    utils.con_log("Style Vars:", dict(STYLE_VARS.items()))
    utils.con_log("Global instances: ", GLOBAL_INSTANCES)
예제 #24
0
def find_packages(pak_dir, zips, zip_name_lst):
    """Search a folder for packages, recursing if necessary."""
    found_pak = False
    for name in os.listdir(pak_dir): # Both files and dirs
        name = os.path.join(pak_dir, name)
        is_dir = os.path.isdir(name)
        if name.endswith('.zip') and os.path.isfile(name):
            zip_file = ZipFile(name)
        elif is_dir:
            zip_file = FakeZip(name)
        else:
            utils.con_log('Extra file: ', name)
            continue

        if 'info.txt' in zip_file.namelist():  # Is it valid?
            zips.append(zip_file)
            zip_name_lst.append(os.path.abspath(name))
            print('Reading package "' + name + '"')
            with zip_file.open('info.txt') as info_file:
                info = Property.parse(info_file, name + ':info.txt')
            pak_id = info['ID']
            disp_name = info['Name', pak_id]
            packages[pak_id] = PackageData(
                zip_file,
                info,
                name,
                disp_name,
            )
            found_pak = True
        else:
            if is_dir:
                # This isn't a package, so check the subfolders too...
                print('Checking subdir "{}" for packages...'.format(name))
                find_packages(name, zips, zip_name_lst)
            else:
                zip_file.close()
                print('ERROR: Bad package "{}"!'.format(name))
    if not found_pak:
        print('No packages in folder!')
예제 #25
0
def find_packages(pak_dir, zips, zip_name_lst):
    """Search a folder for packages, recursing if necessary."""
    found_pak = False
    for name in os.listdir(pak_dir):  # Both files and dirs
        name = os.path.join(pak_dir, name)
        is_dir = os.path.isdir(name)
        if name.endswith('.zip') and os.path.isfile(name):
            zip_file = ZipFile(name)
        elif is_dir:
            zip_file = FakeZip(name)
        else:
            utils.con_log('Extra file: ', name)
            continue

        if 'info.txt' in zip_file.namelist():  # Is it valid?
            zips.append(zip_file)
            zip_name_lst.append(os.path.abspath(name))
            print('Reading package "' + name + '"')
            with zip_file.open('info.txt') as info_file:
                info = Property.parse(info_file, name + ':info.txt')
            pak_id = info['ID']
            disp_name = info['Name', pak_id]
            packages[pak_id] = PackageData(
                zip_file,
                info,
                name,
                disp_name,
            )
            found_pak = True
        else:
            if is_dir:
                # This isn't a package, so check the subfolders too...
                print('Checking subdir "{}" for packages...'.format(name))
                find_packages(name, zips, zip_name_lst)
            else:
                zip_file.close()
                print('ERROR: Bad package "{}"!'.format(name))
    if not found_pak:
        print('No packages in folder!')
예제 #26
0
def resolve(path) -> list:
    """Replace an instance path with the values it refers to.

    Valid paths:
    - "<ITEM_ID:1,2,5>": matches the given indexes for that item.
    - "<ITEM_ID:cube_black, cube_white>": the same, with strings for indexes
    - "[spExitCorridor]": Hardcoded shortcuts for specific items

    This returns a list of instances which match the selector.
    """

    if path.startswith('<') and path.endswith('>'):
        path = path[1:-1]
        if ':' in path:  # We have a set of subitems to parse
            item, subitem = path.split(':')
            try:
                item_values = INSTANCE_FILES[item]
            except KeyError:
                utils.con_log(
                    '"{}" not a valid item!'.format(item)
                )
                return []
            out = set()
            for val in subitem.split(','):
                ind = SUBITEMS.get(
                    val.strip().casefold(),
                    int(val.strip()),
                )
                # Only add if it's actually in range
                if 0 <= ind < len(item_values):
                    out.add(item_values[ind])
            return list(out)
        else:
            try:
                return INSTANCE_FILES[path]
            except KeyError:
                utils.con_log(
                    '"{}" not a valid item!'.format(path)
                )
                return []
    elif path.startswith('[') and path.endswith(']'):
        path = path[1:-1].casefold()
        try:
            return INST_SPECIAL[path]
        except KeyError:
            utils.con_log('"{}" not a valid instance category!'.format(path))
            return []
    else:  # Just a normal path
        return [path.casefold()]
예제 #27
0
def res_cust_fizzler(base_inst, res):
    """Modify a fizzler item to allow for custom brush ents."""
    from vbsp import TEX_FIZZLER

    model_name = res["modelname", None]
    make_unique = utils.conv_bool(res["UniqueModel", "0"])
    fizz_name = base_inst["targetname", ""]

    # search for the model instances
    model_targetnames = (fizz_name + "_modelStart", fizz_name + "_modelEnd")
    is_laser = False
    for inst in VMF.by_class["func_instance"]:
        if inst["targetname", ""] in model_targetnames:
            if inst.fixup["skin", "0"] == "2":
                is_laser = True
            if model_name is not None:
                if model_name == "":
                    inst["targetname"] = base_inst["targetname"]
                else:
                    inst["targetname"] = base_inst["targetname"] + "-" + model_name
            if make_unique:
                inst.make_unique()

            for key, value in base_inst.fixup.items():
                inst.fixup[key] = value

    new_brush_config = list(res.find_all("brush"))
    if len(new_brush_config) == 0:
        return  # No brush modifications

    if is_laser:
        # This is a laserfield! We can't edit those brushes!
        utils.con_log("CustFizzler excecuted on LaserField!")
        return

    for orig_brush in VMF.by_class["trigger_portal_cleanser"] & VMF.by_target[fizz_name + "_brush"]:
        print(orig_brush)
        VMF.remove_ent(orig_brush)
        for config in new_brush_config:
            new_brush = orig_brush.copy()
            VMF.add_ent(new_brush)
            new_brush.clear_keys()  # Wipe the original keyvalues
            new_brush["origin"] = orig_brush["origin"]
            new_brush["targetname"] = fizz_name + "-" + config["name", "brush"]
            # All ents must have a classname!
            new_brush["classname"] = "trigger_portal_cleanser"

            for prop in config["keys", []]:
                new_brush[prop.name] = prop.value

            laserfield_conf = config.find_key("MakeLaserField", None)
            if laserfield_conf.value is not None:
                # Resize the brush into a laserfield format, without
                # the 128*64 parts. If the brush is 128x128, we can
                # skip the resizing since it's already correct.
                laser_tex = laserfield_conf["texture", "effects/laserplane"]
                nodraw_tex = laserfield_conf["nodraw", "tools/toolsnodraw"]
                tex_width = utils.conv_int(laserfield_conf["texwidth", "512"], 512)
                is_short = False
                for side in new_brush.sides():
                    if side.mat.casefold() == "effects/fizzler":
                        is_short = True
                        break

                if is_short:
                    for side in new_brush.sides():
                        if side.mat.casefold() == "effects/fizzler":
                            side.mat = laser_tex

                            uaxis = side.uaxis.split(" ")
                            vaxis = side.vaxis.split(" ")
                            # the format is like "[1 0 0 -393.4] 0.25"
                            side.uaxis = " ".join(uaxis[:3]) + " 0] 0.25"
                            side.vaxis = " ".join(vaxis[:4]) + " 0.25"
                        else:
                            side.mat = nodraw_tex
                else:
                    # The hard part - stretching the brush.
                    convert_to_laserfield(new_brush, laser_tex, nodraw_tex, tex_width)
            else:
                # Just change the textures
                for side in new_brush.sides():
                    try:
                        side.mat = config[TEX_FIZZLER[side.mat.casefold()]]
                    except (KeyError, IndexError):
                        # If we fail, just use the original textures
                        pass
예제 #28
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!')
예제 #29
0
def resolve(path) -> list:
    """Replace an instance path with the values it refers to.

    Valid paths:
    - "<ITEM_ID:1,2,5>": matches the given indexes for that item.
    - "<ITEM_ID:cube_black, cube_white>": the same, with strings for indexes
    - "[spExitCorridor]": Hardcoded shortcuts for specific items

    This returns a list of instances which match the selector.
    When using <> values, the '' path will never be returned.
    """

    if path.startswith('<') and path.endswith('>'):
        path = path[1:-1]
        if ':' in path:  # We have a set of subitems to parse
            item, subitem = path.split(':')
            try:
                item_values = INSTANCE_FILES[item]
            except KeyError:
                utils.con_log(
                    '"{}" not a valid item!'.format(item)
                )
                return []
            out = []
            for val in subitem.split(','):
                ind = SUBITEMS.get(val.strip().casefold(), None)
                if ind is None:
                    try:
                        ind = int(val.strip())
                    except ValueError as e:
                        utils.con_log('--------\nValid subitems:')
                        utils.con_log('\n'.join(
                            ('> ' + k + ' = ' + str(v))
                            for k, v in
                            SUBITEMS.items()
                        ))
                        utils.con_log('--------')
                        raise Exception(
                            '"' + val + '" is not a valid instance'
                            ' subtype or index!'
                        )
                # Only add if it's actually in range, and skip "" values
                if 0 <= ind < len(item_values) and item_values[ind] != '':
                    out.append(item_values[ind])
            return out
        else:
            try:
                # Skip "" instances
                return [
                    inst for inst in
                    INSTANCE_FILES[path]
                    if inst != ''
                    ]
            except KeyError:
                utils.con_log(
                    '"{}" not a valid item!'.format(path)
                )
                return []
    elif path.startswith('[') and path.endswith(']'):
        path = path[1:-1].casefold()
        try:
            return INST_SPECIAL[path]
        except KeyError:
            utils.con_log('"{}" not a valid instance category!'.format(path))
            return []
    else:  # Just a normal path
        return [path.casefold()]
예제 #30
0
            "-textureshadows",
            ):
        # remove final parameters from the modified arguments
        fast_args.remove(a)
    elif a in ('-force_peti', '-force_hammer', '-no_pack'):
        # we need to strip these out, otherwise VBSP 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

utils.con_log("Map path is " + path)
if path == "":
    raise Exception("No map passed!")

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

if '-force_peti' in args or '-force_hammer' in args:
    # we have override command!
    if '-force_peti' in args:
        utils.con_log('OVERRIDE: Applying cheap lighting!')
        is_peti = True
    else:
        utils.con_log('OVERRIDE: Preserving args!')
        is_peti = False
else:
예제 #31
0
def res_cust_fizzler(base_inst, res):
    """Modify a fizzler item to allow for custom brush ents."""
    model_name = res['modelname', None]
    make_unique = utils.conv_bool(res['UniqueModel', '0'])
    fizz_name = base_inst['targetname', '']

    # search for the model instances
    model_targetnames = (
        fizz_name + '_modelStart',
        fizz_name + '_modelEnd',
        )
    for inst in VMF.by_class['func_instance']:
        if inst['targetname', ''] in model_targetnames:
            if inst.fixup['skin', '0'] == '2':
                # This is a laserfield! We can't edit that!
                utils.con_log('CustFizzler excecuted on LaserField!')
                return
            if model_name is not None:
                if model_name == '':
                    inst['targetname'] = base_inst['targetname']
                else:
                    inst['targetname'] = (
                        base_inst['targetname'] +
                        '-' +
                        model_name
                    )
            if make_unique:
                inst.make_unique()

            for key, value in base_inst.fixup.items():
                inst.fixup[key] = value
    new_brush_config = list(res.find_all('brush'))
    if len(new_brush_config) == 0:
        return  # No brush modifications
    for orig_brush in (
            VMF.by_class['trigger_portal_cleanser'] &
            VMF.by_target[fizz_name + '_brush']):
        print(orig_brush)
        VMF.remove_ent(orig_brush)
        for config in new_brush_config:
            new_brush = orig_brush.copy()
            VMF.add_ent(new_brush)
            new_brush.clear_keys()  # Wipe the original keyvalues
            new_brush['origin'] = orig_brush['origin']
            new_brush['targetname'] = (
                fizz_name +
                '-' +
                config['name', 'brush']
            )
            # All ents must have a classname!
            new_brush['classname'] = 'trigger_portal_cleanser'

            for prop in config['keys', []]:
                new_brush[prop.name] = prop.value

            laserfield_conf = config.find_key('MakeLaserField', None)
            if laserfield_conf.value is not None:
                # Resize the brush into a laserfield format, without
                # the 128*64 parts. If the brush is 128x128, we can
                # skip the resizing since it's already correct.
                laser_tex = laserfield_conf['texture', 'effects/laserplane']
                nodraw_tex = laserfield_conf['nodraw', 'tools/toolsnodraw']
                tex_width = utils.conv_int(
                    laserfield_conf['texwidth', '512'], 512
                )
                is_short = False
                for side in new_brush.sides():
                    if side.mat.casefold() == 'effects/fizzler':
                        is_short = True
                        break

                if is_short:
                    for side in new_brush.sides():
                        if side.mat.casefold() == 'effects/fizzler':
                            side.mat = laser_tex

                            uaxis = side.uaxis.split(" ")
                            vaxis = side.vaxis.split(" ")
                            # the format is like "[1 0 0 -393.4] 0.25"
                            side.uaxis = ' '.join(uaxis[:3]) + ' 0] 0.25'
                            side.vaxis = ' '.join(vaxis[:4]) + ' 0.25'
                        else:
                            side.mat = nodraw_tex
                else:
                    # The hard part - stretching the brush.
                    convert_to_laserfield(
                        new_brush,
                        laser_tex,
                        nodraw_tex,
                        tex_width,
                    )
            else:
                # Just change the textures
                for side in new_brush.sides():
                    try:
                        side.mat = config[
                            vbsp.TEX_FIZZLER[side.mat.casefold()]
                        ]
                    except (KeyError, IndexError):
                        # If we fail, just use the original textures
                        pass
예제 #32
0
def add_voice(
        voice_data,
        has_items,
        style_vars_,
        vmf_file,
        timer_config=utils.EmptyMapping,
        mode='SP',
        ):
    """Add a voice line to the map."""
    global ALLOW_MID_VOICES, VMF, map_attr, style_vars
    print('Adding Voice!')

    if len(voice_data.value) == 0:
        print('No Data!')
        return

    VMF = vmf_file
    map_attr = has_items
    style_vars = style_vars_

    if mode == 'SP':
        norm_config = ConfigFile('SP.cfg', root='bee2')
        mid_config = ConfigFile('MID_SP.cfg', root='bee2')
    else:
        norm_config = ConfigFile('COOP.cfg', root='bee2')
        mid_config = ConfigFile('MID_COOP.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:
            # If we have a timer value, we go that many back from the
            # highest priority item. If it fails, default to the first.
            timer_val = timer_config.get(
                group['config', ''].casefold(),
                '3')
            try:
                timer_val = int(timer_val) - 3
            except ValueError:
                timer_val = 0

            choreo_loc = group['choreo_loc', quote_loc]

            utils.con_log('Timer value: ', timer_val)

            try:
                chosen = possible_quotes[timer_val][1]
            except IndexError:
                chosen = possible_quotes[0][1]

            utils.con_log('Chosen: {!r}'.format(chosen))

            # Add all the associated quotes

            for prop in chosen:
                add_quote(prop, 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!')
예제 #33
0
def res_track_plat(_, res):
    """Logic specific to Track Platforms.

    This allows switching the instances used depending on if the track
    is horizontal or vertical and sets the track
    targetnames to a useful value.
    """
    # Get the instances from editoritems
    (inst_bot_grate, inst_bottom, inst_middle, inst_top, inst_plat, inst_plat_oscil, inst_single) = resolve_inst(
        res["orig_item"]
    )
    single_plat_inst = res["single_plat", ""]
    track_targets = res["track_name", ""]

    track_files = [inst_bottom, inst_middle, inst_top, inst_single]
    platforms = [inst_plat, inst_plat_oscil]

    # All the track_set in the map, indexed by origin
    track_instances = {
        Vec.from_str(inst["origin"]).as_tuple(): inst
        for inst in VMF.by_class["func_instance"]
        if inst["file"].casefold() in track_files
    }
    utils.con_log("Track instances:")
    utils.con_log("\n".join("{!s}: {}".format(k, v["file"]) for k, v in track_instances.items()))

    # Now we loop through all platforms in the map, and then locate their
    # track_set
    for plat_inst in VMF.by_class["func_instance"]:
        if plat_inst["file"].casefold() not in platforms:
            continue  # Not a platform!

        utils.con_log('Modifying "' + plat_inst["targetname"] + '"!')

        plat_loc = Vec.from_str(plat_inst["origin"])
        angles = Vec.from_str(plat_inst["angles"])
        # The direction away from the wall/floor/ceil
        normal = Vec(0, 0, 1).rotate(angles.x, angles.y, angles.z)

        for tr_origin, first_track in track_instances.items():
            if plat_loc == tr_origin:
                # Check direction

                if normal == Vec(0, 0, 1).rotate(*Vec.from_str(first_track["angles"])):
                    break
        else:
            raise Exception('Platform "{}" has no track!'.format(plat_inst["targetname"]))

        track_type = first_track["file"].casefold()
        if track_type == inst_single:
            # Track is one block long, use a single-only instance and
            # remove track!
            plat_inst["file"] = single_plat_inst
            first_track.remove()
            continue  # Next platform

        track_set = set()
        if track_type == inst_top or track_type == inst_middle:
            # search left
            track_scan(track_set, track_instances, first_track, middle_file=inst_middle, x_dir=-1)
        if track_type == inst_bottom or track_type == inst_middle:
            # search right
            track_scan(track_set, track_instances, first_track, middle_file=inst_middle, x_dir=+1)

        # Give every track a targetname matching the platform
        for ind, track in enumerate(track_set, start=1):
            if track_targets == "":
                track["targetname"] = plat_inst["targetname"]
            else:
                track["targetname"] = plat_inst["targetname"] + "-" + track_targets + str(ind)

        # Now figure out which way the track faces:

        # The direction horizontal track is offset
        side_dir = Vec(0, 1, 0).rotate(*Vec.from_str(first_track["angles"]))

        # The direction of the platform surface
        facing = Vec(-1, 0, 0).rotate(angles.x, angles.y, angles.z)
        if side_dir == facing:
            track_facing = "HORIZ"
        elif side_dir == -facing:
            track_facing = "HORIZ_MIRR"
        else:
            track_facing = "VERT"
        # Now add the suffixes
        if track_facing == "VERT":
            if utils.conv_bool(res["vert_suffix", ""]):
                for inst in track_set:
                    add_suffix(inst, "_vert")
                if utils.conv_bool(res["plat_suffix", ""]):
                    add_suffix(plat_inst, "_vert")
        elif track_facing == "HORIZ_MIRR":
            if utils.conv_bool(res["horiz_suffix", ""]):
                for inst in track_set:
                    add_suffix(inst, "_horiz_mirrored")
                if utils.conv_bool(res["plat_suffix", ""]):
                    add_suffix(plat_inst, "_horiz")
        else:  # == 'HORIZ'
            if utils.conv_bool(res["horiz_suffix", ""]):
                for inst in track_set:
                    add_suffix(inst, "_horiz")
                if utils.conv_bool(res["plat_suffix", ""]):
                    add_suffix(plat_inst, "_horiz")
    return True  # Only run once!
예제 #34
0
파일: vrad.py 프로젝트: Stendec-UA/BEE2.4
def mod_screenshots():
    """Modify the map's screenshot."""
    mod_type = CONF['screenshot_type', 'PETI'].lower()

    if mod_type == 'cust':
        utils.con_log('Using custom screenshot!')
        scr_loc = CONF['screenshot', '']
    elif mod_type == 'auto':
        utils.con_log('Using automatic screenshot!')
        scr_loc = None
        # The automatic screenshots are found at this location:
        auto_path = os.path.join(
            '..',
            'portal2',
            '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.
        screens = [
            os.path.join(auto_path, path)
            for path in
            os.listdir(auto_path)
        ]
        screens.sort(
            key=os.path.getmtime,
            reverse=True,
            # Go from most recent to least
        )
        playtested = False
        for scr_shot in screens:
            utils.con_log(scr_shot)
            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:
                utils.con_log('Screenshot "{scr}" too old ({diff!s})'.format(
                    scr=scr_shot, diff=diff
                ))
                continue

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

        if utils.conv_bool(CONF['clean_screenshots', '0']):
            utils.con_log('Cleaning up screenshots...')
            # Clean up this folder - otherwise users will get thousands of
            # pics in there!
            for screen in screens:
                if screen != scr_loc:
                    os.remove(screen)
            utils.con_log('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_screenshots():
            utils.con_log('Replacing "{}"...'.format(screen))
            # Allow us to edit the file...
            unset_readonly(screen)
            shutil.copy(scr_loc, screen)
            # Make the screenshot readonly, so P2 can't replace it.
            # Then it'll use our own
            set_readonly(screen)

    else:
        if mod_type != 'peti':
            # Error if the screenshot doesn't exist
            utils.con_log('"{}" not found!'.format(scr_loc))
        utils.con_log('Using PeTI screenshot!')
        for screen in find_screenshots():
            # Make the screenshot writeable, so P2 will replace it
            utils.con_log('Making "{}" replaceable...'.format(screen))
            unset_readonly(screen)
예제 #35
0
def res_make_catwalk(_, res):
    """Speciallised result to generate catwalks from markers.

    Only runs once, and then quits the condition list.
    """
    utils.con_log("Starting catwalk generator...")
    marker = resolve_inst(res["markerInst"])
    output_target = res["output_name", "MARKER"]

    instances = {
        name: resolve_inst(res[name, ""])[0]
        for name in (
            "straight_128",
            "straight_256",
            "straight_512",
            "corner",
            "tjunction",
            "crossjunction",
            "end",
            "stair",
            "end_wall",
            "support_wall",
            "support_ceil",
            "support_floor",
            "single_wall",
            "markerInst",
        )
    }
    # If there are no attachments remove a catwalk piece
    instances["NONE"] = ""
    if instances["end_wall"] == "":
        instances["end_wall"] = instances["end"]

    connections = {}  # The directions this instance is connected by (NSEW)
    markers = {}

    for inst in VMF.by_class["func_instance"]:
        if inst["file"].casefold() not in marker:
            continue
        #                   [North, South, East,  West ]
        connections[inst] = [False, False, False, False]
        markers[inst["targetname"]] = inst

    if not markers:
        return True  # No catwalks!

    utils.con_log("Conn:", connections)
    utils.con_log("Markers:", markers)

    # First loop through all the markers, adding connecting sections
    for inst in markers.values():
        for conn in inst.outputs:
            if conn.output != output_target or conn.input != output_target:
                # Indicator toggles or similar, delete these
                print("Removing ", conn.target)
                for del_inst in VMF.by_target[conn.target]:
                    del_inst.remove()
                continue

            inst2 = markers[conn.target]
            print(inst["targetname"], "<->", inst2["targetname"])
            origin1 = Vec.from_str(inst["origin"])
            origin2 = Vec.from_str(inst2["origin"])
            if origin1.x != origin2.x and origin1.y != origin2.y:
                utils.con_log("Instances not aligned!")
                continue

            y_dir = origin1.x == origin2.x  # Which way the connection is
            if y_dir:
                dist = abs(origin1.y - origin2.y)
            else:
                dist = abs(origin1.x - origin2.x)
            vert_dist = origin1.z - origin2.z

            utils.con_log("Dist =", dist, ", Vert =", vert_dist)

            if dist // 2 < vert_dist:
                # The stairs are 2 long, 1 high.
                utils.con_log("Not enough room for stairs!")
                continue

            if dist > 128:
                # add straight sections in between
                place_catwalk_connections(instances, origin1, origin2)

            # Update the lists based on the directions that were set
            conn_lst1 = connections[inst]
            conn_lst2 = connections[inst2]
            if origin1.x < origin2.x:
                conn_lst1[2] = True  # E
                conn_lst2[3] = True  # W
            elif origin2.x < origin1.x:
                conn_lst1[3] = True  # W
                conn_lst2[2] = True  # E

            if origin1.y < origin2.y:
                conn_lst1[0] = True  # N
                conn_lst2[1] = True  # S
            elif origin2.y < origin1.y:
                conn_lst1[1] = True  # S
                conn_lst2[0] = True  # N

        inst.outputs.clear()  # Remove the outputs now, they're useless

    for inst, dir_mask in connections.items():
        # Set the marker instances based on the attached walkways.
        print(inst["targetname"], dir_mask)
        angle = Vec.from_str(inst["angles"], 0, 0, 0)
        new_type, inst["angles"] = utils.CONN_LOOKUP[tuple(dir_mask)]
        inst["file"] = instances[CATWALK_TYPES[new_type]]

        normal = Vec(0, 0, 1).rotate(angle.x, angle.y, angle.z)
        ":type normal: Vec"

        if new_type is utils.CONN_TYPES.side:
            # If the end piece is pointing at a wall, switch the instance.
            if normal.z == 0:
                # Treat booleans as ints to get the direction the connection is
                # in - True == 1, False == 0
                conn_dir = Vec(x=dir_mask[2] - dir_mask[3], y=dir_mask[0] - dir_mask[1], z=0)  # +E, -W  # +N, -S,
                if normal == conn_dir:
                    inst["file"] = instances["end_wall"]
            continue  # We never have normal supports on end pieces
        elif new_type is utils.CONN_TYPES.none:
            # Unconnected catwalks on the wall switch to a special instance.
            # This lets players stand next to a portal surface on the wall.
            if normal.z == 0:
                inst["file"] = instances["single_wall"]
                inst["angles"] = INST_ANGLE[normal.as_tuple()]
            else:
                inst.remove()
            continue  # These don't get supports otherwise

        # Add regular supports
        if normal == (0, 0, 1):
            supp = instances["support_floor"]
        elif normal == (0, 0, -1):
            supp = instances["support_ceil"]
        else:
            supp = instances["support_wall"]

        if supp:
            VMF.create_ent(
                classname="func_instance", origin=inst["origin"], angles=INST_ANGLE[normal.as_tuple()], file=supp
            )

    utils.con_log("Finished catwalk generation!")
    return True  # Don't run this again
예제 #36
0
파일: vrad.py 프로젝트: Stendec-UA/BEE2.4
def main(argv):
    utils.con_log('BEE2 VRAD hook started!')
    args = " ".join(argv)
    fast_args = argv[1:]
    full_args = argv[1:]

    path = argv[-1]  # The path is the last argument to vrad
    fast_args[-1] = os.path.normpath(path)

    utils.con_log("Map path is " + path)
    if path == "":
        raise Exception("No map passed!")

    load_config()

    for a in fast_args[:]:
        if a.casefold() in (
                "-both",
                "-final",
                "-staticproplighting",
                "-staticproppolys",
                "-textureshadows",
                ):
            # remove final parameters from the modified arguments
            fast_args.remove(a)
        elif a in ('-force_peti', '-force_hammer', '-no_pack'):
            # we need to strip these out, otherwise VBSP 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 '-force_peti' in args or '-force_hammer' in args:
        # we have override command!
        if '-force_peti' in args:
            utils.con_log('OVERRIDE: Applying cheap lighting!')
            is_peti = True
        else:
            utils.con_log('OVERRIDE: Preserving args!')
            is_peti = False
    else:
        # If we don't get the special -force args, check for the name
        # equalling preview to determine if we should convert
        # If that is false, check the config file to see what was
        # specified there.
        is_peti = (
            os.path.basename(path) == "preview.bsp" or
            utils.conv_bool(CONF['force_full'], False)
        )

    mod_screenshots()

    if is_peti:
        utils.con_log("Forcing Cheap Lighting!")
        run_vrad(fast_args)
    else:
        utils.con_log("Hammer map detected! Not forcing cheap lighting..")
        run_vrad(full_args)

    if '-no_pack' not in args:
        pack_content(path)
    else:
        utils.con_log("No items to pack!")
    utils.con_log("BEE2 VRAD hook finished!")
예제 #37
0
def mod_screenshots():
    """Modify the map's screenshot."""
    mod_type = CONF['screenshot_type', 'PETI'].lower()

    if mod_type == 'cust':
        utils.con_log('Using custom screenshot!')
        scr_loc = CONF['screenshot', '']
    elif mod_type == 'auto':
        utils.con_log('Using automatic screenshot!')
        scr_loc = None
        # The automatic screenshots are found at this location:
        auto_path = os.path.join('..', 'portal2', '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.
        screens = [
            os.path.join(auto_path, path) for path in os.listdir(auto_path)
        ]
        screens.sort(
            key=os.path.getmtime,
            reverse=True,
            # Go from most recent to least
        )
        playtested = False
        for scr_shot in screens:
            utils.con_log(scr_shot)
            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:
                utils.con_log('Screenshot "{scr}" too old ({diff!s})'.format(
                    scr=scr_shot, diff=diff))
                continue

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

        if utils.conv_bool(CONF['clean_screenshots', '0']):
            utils.con_log('Cleaning up screenshots...')
            # Clean up this folder - otherwise users will get thousands of
            # pics in there!
            for screen in screens:
                if screen != scr_loc:
                    os.remove(screen)
            utils.con_log('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_screenshots():
            utils.con_log('Replacing "{}"...'.format(screen))
            # Allow us to edit the file...
            unset_readonly(screen)
            shutil.copy(scr_loc, screen)
            # Make the screenshot readonly, so P2 can't replace it.
            # Then it'll use our own
            set_readonly(screen)

    else:
        if mod_type != 'peti':
            # Error if the screenshot doesn't exist
            utils.con_log('"{}" not found!'.format(scr_loc))
        utils.con_log('Using PeTI screenshot!')
        for screen in find_screenshots():
            # Make the screenshot writeable, so P2 will replace it
            utils.con_log('Making "{}" replaceable...'.format(screen))
            unset_readonly(screen)
예제 #38
0
def pack_content(path):
    """Pack any custom content into the map."""
    files = set()
    try:
        pack_list = open(path[:-4] + '.filelist.txt')
    except (IOError, FileNotFoundError):
        pass
    else:
        with pack_list:
            for line in pack_list:
                files.add(line.strip().lower())

    if '' in files:
        # Allow blank lines in original files
        files.remove('')

    if not files:
        utils.con_log('No files to pack!')
        return

    utils.con_log('Files to pack:')
    for file in sorted(files):
        utils.con_log(' # "' + file + '"')

    utils.con_log("Packing Files!")
    bsp_file = BSP(path)
    utils.con_log(' - Header read')
    bsp_file.read_header()

    # Manipulate the zip entirely in memory
    zip_data = BytesIO()
    zip_data.write(bsp_file.get_lump(BSP_LUMPS.PAKFILE))
    zipfile = ZipFile(zip_data, mode='a')
    utils.con_log(' - Existing zip read')

    for file in files:
        pack_file(zipfile, file)

    utils.con_log(' - Added files')

    zipfile.close()  # Finalise the zip modification

    # Copy the zipfile into the BSP file, and adjust the headers
    bsp_file.replace_lump(
        path,
        BSP_LUMPS.PAKFILE,
        zip_data.getvalue(),  # Get the binary data we need
    )
    utils.con_log(' - BSP written!')

    utils.con_log("Packing complete!")
예제 #39
0
def main(argv):
    utils.con_log('BEE2 VRAD hook started!')
    args = " ".join(argv)
    fast_args = argv[1:]
    full_args = argv[1:]

    path = argv[-1]  # The path is the last argument to vrad
    fast_args[-1] = os.path.normpath(path)

    utils.con_log("Map path is " + path)
    if path == "":
        raise Exception("No map passed!")

    load_config()

    for a in fast_args[:]:
        if a.casefold() in (
                "-both",
                "-final",
                "-staticproplighting",
                "-staticproppolys",
                "-textureshadows",
        ):
            # remove final parameters from the modified arguments
            fast_args.remove(a)
        elif a in ('-force_peti', '-force_hammer', '-no_pack'):
            # we need to strip these out, otherwise VBSP 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 '-force_peti' in args or '-force_hammer' in args:
        # we have override command!
        if '-force_peti' in args:
            utils.con_log('OVERRIDE: Applying cheap lighting!')
            is_peti = True
        else:
            utils.con_log('OVERRIDE: Preserving args!')
            is_peti = False
    else:
        # If we don't get the special -force args, check for the name
        # equalling preview to determine if we should convert
        # If that is false, check the config file to see what was
        # specified there.
        is_peti = (os.path.basename(path) == "preview.bsp"
                   or utils.conv_bool(CONF['force_full'], False))

    mod_screenshots()

    if is_peti:
        utils.con_log("Forcing Cheap Lighting!")
        run_vrad(fast_args)
    else:
        utils.con_log("Hammer map detected! Not forcing cheap lighting..")
        run_vrad(full_args)

    if '-no_pack' not in args:
        pack_content(path)
    else:
        utils.con_log("No items to pack!")
    utils.con_log("BEE2 VRAD hook finished!")
예제 #40
0
def add_quote(quote, targetname, quote_loc):
    """Add a quote to the map."""
    utils.con_log('Adding quote: ', quote)

    for prop in quote:
        name = prop.name.casefold()
        if name == 'file':
            VMF.create_ent(
                classname='func_instance',
                targetname='',
                file=INST_PREFIX + prop.value,
                origin=quote_loc,
                fixup_style='2',  # No fixup
            )
        elif name == 'choreo':

            c_line = prop.value
            # Add this to the beginning, since all scenes need it...
            if not c_line.startswith('scenes/'):
                c_line = 'scenes/' + c_line

            VMF.create_ent(
                classname='logic_choreographed_scene',
                targetname=targetname,
                origin=quote_loc,
                scenefile=c_line,
                busyactor="1",  # Wait for actor to stop talking
                onplayerdeath='0',
            )
        elif name == 'snd':
            VMF.create_ent(
                classname='ambient_generic',
                spawnflags='49',  # Infinite Range, Starts Silent
                targetname=targetname,
                origin=quote_loc,
                message=prop.value,
                health='10',  # Volume
            )
        elif name == 'ambientchoreo':
            # For some lines, they don't play globally. Workaround this
            # by placing an ambient_generic and choreo ent, and play the
            # sound when the choreo starts.
            VMF.create_ent(
                classname='ambient_generic',
                spawnflags='49',  # Infinite Range, Starts Silent
                targetname=targetname + '_snd',
                origin=quote_loc,
                message=prop['File'],
                health='10',  # Volume
            )

            c_line = prop['choreo']
            # Add this to the beginning, since all scenes need it...
            if not c_line.startswith('scenes/'):
                c_line = 'scenes/' + c_line

            choreo = VMF.create_ent(
                classname='logic_choreographed_scene',
                targetname=targetname,
                origin=quote_loc,
                scenefile=c_line,
                busyactor="1",  # Wait for actor to stop talking
                onplayerdeath='0',
            )
            choreo.outputs.append(
                vmfLib.Output('OnStart', targetname + '_snd', 'PlaySound'))
        elif name == 'bullseye':
            # Cave's voice lines require a special named bullseye to
            # work correctly.

            # Don't add the same one more than once.
            if prop.value not in ADDED_BULLSEYES:
                VMF.create_ent(
                    classname='npc_bullseye',
                    # Not solid, Take No Damage, Think outside PVS
                    spawnflags='222224',
                    targetname=prop.value,
                    origin=quote_loc,
                    angles='0 0 0',
                )
                ADDED_BULLSEYES.add(prop.value)
        elif name == 'setstylevar':
            # Set this stylevar to True
            # This is useful so some styles can react to which line was
            # chosen.
            style_vars[prop.value.casefold()] = True
예제 #41
0
파일: vrad.py 프로젝트: Stendec-UA/BEE2.4
def pack_content(path):
    """Pack any custom content into the map."""
    files = set()
    try:
        pack_list = open(path[:-4] + '.filelist.txt')
    except (IOError, FileNotFoundError):
        pass
    else:
        with pack_list:
            for line in pack_list:
                files.add(line.strip().lower())

    if '' in files:
        # Allow blank lines in original files
        files.remove('')

    if not files:
        utils.con_log('No files to pack!')
        return

    utils.con_log('Files to pack:')
    for file in sorted(files):
        utils.con_log(' # "' + file + '"')

    utils.con_log("Packing Files!")
    bsp_file = BSP(path)
    utils.con_log(' - Header read')
    bsp_file.read_header()

    # Manipulate the zip entirely in memory
    zip_data = BytesIO()
    zip_data.write(bsp_file.get_lump(BSP_LUMPS.PAKFILE))
    zipfile = ZipFile(zip_data, mode='a')
    utils.con_log(' - Existing zip read')

    for file in files:
        pack_file(zipfile, file)

    utils.con_log(' - Added files')

    zipfile.close()  # Finalise the zip modification

    # Copy the zipfile into the BSP file, and adjust the headers
    bsp_file.replace_lump(
        path,
        BSP_LUMPS.PAKFILE,
        zip_data.getvalue(),  # Get the binary data we need
    )
    utils.con_log(' - BSP written!')

    utils.con_log("Packing complete!")