示例#1
0
    def parse(cls, data):
        """Parse an item definition."""
        versions = {}
        def_version = None
        folders = {}
        unstyled = utils.conv_bool(data.info['unstyled', '0'])

        glob_desc = list(desc_parse(data.info))
        desc_last = utils.conv_bool(data.info['AllDescLast', '0'])

        all_config = get_config(
            data.info,
            data.zip_file,
            'items',
            pak_id=data.pak_id,
            prop_name='all_conf',
        )

        needs_unlock = utils.conv_bool(data.info['needsUnlock', '0'])

        for ver in data.info.find_all('version'):
            vals = {
                'name':    ver['name', 'Regular'],
                'id':      ver['ID', 'VER_DEFAULT'],
                'is_wip': utils.conv_bool(ver['wip', '0']),
                'is_dep':  utils.conv_bool(ver['deprecated', '0']),
                'styles':  {},
                'def_style': None,
                }
            for sty_list in ver.find_all('styles'):
                for sty in sty_list:
                    if vals['def_style'] is None:
                        vals['def_style'] = sty.value
                    vals['styles'][sty.real_name] = sty.value
                    folders[sty.value] = True
            versions[vals['id']] = vals
            if def_version is None:
                def_version = vals

        parse_item_folder(folders, data.zip_file, data.pak_id)

        for ver in versions.values():
            if ver['def_style'] in folders:
                ver['def_style'] = folders[ver['def_style']]
            for sty, fold in ver['styles'].items():
                ver['styles'][sty] = folders[fold]

        if not versions:
            raise ValueError('Item "' + data.id + '" has no versions!')

        return cls(
            data.id,
            versions=versions,
            def_version=def_version,
            needs_unlock=needs_unlock,
            all_conf=all_config,
            unstyled=unstyled,
            glob_desc=glob_desc,
            desc_last=desc_last,
        )
示例#2
0
def flag_brush_at_loc(inst, flag):
    """Checks to see if a wall is present at the given location.

    - Pos is the position of the brush, where `0 0 0` is the floor-position
       of the brush.
    - Dir is the normal the face is pointing. (0 0 -1) is 'up'.
    - Type defines the type the brush must be:
      - "Any" requires either a black or white brush.
      - "None" means that no brush must be present.
      - "White" requires a portalable surface.
      - "Black" requires a non-portalable surface.
    - SetVar defines an instvar which will be given a value of "black",
      "white" or "none" to allow the result to be reused.
    - If gridPos is true, the position will be snapped so it aligns with
      the 128 brushes (Useful with fizzler/light strip items).
    - RemoveBrush: If set to 1, the brush will be removed if found.
      Only do this to EmbedFace brushes, since it will remove the other
      sides as well.
    """
    from conditions import VMF
    pos = Vec.from_str(flag['pos', '0 0 0'])
    pos.z -= 64  # Subtract so origin is the floor-position
    pos = pos.rotate_by_str(inst['angles', '0 0 0'])

    # Relative to the instance origin
    pos += Vec.from_str(inst['origin', '0 0 0'])

    norm = Vec.from_str(flag['dir', '0 0 -1']).rotate_by_str(
        inst['angles', '0 0 0']
    )

    if utils.conv_bool(flag['gridpos', '0']):
        for axis in 'xyz':
            # Don't realign things in the normal's axis -
            # those are already fine.
            if norm[axis] == 0:
                pos[axis] = pos[axis] // 128 * 128 + 64

    result_var = flag['setVar', '']
    should_remove = utils.conv_bool(flag['RemoveBrush', False], False)
    des_type = flag['type', 'any'].casefold()

    brush = SOLIDS.get(pos.as_tuple(), None)

    if brush is None or brush.normal != norm:
        br_type = 'none'
    else:
        br_type = str(brush.color)
        if should_remove:
            VMF.remove_brush(
                brush.solid,
            )

    if result_var:
        inst.fixup[result_var] = br_type

    if des_type == 'any' and br_type != 'none':
        return True

    return des_type == br_type
示例#3
0
    def parse(cls, data):
        """Parse an item definition."""
        versions = {}
        def_version = None
        folders = {}
        unstyled = utils.conv_bool(data.info['unstyled', '0'])

        glob_desc = list(desc_parse(data.info))
        desc_last = utils.conv_bool(data.info['AllDescLast', '0'])

        all_config = get_config(
            data.info,
            data.zip_file,
            'items',
            pak_id=data.pak_id,
            prop_name='all_conf',
        )

        needs_unlock = utils.conv_bool(data.info['needsUnlock', '0'])

        for ver in data.info.find_all('version'):
            vals = {
                'name': ver['name', 'Regular'],
                'id': ver['ID', 'VER_DEFAULT'],
                'is_wip': utils.conv_bool(ver['wip', '0']),
                'is_dep': utils.conv_bool(ver['deprecated', '0']),
                'styles': {},
                'def_style': None,
            }
            for sty_list in ver.find_all('styles'):
                for sty in sty_list:
                    if vals['def_style'] is None:
                        vals['def_style'] = sty.value
                    vals['styles'][sty.real_name] = sty.value
                    folders[sty.value] = True
            versions[vals['id']] = vals
            if def_version is None:
                def_version = vals

        parse_item_folder(folders, data.zip_file, data.pak_id)

        for ver in versions.values():
            if ver['def_style'] in folders:
                ver['def_style'] = folders[ver['def_style']]
            for sty, fold in ver['styles'].items():
                ver['styles'][sty] = folders[fold]

        if not versions:
            raise ValueError('Item "' + data.id + '" has no versions!')

        return cls(
            data.id,
            versions=versions,
            def_version=def_version,
            needs_unlock=needs_unlock,
            all_conf=all_config,
            unstyled=unstyled,
            glob_desc=glob_desc,
            desc_last=desc_last,
        )
示例#4
0
def res_unst_scaffold_setup(res):
    group = res["group", "DEFAULT_GROUP"]

    if group not in SCAFFOLD_CONFIGS:
        # Store our values in the CONFIGS dictionary
        targ_inst, links = SCAFFOLD_CONFIGS[group] = {}, {}
    else:
        # Grab the already-filled values, and add to them
        targ_inst, links = SCAFFOLD_CONFIGS[group]

    for block in res.find_all("Instance"):
        conf = {
            # If set, adjusts the offset appropriately
            "is_piston": utils.conv_bool(block["isPiston", "0"]),
            "rotate_logic": utils.conv_bool(block["AlterAng", "1"], True),
            "off_floor": Vec.from_str(block["FloorOff", "0 0 0"]),
            "off_wall": Vec.from_str(block["WallOff", "0 0 0"]),
            "logic_start": block["startlogic", ""],
            "logic_end": block["endLogic", ""],
            "logic_mid": block["midLogic", ""],
            "logic_start_rev": block["StartLogicRev", None],
            "logic_end_rev": block["EndLogicRev", None],
            "logic_mid_rev": block["EndLogicRev", None],
            "inst_wall": block["wallInst", ""],
            "inst_floor": block["floorInst", ""],
            "inst_offset": block["offsetInst", None],
            # Specially rotated to face the next track!
            "inst_end": block["endInst", None],
        }
        for logic_type in ("logic_start", "logic_mid", "logic_end"):
            if conf[logic_type + "_rev"] is None:
                conf[logic_type + "_rev"] = conf[logic_type]

        for inst in resolve_inst(block["file"]):
            targ_inst[inst] = conf

    # We need to provide vars to link the tracks and beams.
    for block in res.find_all("LinkEnt"):
        # The name for this set of entities.
        # It must be a '@' name, or the name will be fixed-up incorrectly!
        loc_name = block["name"]
        if not loc_name.startswith("@"):
            loc_name = "@" + loc_name
        links[block["nameVar"]] = {
            "name": loc_name,
            # The next entity (not set in end logic)
            "next": block["nextVar"],
            # A '*' name to reference all the ents (set on the start logic)
            "all": block["allVar", None],
        }

    return group  # We look up the group name to find the values.
示例#5
0
    def test_conv_bool(self):
        for val in true_strings:
            self.assertTrue(utils.conv_bool(val))
        for val in false_strings:
            self.assertFalse(utils.conv_bool(val))

        # Check that bools pass through
        self.assertTrue(utils.conv_bool(True))
        self.assertFalse(utils.conv_bool(False))

        # None passes through the default
        for val in def_vals:
            self.assertIs(utils.conv_bool(None, val), val)
示例#6
0
 def parse(cls, data):
     name = data.info['name']
     unstyled = utils.conv_bool(data.info['unstyled', '0'])
     default = utils.conv_bool(data.info['enabled', '0'])
     styles = [
         prop.value
         for prop in data.info.find_all('Style')
     ]
     return cls(
         data.id,
         name,
         styles,
         unstyled=unstyled,
         default=default,
     )
示例#7
0
 def parse(cls, data):
     name = data.info['name']
     unstyled = utils.conv_bool(data.info['unstyled', '0'])
     default = utils.conv_bool(data.info['enabled', '0'])
     styles = [prop.value for prop in data.info.find_all('Style')]
     desc = '\n'.join(prop.value
                      for prop in data.info.find_all('description'))
     return cls(
         data.id,
         name,
         styles,
         unstyled=unstyled,
         default=default,
         desc=desc,
     )
示例#8
0
def flag_angles(inst, flag):
    """Check that a instance is pointed in a direction."""
    angle = inst["angles", "0 0 0"]

    if flag.has_children():
        targ_angle = flag["direction", "0 0 0"]
        from_dir = flag["from_dir", "0 0 1"]
        if from_dir.casefold() in DIRECTIONS:
            from_dir = Vec(DIRECTIONS[from_dir.casefold()])
        else:
            from_dir = Vec.from_str(from_dir, 0, 0, 1)
        allow_inverse = utils.conv_bool(flag["allow_inverse", "0"])
    else:
        targ_angle = flag.value
        from_dir = Vec(0, 0, 1)
        allow_inverse = False

    if angle == targ_angle:
        return True  # Check for exact match

    normal = DIRECTIONS.get(targ_angle.casefold(), None)
    if normal is None:
        return False  # If it's not a special angle,
        # so it failed the exact match

    angle = Vec.from_str(angle, 0, 0, 0)
    inst_normal = from_dir.rotate(angle.x, angle.y, angle.z)

    if normal == "WALL":
        # Special case - it's not on the floor or ceiling
        return not (inst_normal == (0, 0, 1) or inst_normal == (0, 0, -1))
    else:
        return inst_normal == normal or (allow_inverse and -inst_normal == normal)
示例#9
0
def res_add_global_inst(_, res):
    """Add one instance in a location.

    Once this is executed, it will be ignored thereafter.
    """
    if res.value is not None:
        if utils.conv_bool(res["allow_multiple", "0"]) or res["file"] not in GLOBAL_INSTANCES:
            # By default we will skip adding the instance
            # if was already added - this is helpful for
            # items that add to original items, or to avoid
            # bugs.
            new_inst = VLib.Entity(
                VMF,
                keys={
                    "classname": "func_instance",
                    "targetname": res["name", ""],
                    "file": resolve_inst(res["file"])[0],
                    "angles": res["angles", "0 0 0"],
                    "origin": res["position", "0 0 -10000"],
                    "fixup_style": res["fixup_style", "0"],
                },
            )
            GLOBAL_INSTANCES.add(res["file"])
            if new_inst["targetname"] == "":
                new_inst["targetname"] = "inst_"
                new_inst.make_unique()
            VMF.add_ent(new_inst)
    return True  # Remove this result
示例#10
0
def res_rand_vec(inst, res):
    """A modification to RandomNum which generates a random vector instead.

    'decimal', 'seed' and 'ResultVar' work like RandomNum. min/max x/y/z
    are for each section. If the min and max are equal that number will be used
    instead.
    """
    is_float = utils.conv_bool(res['decimal'])
    var = res['resultvar', '$random']
    seed = res['seed', 'random']

    random.seed(inst['origin'] + inst['angles'] + 'random_' + seed)

    if is_float:
        func = random.uniform
    else:
        func = random.randint

    value = Vec()

    for axis in 'xyz':
        max_val = utils.conv_float(res['max_' + axis, 0.0])
        min_val = utils.conv_float(res['min_' + axis, 0.0])
        if min_val == max_val:
            value[axis] = min_val
        else:
            value[axis] = func(min_val, max_val)

    inst.fixup[var] = value.join(' ')
示例#11
0
def res_add_global_inst(_, res):
    """Add one instance in a location.

    Once this is executed, it will be ignored thereafter.
    """
    if res.value is not None:
        if (res['file'] not in GLOBAL_INSTANCES or
                utils.conv_bool(res['allow_multiple', '0'], True)):
            # By default we will skip adding the instance
            # if was already added - this is helpful for
            # items that add to original items, or to avoid
            # bugs.
            new_inst = VLib.Entity(VMF, keys={
                "classname": "func_instance",
                "targetname": res['name', ''],
                "file": resolve_inst(res['file'])[0],
                "angles": res['angles', '0 0 0'],
                "origin": res['position', '0 0 -10000'],
                "fixup_style": res['fixup_style', '0'],
                })
            GLOBAL_INSTANCES.append(res['file'])
            if new_inst['targetname'] == '':
                new_inst['targetname'] = "inst_"
                new_inst.make_unique()
            VMF.add_ent(new_inst)
            res.value = None  # Disable this
示例#12
0
def res_replace_instance(inst: VLib.Entity, res):
    """Replace an instance with another entity.

    'keys' and 'localkeys' defines the new keyvalues used.
    'targetname' and 'angles' are preset, and 'origin' will be used to offset
    the given amount from the current location.
    If 'keep_instance' is true, the instance entity will be kept instead of
    removed.
    """
    import vbsp

    origin = Vec.from_str(inst['origin'])
    angles = inst['angles']

    if not utils.conv_bool(res['keep_instance', '0'], False):
        inst.remove()  # Do this first to free the ent ID, so the new ent has
        # the same one.

    # We copy to allow us to still acess the $fixups and other values.
    new_ent = inst.copy(des_id=inst.id)
    new_ent.clear_keys()
    # Ensure there's a classname, just in case.
    new_ent['classname'] = 'info_null'

    vbsp.VMF.add_ent(new_ent)

    conditions.set_ent_keys(new_ent, inst, res)

    origin += Vec.from_str(new_ent['origin']).rotate_by_str(angles)
    new_ent['origin'] = origin
    new_ent['angles'] = angles
    new_ent['targetname'] = inst['targetname']
示例#13
0
def generate_resp_script(file, allow_dings):
    """Write the responses section into a file."""
    use_dings = allow_dings

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

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

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

    file.write('BEE2_PLAY_DING = {};\n'.format(
        'true' if use_dings else 'false'
    ))
示例#14
0
def res_rand_vec(inst, res):
    """A modification to RandomNum which generates a random vector instead.

    'decimal', 'seed' and 'ResultVar' work like RandomNum. min/max x/y/z
    are for each section. If the min and max are equal that number will be used
    instead.
    """
    is_float = utils.conv_bool(res["decimal"])
    var = res["resultvar", "$random"]
    seed = res["seed", "random"]

    random.seed(inst["origin"] + inst["angles"] + "random_" + seed)

    if is_float:
        func = random.uniform
    else:
        func = random.randint

    value = Vec()

    for axis in "xyz":
        max_val = utils.conv_float(res["max_" + axis, 0.0])
        min_val = utils.conv_float(res["min_" + axis, 0.0])
        if min_val == max_val:
            value[axis] = min_val
        else:
            value[axis] = func(min_val, max_val)

    inst.fixup[var] = value.join(" ")
示例#15
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)
示例#16
0
    def parse(cls, data):
        """Parse a style definition."""
        info = data.info
        selitem_data = get_selitem_data(info)
        base = info['base', '']
        has_video = utils.conv_bool(info['has_video', '1'])

        sugg = info.find_key('suggested', [])
        sugg = (
            sugg['quote', '<NONE>'],
            sugg['music', '<NONE>'],
            sugg['skybox', 'SKY_BLACK'],
            sugg['goo', 'GOO_NORM'],
            sugg['elev', '<NONE>'],
            )

        corridors = info.find_key('corridors', [])
        corridors = {
            'sp_entry': corridors.find_key('sp_entry', []),
            'sp_exit':  corridors.find_key('sp_exit', []),
            'coop':     corridors.find_key('coop', []),
        }

        short_name = selitem_data.short_name or None
        if base == '':
            base = None
        folder = 'styles/' + info['folder']
        config = folder + '/vbsp_config.cfg'
        with data.zip_file.open(folder + '/items.txt', 'r') as item_data:
            items = Property.parse(
                item_data,
                data.pak_id+':'+folder+'/items.txt'
            )

        try:
            with data.zip_file.open(config, 'r') as vbsp_config:
                vbsp = Property.parse(
                    vbsp_config,
                    data.pak_id+':'+config,
                )
        except KeyError:
            vbsp = None
        return cls(
            style_id=data.id,
            name=selitem_data.name,
            author=selitem_data.auth,
            desc=selitem_data.desc,
            icon=selitem_data.icon,
            editor=items,
            config=vbsp,
            base_style=base,
            short_name=short_name,
            suggested=sugg,
            has_video=has_video,
            corridor_names=corridors,
            )
示例#17
0
文件: globals.py 项目: goodDOS/BEE2.4
def flag_is_preview(_, flag):
    """Checks if the preview mode status equals the given value.

    If preview mode is enabled, the player will start before the entry
    door, and restart the map after reaching the exit door. If false,
    they start in the elevator.

    Preview mode is always False when publishing.
    """
    import vbsp
    return vbsp.IS_PREVIEW == utils.conv_bool(flag.value, False)
示例#18
0
    def parse(cls, data):
        """Parse an item definition."""
        versions = {}
        def_version = None
        folders = {}

        needs_unlock = utils.conv_bool(data.info['needsUnlock', '0'])

        for ver in data.info.find_all('version'):
            vals = {
                'name':    ver['name', 'Regular'],
                'id':      ver['ID', 'VER_DEFAULT'],
                'is_wip': utils.conv_bool(ver['wip', '0']),
                'is_dep':  utils.conv_bool(ver['deprecated', '0']),
                'styles':  {},
                'def_style': None,
                }
            for sty_list in ver.find_all('styles'):
                for sty in sty_list:
                    if vals['def_style'] is None:
                        vals['def_style'] = sty.value
                    vals['styles'][sty.real_name] = sty.value
                    folders[sty.value] = True
            versions[vals['id']] = vals
            if def_version is None:
                def_version = vals

        parse_item_folder(folders, data.zip_file)

        for ver in versions.values():
            if ver['def_style'] in folders:
                ver['def_style'] = folders[ver['def_style']]
            for sty, fold in ver['styles'].items():
                ver['styles'][sty] = folders[fold]

        if not versions:
            raise ValueError('Item "' + data.id + '" has no versions!')

        return cls(data.id, versions, def_version, needs_unlock)
示例#19
0
 def parse(cls, data):
     name = data.info['name']
     unstyled = utils.conv_bool(data.info['unstyled', '0'])
     default = utils.conv_bool(data.info['enabled', '0'])
     styles = [
         prop.value
         for prop in
         data.info.find_all('Style')
     ]
     desc = '\n'.join(
         prop.value
         for prop in
         data.info.find_all('description')
     )
     return cls(
         data.id,
         name,
         styles,
         unstyled=unstyled,
         default=default,
         desc=desc,
     )
示例#20
0
    def parse(cls, data):
        """Parse a style definition."""
        info = data.info
        selitem_data = get_selitem_data(info)
        base = info['base', '']
        has_video = utils.conv_bool(info['has_video', '1'])

        sugg = info.find_key('suggested', [])
        sugg = (
            sugg['quote', '<NONE>'],
            sugg['music', '<NONE>'],
            sugg['skybox', 'SKY_BLACK'],
            sugg['goo', 'GOO_NORM'],
            sugg['elev', '<NONE>'],
        )

        corridors = info.find_key('corridors', [])
        corridors = {
            'sp_entry': corridors.find_key('sp_entry', []),
            'sp_exit': corridors.find_key('sp_exit', []),
            'coop': corridors.find_key('coop', []),
        }

        if base == '':
            base = None
        folder = 'styles/' + info['folder']
        config = folder + '/vbsp_config.cfg'
        with data.zip_file.open(folder + '/items.txt', 'r') as item_data:
            items = Property.parse(item_data,
                                   data.pak_id + ':' + folder + '/items.txt')

        try:
            with data.zip_file.open(config, 'r') as vbsp_config:
                vbsp = Property.parse(
                    vbsp_config,
                    data.pak_id + ':' + config,
                )
        except KeyError:
            vbsp = None
        return cls(
            style_id=data.id,
            selitem_data=selitem_data,
            editor=items,
            config=vbsp,
            base_style=base,
            suggested=sugg,
            has_video=has_video,
            corridor_names=corridors,
        )
示例#21
0
def res_change_inputs_setup(res: Property):
    vals = {}
    for prop in res:
        out_key = VLib.Output.parse_name(prop.real_name)
        if prop.has_children():
            vals[out_key] = (
                prop['inst_in', None],
                prop['input'],
                prop['params', ''],
                utils.conv_float(prop['delay', 0.0]),
                1 if utils.conv_bool(prop['only_once', '0']) else -1,
            )
        else:
            vals[out_key] = None
    return vals
示例#22
0
def res_add_overlay_inst(inst, res):
    """Add another instance on top of this one."""
    print("adding overlay", res["file"])
    overlay_inst = VMF.create_ent(
        classname="func_instance",
        targetname=inst["targetname", ""],
        file=resolve_inst(res["file", ""])[0],
        angles=inst["angles", "0 0 0"],
        origin=inst["origin"],
        fixup_style=res["fixup_style", "0"],
    )
    if utils.conv_bool(res["copy_fixup", "1"]):
        # Copy the fixup values across from the original instance
        for fixup, value in inst.fixup.items():
            overlay_inst.fixup[fixup] = value
示例#23
0
def res_vactube_setup(res):
    group = res['group', 'DEFAULT_GROUP']

    if group not in VAC_CONFIGS:
        # Store our values in the CONFIGS dictionary
        config, inst_configs = VAC_CONFIGS[group] = {}, {}
    else:
        # Grab the already-filled values, and add to them
        config, inst_configs = VAC_CONFIGS[group]

    for block in res.find_all("Instance"):
        # Configuration info for each instance set..
        conf = {
            # The three sizes of corner instance
            ('corner', 1): block['corner_small_inst', ''],
            ('corner', 2): block['corner_medium_inst', ''],
            ('corner', 3): block['corner_large_inst', ''],

            ('corner_temp', 1): block['temp_corner_small', ''],
            ('corner_temp', 2): block['temp_corner_medium', ''],
            ('corner_temp', 3): block['temp_corner_large', ''],

            # Straight instances connected to the next part
            'straight': block['straight_inst', ''],

            # Supports attach to the 4 sides of the straight part,
            # if there's a brush there.
            'support': block['support_inst', ''],

            'is_tsection': utils.conv_bool(block['is_tsection', '0']),

            ('entry', 'wall'): block['entry_inst'],
            ('entry', 'floor'): block['entry_floor_inst'],
            ('entry', 'ceiling'): block['entry_ceil_inst'],

            'exit': block['exit_inst'],
        }

        for prop in block.find_all("File"):
            try:
                size, file = prop.value.split(":", 1)
            except ValueError:
                size = 1
                file = prop.value

            inst_configs[resolve_inst(file)[0]] = conf, utils.conv_int(size, 1)

    return group
示例#24
0
def res_cust_output_setup(res):
    conds = [
        Condition.parse(sub_res)
        for sub_res in res
        if sub_res.name == 'targcondition'
    ]
    outputs = list(res.find_all('addOut'))
    dec_con_count = utils.conv_bool(res["decConCount", '0'], False)
    sign_type = IND_PANEL_TYPES.get(res['sign_type', None], None)

    if sign_type is None:
        sign_act = sign_deact = (None, '')
    else:
        # The outputs which trigger the sign.
        sign_act = VLib.Output.parse_name(res['sign_activate', ''])
        sign_deact = VLib.Output.parse_name(res['sign_deactivate', ''])

    return outputs, dec_con_count, conds, sign_type, sign_act, sign_deact
示例#25
0
def flag_angles(inst, flag):
    """Check that a instance is pointed in a direction.

    The value should be either just the angle to check, or a block of
    options:
    - Angle: A unit vector (XYZ value) pointing in a direction, or some
        keywords: +z, -y, N/S/E/W, up/down, floor/ceiling, or walls
    - From_dir: The direction the unrotated instance is pointed in.
        This lets the flag check multiple directions
    - Allow_inverse: If true, this also returns True if the instance is
        pointed the opposite direction .
    """
    angle = inst['angles', '0 0 0']

    if flag.has_children():
        targ_angle = flag['direction', '0 0 0']
        from_dir = flag['from_dir', '0 0 1']
        if from_dir.casefold() in DIRECTIONS:
            from_dir = Vec(DIRECTIONS[from_dir.casefold()])
        else:
            from_dir = Vec.from_str(from_dir, 0, 0, 1)
        allow_inverse = utils.conv_bool(flag['allow_inverse', '0'])
    else:
        targ_angle = flag.value
        from_dir = Vec(0, 0, 1)
        allow_inverse = False

    if angle == targ_angle:
        return True  # Check for exact match

    normal = DIRECTIONS.get(targ_angle.casefold(), None)
    if normal is None:
        return False  # If it's not a special angle,
        # so it failed the exact match

    inst_normal = from_dir.rotate_by_str(angle)

    if normal == 'WALL':
        # Special case - it's not on the floor or ceiling
        return not (inst_normal == (0, 0, 1) or inst_normal == (0, 0, -1))
    else:
        return inst_normal == normal or (
            allow_inverse and -inst_normal == normal
        )
示例#26
0
def make_static_pist(ent, res):
    """Convert a regular piston into a static version.

    This is done to save entities and improve lighting."""

    bottom_pos = ent.fixup["bottom_level", "-1"]

    if ent.fixup["connectioncount", "0"] != "0" or ent.fixup["disable_autodrop", "0"] != "0":  # can it move?
        if int(bottom_pos) > 0:
            # The piston doesn't go fully down, use alt instances.
            val = res.value["bottom_" + bottom_pos]
            if val:  # Only if defined
                ent["file"] = val
    else:  # we are static
        val = res.value[
            "static_" + (ent.fixup["top_level", "1"] if utils.conv_bool(ent.fixup["start_up"], False) else bottom_pos)
        ]
        if val:
            ent["file"] = val
示例#27
0
文件: brushes.py 项目: goodDOS/BEE2.4
def res_hollow_brush(inst, res):
    """Hollow out the attached brush, as if EmbeddedVoxel was set.

    This just removes the surface if it's already an embeddedVoxel. This allows
    multiple items to embed thinly in the same block without affecting each
    other.
    """
    loc = Vec(0, 0, -64).rotate_by_str(inst['angles'])
    loc += Vec.from_str(inst['origin'])

    try:
        group = SOLIDS[loc.as_tuple()]
    except KeyError:
        LOGGER.warning('No brush for hollowing at ({})', loc)
        return  # No brush here?

    conditions.hollow_block(
        group,
        remove_orig_face=utils.conv_bool(res['RemoveFace', False])
    )
示例#28
0
def res_add_output_setup(res):
    output = res['output']
    input_name = res['input']
    inst_in = res['inst_out', '']
    inst_out = res['inst_out', '']
    targ = res['target']
    only_once = utils.conv_bool(res['only_once', None])
    times = 1 if only_once else utils.conv_int(res['times', None], -1)
    delay = utils.conv_float(res['delay', '0.0'])
    parm = res['parm', '']

    return (
        output,
        targ,
        input_name,
        parm,
        delay,
        times,
        inst_in,
        inst_out,
    )
示例#29
0
def res_rand_num(inst, res):
    """Generate a random number and save in a fixup value.

    If 'decimal' is true, the value will contain decimals. 'max' and 'min' are
    inclusive. 'ResultVar' is the variable the result will be saved in.
    If 'seed' is set, it will be used to keep the value constant across
    map recompiles. This should be unique.
    """
    is_float = utils.conv_bool(res['decimal'])
    max_val = utils.conv_float(res['max', 1.0])
    min_val = utils.conv_float(res['min', 0.0])
    var = res['resultvar', '$random']
    seed = res['seed', 'random']

    random.seed(inst['origin'] + inst['angles'] + 'random_' + seed)

    if is_float:
        func = random.uniform
    else:
        func = random.randint

    inst.fixup[var] = str(func(min_val, max_val))
示例#30
0
文件: backup.py 项目: SpyyZ158/BEE2.4
    def from_file(cls, path, zip_file):
        """Initialise from a file.

        path is the file path for the map inside the zip, without extension.
        zip_file is either a ZipFile or FakeZip object.
        """
        with zip_file.open(path + '.p2c') as file:
            props = Property.parse(file, path)
        props = props.find_key('portal2_puzzle', [])

        title = props['title', None]
        if title is None:
            title = '<' + path.rsplit('/', 1)[-1] + '.p2c>'

        return cls(
            path=path,
            zip_file = zip_file,
            title=title,
            desc=props['description', '...'],
            is_coop=utils.conv_bool(props['coop', '0']),
            create_time=Date(props['timestamp_created', '']),
            mod_time=Date(props['timestamp_modified', '']),
        )
示例#31
0
def res_add_global_inst(_, res):
    """Add one instance in a location.

    Options:
        allow_multiple: Allow multiple copies of this instance. If 0, the
            instance will not be added if it was already added.
        name: The targetname of the instance. IF blank, the instance will
              be given a name of the form 'inst_1234'.
        file: The filename for the instance.
        Angles: The orientation of the instance (defaults to '0 0 0').
        Origin: The location of the instance (defaults to '0 0 -10000').
        Fixup_style: The Fixup style for the instance. '0' (default) is
            Prefix, '1' is Suffix, and '2' is None.
    """
    if res.value is not None:
        if utils.conv_bool(res["allow_multiple", "0"]) or res["file"] not in GLOBAL_INSTANCES:
            # By default we will skip adding the instance
            # if was already added - this is helpful for
            # items that add to original items, or to avoid
            # bugs.
            new_inst = VLib.Entity(
                vbsp.VMF,
                keys={
                    "classname": "func_instance",
                    "targetname": res["name", ""],
                    "file": resolve_inst(res["file"])[0],
                    "angles": res["angles", "0 0 0"],
                    "origin": res["position", "0 0 -10000"],
                    "fixup_style": res["fixup_style", "0"],
                },
            )
            GLOBAL_INSTANCES.add(res["file"])
            if new_inst["targetname"] == "":
                new_inst["targetname"] = "inst_"
                new_inst.make_unique()
            vbsp.VMF.add_ent(new_inst)
    return RES_EXHAUSTED
示例#32
0
def res_translate_inst(inst, res):
    """Translate the instance locally by the given amount.

    The special values <piston>, <piston_bottom> and <piston_top> can be
    used to offset it based on the starting position, bottom or top position
    of a piston platform.
    """
    folded_val = res.value.casefold()
    if folded_val == '<piston>':
        folded_val = (
            '<piston_top>' if
            utils.conv_bool(inst.fixup['$start_up'])
            else '<piston_bottom>'
        )

    if folded_val == '<piston_top>':
        val = Vec(z=128 * utils.conv_int(inst.fixup['$top_level', '1'], 1))
    elif folded_val == '<piston_bottom>':
        val = Vec(z=128 * utils.conv_int(inst.fixup['$bottom_level', '0'], 0))
    else:
        val = Vec.from_str(res.value)

    offset = val.rotate_by_str(inst['angles'])
    inst['origin'] = (offset + Vec.from_str(inst['origin'])).join(' ')
示例#33
0
    def from_file(cls, path, zip_file):
        """Initialise from a file.

        path is the file path for the map inside the zip, without extension.
        zip_file is either a ZipFile or FakeZip object.
        """
        # Some P2Cs may have non-ASCII characters in descriptions, so we
        # need to read it as bytes and convert to utf-8 ourselves - zips
        # don't convert encodings automatically for us.
        with zip_open_bin(zip_file, path + '.p2c') as file:
            props = Property.parse(
                # Decode the P2C as UTF-8, and skip unknown characters.
                # We're only using it for display purposes, so that should
                # be sufficent.
                EncodedFile(
                    file,
                    data_encoding='utf-8',
                    errors='replace',
                ),
                path,
            )
        props = props.find_key('portal2_puzzle', [])

        title = props['title', None]
        if title is None:
            title = '<' + path.rsplit('/', 1)[-1] + '.p2c>'

        return cls(
            path=path,
            zip_file=zip_file,
            title=title,
            desc=props['description', '...'],
            is_coop=utils.conv_bool(props['coop', '0']),
            create_time=Date(props['timestamp_created', '']),
            mod_time=Date(props['timestamp_modified', '']),
        )
示例#34
0
def show_window(used_props, parent, item_name):
    global propList, is_open, block_sound, last_angle
    propList = [key.casefold() for key in used_props]
    is_open = True
    spec_row = 1

    start_up = utils.conv_bool(used_props.get('startup', '0'))
    values['startup'] = start_up
    for prop, value in used_props.items():
        if prop in PROP_TYPES and value is not None:
            prop_type = PROP_TYPES[prop][0]
            if prop_type == 'checkbox':
                values[prop].set(utils.conv_bool(value))
            elif prop_type == 'railLift':
                values[prop].set(utils.conv_bool(value))
                save_rail(prop)
            elif prop_type == 'gelType':
                values[prop].set(value)
            elif prop_type == 'panAngle':
                last_angle = value[5:7]
                values[prop].set(last_angle)
                out_values[prop] = value
            elif prop_type == 'pistPlat':
                values[prop] = value
                try:
                    top_level = int(used_props.get('toplevel', 4))
                    bot_level = int(used_props.get('bottomlevel', 0))
                except ValueError:
                    pass
                else:
                    if ((prop == 'toplevel' and start_up)
                            or (prop == 'bottomlevel' and not start_up)):
                        widgets[prop].set(max(
                            top_level,
                            bot_level,
                        ))
                    if ((prop == 'toplevel' and not start_up)
                            or (prop == 'bottomlevel' and start_up)):
                        widgets[prop].set(min(
                            top_level,
                            bot_level,
                        ))
            elif prop_type == 'timerDel':
                try:
                    values[prop] = int(value)
                    widgets[prop].set(values[prop])
                except ValueError:
                    pass
            else:
                values[prop] = value

    for key in PROP_POS_SPECIAL:
        if key in propList:
            labels[key].grid(
                row=spec_row,
                column=0,
                sticky=E,
                padx=2,
                pady=5,
            )
            widgets[key].grid(
                row=spec_row,
                column=1,
                sticky="EW",
                padx=2,
                pady=5,
                columnspan=9,
            )
            spec_row += 1
        else:
            labels[key].grid_remove()
            widgets[key].grid_remove()
# if we have a 'special' prop, add the divider between the types
    if spec_row > 1:
        widgets['div_h'].grid(
            row=spec_row + 1,
            columnspan=9,
            sticky="EW",
        )
        spec_row += 2
    else:
        widgets['div_h'].grid_remove()
    ind = 0

    for key in PROP_POS:
        # Position each widget
        if key in propList:
            labels[key].grid(
                row=(ind // 3) + spec_row,
                column=(ind % 3) * 3,
                sticky=E,
                padx=2,
                pady=5,
            )
            widgets[key].grid(
                row=(ind // 3) + spec_row,
                column=(ind % 3) * 3 + 1,
                sticky="EW",
                padx=2,
                pady=5,
            )
            ind += 1
        else:
            labels[key].grid_remove()
            widgets[key].grid_remove()

    if ind > 1:  # is there more than 1 checkbox? (add left divider)
        widgets['div_1'].grid(row=spec_row,
                              column=2,
                              sticky="NS",
                              rowspan=(ind // 3) + 1)
    else:
        widgets['div_1'].grid_remove()

    if ind > 2:  # are there more than 2 checkboxes? (add right divider)
        widgets['div_2'].grid(
            row=spec_row,
            column=5,
            sticky="NS",
            rowspan=(ind // 3) + 1,
        )
    else:
        widgets['div_2'].grid_remove()

    if ind + spec_row == 1:
        # There aren't any items, display error message
        labels['noOptions'].grid(row=1, columnspan=9)
        ind = 1
    else:
        labels['noOptions'].grid_remove()

    widgets['saveButton'].grid(
        row=ind + spec_row,
        columnspan=9,
        sticky="EW",
    )

    # Block sound for the first few millisec to stop excess sounds from
    # playing
    block_sound = False
    win.after(50, reset_sfx)

    widgets['titleLabel'].configure(text='Settings for "' + item_name + '"')
    win.title('BEE2 - ' + item_name)
    win.transient(master=parent)
    win.deiconify()
    win.lift(parent)
    win.grab_set()
    win.geometry('+' + str(parent.winfo_rootx() - 30) + '+' +
                 str(parent.winfo_rooty() - win.winfo_reqheight() - 30))
示例#35
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!")
示例#36
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)