Пример #1
0
def edit_region_mesh_range(region: Region, new_mesh_range: Hex):
    new_mesh_range_str = str(new_mesh_range)
    assert new_mesh_range_str.startswith(
        '0x00000')  # only last 3 digits may be used
    old_mesh_range: Hex = region.get_data().mesh_range
    if new_mesh_range == old_mesh_range:
        print('no change in mesh range')
        return

    print('edit mesh range: ' + str(old_mesh_range) + ' -> ' +
          str(new_mesh_range))
    region.get_data().mesh_range = new_mesh_range
    region.save()

    new_mesh_prefix = new_mesh_range_str[7:]
    assert len(new_mesh_prefix) == 3, new_mesh_prefix
    # replace & collect in node_mesh_index
    node_mesh_index_file = region.gas_dir.get_subdir('index').get_gas_file(
        'node_mesh_index')
    mesh_replacements = list()
    for node_mesh_attr in node_mesh_index_file.get_gas().get_section(
            'node_mesh_index').items:
        assert isinstance(node_mesh_attr, Attribute)
        old_hex = Hex.parse(node_mesh_attr.name)
        new_hex = Hex.parse('0x' + new_mesh_prefix + node_mesh_attr.name[5:])
        mesh_replacements.append((old_hex, new_hex))
        node_mesh_attr.name = new_hex.to_str_lower()
    node_mesh_index_file.save()
    # replace mesh references in terrain nodes
    replace_hexes_in_file(
        region.gas_dir.get_subdir('terrain_nodes').get_gas_file('nodes').path,
        mesh_replacements)
Пример #2
0
 def write_gas(self) -> Gas:
     node_sections = [Section('t:snode,n:' + snode.guid.to_str_lower(), [
         Attribute('bounds_camera', snode.bounds_camera),
         Attribute('camera_fade', snode.camera_fade),
         Attribute('guid', snode.guid),
         Attribute('mesh_guid', Hex(snode.mesh_guid)),
         Attribute('nodelevel', snode.nodelevel),
         Attribute('nodeobject', snode.nodeobject),
         Attribute('nodesection', snode.nodesection),
         Attribute('occludes_camera', snode.occludes_camera),
         Attribute('occludes_light', snode.occludes_light),
         Attribute('texsetabbr', snode.texsetabbr),
     ] + [Section('door*', [
         Attribute('fardoor', door.fardoor),
         Attribute('farguid', door.farguid),
         Attribute('id', door.id),
     ]) for door in snode.doors]) for snode in self.nodes]
     return Gas([
         Section('t:terrain_nodes,n:siege_node_list', [
             Attribute('ambient_color', Hex(self.ambient_color)),
             Attribute('ambient_intensity', float(self.ambient_intensity)),
             Attribute('object_ambient_color', Hex(self.object_ambient_color)),
             Attribute('object_ambient_intensity', float(self.object_ambient_intensity)),
             Attribute('actor_ambient_color', Hex(self.actor_ambient_color)),
             Attribute('actor_ambient_intensity', float(self.actor_ambient_intensity)),
             Attribute('targetnode', self.targetnode)
         ] + node_sections)
     ])
Пример #3
0
    def store_generated_objects(self):
        snci_section = self.gas_dir.get_or_create_subdir(
            'index').get_or_create_gas_file(
                'streamer_node_content_index').get_gas().get_or_create_section(
                    'streamer_node_content_index')
        all_ioids = [
            Hex.parse('0x' + str(attr.value)[5:])
            for attr in snci_section.get_attrs()
        ]  # all internal object ids (internal meaning without scid range)
        streamer_node_content_index = {}
        object_sections = []
        last_ioid = 0
        for go_data in self.generated_objects_non_interactive:
            assert isinstance(go_data, GameObjectData)
            assert go_data.scid is None
            ioid = last_ioid + 1
            while ioid in all_ioids:  # find free oid
                ioid += 1
            last_ioid = ioid
            go_data.scid = Hex.parse('0x{:03X}{:05X}'.format(
                self.data.scid_range, ioid))

            object_sections.append(go_data.make_gas())
            node_guid = go_data.placement.position.node_guid
            if node_guid not in streamer_node_content_index:
                streamer_node_content_index[node_guid] = []
            streamer_node_content_index[node_guid].append(go_data.scid)
        objects_dir = self.gas_dir.get_or_create_subdir('objects')
        objects_dir.get_or_create_gas_file(
            'non_interactive').get_gas().items.extend(object_sections)
        snci_attrs = []
        for node_guid, oids in streamer_node_content_index.items():
            snci_attrs.extend([Attribute(node_guid, oid) for oid in oids])
        snci_section.items.extend(snci_attrs)
Пример #4
0
    def store_terrain(self):
        # index
        mesh_index = self.terrain.get_mesh_index()
        mesh_index = {
            Hex.parse('0x{:03X}{:05X}'.format(self.data.mesh_range,
                                              mesh_guid)): mesh_name
            for mesh_guid, mesh_name in mesh_index.items()
        }
        self.gas_dir.create_subdir(
            'index', {
                'node_mesh_index':
                Gas([
                    Section('node_mesh_index', [
                        Attribute(str(mesh_guid), mesh_name)
                        for mesh_guid, mesh_name in mesh_index.items()
                    ])
                ]),
                'streamer_node_index':
                Gas([
                    Section('streamer_node_index', [
                        Attribute('*', node.guid)
                        for node in self.terrain.nodes
                    ])
                ])
            })

        # terrain_nodes
        nodes_gas = NodesGas()
        nodes_gas.ambient_color = self.terrain.ambient_light.terrain_color
        nodes_gas.ambient_intensity = self.terrain.ambient_light.terrain_intensity
        nodes_gas.object_ambient_color = self.terrain.ambient_light.object_color
        nodes_gas.object_ambient_intensity = self.terrain.ambient_light.object_intensity
        nodes_gas.actor_ambient_color = self.terrain.ambient_light.actor_color
        nodes_gas.actor_ambient_intensity = self.terrain.ambient_light.actor_intensity
        nodes_gas.targetnode = self.terrain.target_node.guid
        snodes = list()
        for node in self.terrain.nodes:
            mesh_guid = Terrain.mesh_index_lookup[node.mesh_name]
            mesh_guid = Hex.parse('0x{:03X}{:05X}'.format(
                self.data.mesh_range, mesh_guid))
            doors = [
                Door(door_id, far_node.guid, far_door)
                for door_id, (far_node, far_door) in node.doors.items()
            ]
            nodesection = Hex(
                node.section if node.section != -1 else 0xffffffff)
            nodelevel = Hex(node.section if node.section != -1 else 0xffffffff)
            nodeobject = Hex(
                node.section if node.section != -1 else 0xffffffff)
            snode = SNode(node.guid, mesh_guid, node.texture_set, True, False,
                          False, True, nodesection, nodelevel, nodeobject,
                          doors)
            snodes.append(snode)
        nodes_gas.nodes = snodes
        self.gas_dir.create_subdir('terrain_nodes',
                                   {'nodes': nodes_gas.write_gas()})
Пример #5
0
def main(argv):
    path = argv[0]
    hexes = [(Hex.parse(hex_hex.split(',')[0]), Hex.parse(hex_hex.split(',')[1])) for hex_hex in argv[1].split(';')]
    if os.path.isdir(path):
        replace_hexes_in_dir(path, hexes)
    elif os.path.isfile(path):
        replace_hexes_in_file(path, hexes)
    else:
        assert False, f'{path} does not exist'
    return 0
Пример #6
0
 def ensure_north_vector(self):
     editor_subdir = self.gas_dir.get_or_create_subdir('editor')
     if not editor_subdir.has_gas_file('hotpoints'):
         hotpoints_gas = Gas([
             Section('hotpoints', [
                 Section('t:hotpoint_directional,n:' + str(Hex(1)), [
                     Attribute('direction', '0,0,-1'),
                     Attribute('id', Hex(1))
                 ])
             ])
         ])
         editor_subdir.create_gas_file('hotpoints', hotpoints_gas)
Пример #7
0
 def load_data(self):
     main_file = self.gas_dir.get_gas_file('main')
     assert main_file is not None
     main = main_file.get_gas()
     region_section = main.get_section('t:region,n:region')
     data = Region.Data()
     guid = Hex(region_section.get_attr_value('guid'))
     mesh_range = Hex(region_section.get_attr_value('mesh_range'))
     scid_range = Hex(region_section.get_attr_value('scid_range'))
     # assert guid == mesh_range == scid_range  # not the case in map_world
     data.id = guid
     data.mesh_range = mesh_range
     data.scid_range = scid_range
     self.data = data
Пример #8
0
 def new_node_guid(self):
     guid = Hex.random()
     assert guid not in [
         n.guid for n in self.nodes
     ], f'new guid {guid} already in existing {len(self.nodes)} nodes'
     assert guid not in self.all_map_node_ids, f'new guid {guid} already in existing map nodes'
     return guid
Пример #9
0
 def write_gas(self) -> Gas:
     se_sections = [
         Section(f't:stitch_editor,n:{se.dest_region}', [
             Attribute('dest_region', se.dest_region),
             Section('node_ids', [
                 Attribute(
                     Hex(stitch_id).to_str_lower(), f'{node_guid},{door}')
                 for stitch_id, (node_guid, door) in se.node_ids.items()
             ])
         ]) for se in self.stitch_editors
     ]
     return Gas([
         Section('stitch_helper_data', [
             Attribute('source_region_guid', Hex(self.source_region_guid)),
             Attribute('source_region_name', self.source_region_name),
         ] + se_sections)
     ])
Пример #10
0
def get_stitch_id(rx: int, rz: int, hv: bool, xz: int):
    assert 0 <= rx < 100
    assert 0 <= rz < 100
    assert 0 <= xz < 0x1000
    rx = str(rx).zfill(2)
    rz = str(rz).zfill(2)
    hv = '0' if hv else '1'
    stitch_range = f'0x{rx}{rz}{hv}000'
    return Hex.parse(stitch_range) + xz
Пример #11
0
def edit_region_ids(map_name,
                    region_name,
                    mesh_range=None,
                    scid_range=None,
                    guid=None):
    bits = Bits()
    assert map_name in bits.maps, map_name
    _map = bits.maps[map_name]
    regions = _map.get_regions()
    assert region_name in regions, region_name
    region = regions[region_name]
    if mesh_range is not None:
        edit_region_mesh_range(region, Hex.parse(mesh_range))
    region.gas_dir.clear_cache()
    region.load_data()
    if scid_range is not None:
        edit_region_scid_range(region, Hex.parse(scid_range))
    region.gas_dir.clear_cache()
    region.load_data()
    if guid is not None:
        edit_region_guid(region, Hex.parse(guid))
Пример #12
0
def convert_region(region: Region, nmg: NodeMeshGuids):
    index_dir = region.gas_dir.get_subdir('index')
    if index_dir.has_gas_file('node_mesh_index'):
        return
    nodes_file = region.gas_dir.get_subdir('terrain_nodes').get_gas_file(
        'nodes')
    node_gas = nodes_file.get_gas()
    nodes_section: Section = node_gas.items[0]
    node_sections = nodes_section.get_sections()
    mesh_guid_attrs = [ns.get_attr('mesh_guid') for ns in node_sections]
    node_mesh_index = {}
    node_mesh_guids = nmg.get_node_mesh_guids()
    for mesh_guid_attr in mesh_guid_attrs:
        mesh_guid = mesh_guid_attr.value.to_str_lower()
        assert mesh_guid in node_mesh_guids, str(
            mesh_guid) + ' is not in ' + str(node_mesh_guids)
        if mesh_guid not in node_mesh_index:
            node_mesh_index[mesh_guid] = Hex(len(node_mesh_index) + 1)
        mesh_id = node_mesh_index[mesh_guid]
        mesh_guid_attr.value = mesh_id
    nodes_file.save()
    node_mesh_index_attrs = [
        Attribute(mesh_id, node_mesh_guids[mesh_guid])
        for mesh_guid, mesh_id in node_mesh_index.items()
    ]
    index_dir.create_gas_file(
        'node_mesh_index',
        Gas([Section('node_mesh_index', node_mesh_index_attrs)])).save()
    region_data = region.get_data()
    if region_data.mesh_range == 0:
        if 0 < region_data.scid_range < Hex.parse('0x00001000'):
            region_data.mesh_range = region_data.scid_range
        elif 0 < region_data.id < Hex.parse('0x00001000'):
            region_data.mesh_range = region_data.id
        if region_data.mesh_range != 0:
            region.save()
    print(f'Converted region {region.get_name()} to NMI')
Пример #13
0
def edit_region_scid_range(region: Region, new_scid_range: Hex):
    new_scid_range_str = str(new_scid_range)
    assert new_scid_range_str.startswith(
        '0x00000')  # only last 3 digits may be used
    old_scid_range: Hex = region.get_data().scid_range
    if new_scid_range == old_scid_range:
        print('no change in scid range')
        return
    # check that no region already uses the new scid range
    other_regions: list[Region] = [
        r for r in region.map.get_regions().values()
        if r.get_name() != region.get_name()
    ]
    other_regions_using_new_scid_range = [
        r for r in other_regions if r.get_data().scid_range == new_scid_range
    ]
    assert len(
        other_regions_using_new_scid_range
    ) == 0, f'new scid range is already used by {[r.get_name() for r in other_regions_using_new_scid_range]}'
    # check that target region is the only one with the old scid range
    other_regions_using_old_scid_range = [
        r for r in other_regions if r.get_data().scid_range == old_scid_range
    ]
    assert len(
        other_regions_using_old_scid_range
    ) == 0, f'old scid range is also used by {[r.get_name() for r in other_regions_using_old_scid_range]}'

    print('edit scid range: ' + str(old_scid_range) + ' -> ' +
          new_scid_range_str)
    region.get_data().scid_range = new_scid_range
    region.save()

    new_scid_prefix = new_scid_range_str[7:]
    assert len(new_scid_prefix) == 3, new_scid_prefix
    old_scids = region.get_scids()
    scid_replacements = [
        (old_scid, Hex.parse('0x' + new_scid_prefix + str(old_scid)[5:]))
        for old_scid in old_scids
    ]
    # replace in all files of target region
    replace_hexes_in_dir(region.gas_dir.path, scid_replacements)
    # replace in referencing files of all regions
    replace_hexes_in_dir(region.map.gas_dir.path, scid_replacements,
                         'elevator.gas')
    replace_hexes_in_dir(region.map.gas_dir.path, scid_replacements,
                         'interactive.gas')
    replace_hexes_in_dir(region.map.gas_dir.path, scid_replacements,
                         'special.gas')
Пример #14
0
 def test_attribute(self):
     s = Attribute('s', 'some string')
     self.assertEqual(None, s.datatype)
     self.assertEqual('s = some string', str(s))
     b = Attribute('b', True)
     self.assertEqual('b', b.datatype)
     self.assertEqual('b (b) = true', str(b))
     i = Attribute('i', 42)
     self.assertEqual('i', i.datatype)
     self.assertEqual('i (i) = 42', str(i))
     f = Attribute('f', 13.37)
     self.assertEqual('f', f.datatype)
     self.assertEqual('f (f) = 13.370000', str(f))
     x = Attribute('x', Hex(4711))
     self.assertEqual('x', x.datatype)
     self.assertEqual('x (x) = 0x00001267', str(x))
Пример #15
0
    def load_terrain(self):
        node_mesh_index_file = self.gas_dir.get_subdir('index').get_gas_file(
            'node_mesh_index')
        nmi = {
            Hex.parse(attr.name): attr.value
            for attr in node_mesh_index_file.get_gas().get_section(
                'node_mesh_index').get_attrs()
        }

        nodes_gas_file = self.gas_dir.get_subdir('terrain_nodes').get_gas_file(
            'nodes')
        nodes_gas = NodesGas.load(nodes_gas_file)

        # ambient light
        ambient_light = AmbientLight(nodes_gas.ambient_color,
                                     nodes_gas.ambient_intensity,
                                     nodes_gas.object_ambient_color,
                                     nodes_gas.object_ambient_intensity,
                                     nodes_gas.actor_ambient_color,
                                     nodes_gas.actor_ambient_intensity)

        # nodes
        nodes = list()
        nodes_dict = dict()
        for snode in nodes_gas.nodes:
            assert snode.mesh_guid in nmi, 'unknown mesh_guid: ' + str(
                snode.mesh_guid)
            mesh_name = nmi[snode.mesh_guid]
            node = TerrainNode(snode.guid, mesh_name, snode.texsetabbr)
            node.section = snode.nodesection if snode.nodesection != 0xffffffff else -1
            node.level = snode.nodelevel if snode.nodelevel != 0xffffffff else -1
            node.object = snode.nodeobject if snode.nodeobject != 0xffffffff else -1
            nodes_dict[snode.guid] = node
            nodes.append(node)
        for snode in nodes_gas.nodes:
            node = nodes_dict[snode.guid]
            for door in snode.doors:
                far_node = nodes_dict[door.farguid]
                node.doors[door.id] = (far_node, door.fardoor)

        target_node = nodes_dict[nodes_gas.targetnode]

        terrain = Terrain()
        terrain.nodes = nodes
        terrain.target_node = target_node
        terrain.ambient_light = ambient_light
        self.terrain = terrain
Пример #16
0
def generate_region(_map, region_name, size_x, size_z, args: Args,
                    rt: RegionTiling):
    print(f'generate region {region_name} {size_x}x{size_z} ({args})')

    # generate the region!
    region_data = generate_region_data(size_x, size_z, args, region_name, rt,
                                       _map.get_all_node_ids())
    if region_data is None:
        print('all tiles culled - not saving region')
        return
    terrain, plants, stitches, decals = region_data

    # add lighting
    ambient_color = Hex(0xff8080ff)
    ambient_intensity = 0.2
    terrain.ambient_light.terrain_intensity = ambient_intensity
    terrain.ambient_light.terrain_color = ambient_color
    terrain.ambient_light.object_intensity = ambient_intensity
    terrain.ambient_light.object_color = ambient_color
    terrain.ambient_light.actor_intensity = ambient_intensity
    terrain.ambient_light.actor_color = ambient_color
    dir_lights = [
        # daylight from south-east
        DirectionalLight(
            color=Color(0xffffffff),
            draw_shadow=True,
            intensity=1,
            occlude_geometry=True,
            on_timer=True,
            direction=DirectionalLight.direction_from_orbit_and_azimuth(
                225, 45)),
        # counter-light from north-west
        DirectionalLight(
            color=Color(0xffffffff),
            draw_shadow=False,
            intensity=0.5,
            occlude_geometry=False,
            on_timer=True,
            direction=DirectionalLight.direction_from_orbit_and_azimuth(
                45, 45))
    ]

    # save
    if region_name in _map.get_regions():
        print(f'deleting existing region {region_name}')
        _map.delete_region(region_name)
        _map.gas_dir.clear_cache()
    region_i = rt.cur_x * rt.num_z + rt.cur_z + 1
    region: Region = _map.create_region(region_name, region_i)
    region.terrain = terrain
    region.decals = DecalsGas(decals) if len(decals) > 0 else None
    region.lights = dir_lights
    region.generated_objects_non_interactive = []

    if args.start_pos is not None:
        pos = Position(0, 0, 0, terrain.target_node.guid)
        start_group_name = args.start_pos
        _map.load_start_positions()
        if start_group_name in _map.start_positions.start_groups:
            _map.start_positions.start_groups[
                start_group_name].start_positions = [
                    StartPos(1, pos, Camera(0.5, 20, 0, pos))
                ]
        else:
            sg_id = _map.start_positions.new_start_group_id()
            _map.start_positions.start_groups[start_group_name] = StartGroup(
                'Heightmap generated start pos', False, sg_id, 'Heightmap',
                [StartPos(1, pos, Camera(0.5, 20, 0, pos))])
            _map.start_positions.default = start_group_name
        region.generated_objects_non_interactive.append(
            GameObjectData(
                'trigger_change_mood_box',
                placement=Placement(position=pos),
                common=Common(instance_triggers=[
                    TriggerInstance(
                        'party_member_within_bounding_box(2,1,2,"on_every_enter")',
                        'mood_change("map_world_df_r0_2")')
                ])))
        _map.save()

    if plants is not None:
        region.generated_objects_non_interactive.extend([
            GameObjectData(
                plant.template_name,
                placement=Placement(position=plant.position,
                                    orientation=Quaternion.rad_to_quat(
                                        plant.orientation)),
                aspect=Aspect(scale_multiplier=plant.size)) for plant in plants
        ])

    if rt and (rt.num_x > 1 or rt.num_z > 1):
        top_stitches, left_stitches, bottom_stitches, right_stitches = stitches
        shg = StitchHelperGas(region.data.id, region_name)
        if top_stitches is not None:
            shg.stitch_editors.append(
                StitchEditor(rt.region_name(rt.cur_x, rt.cur_z - 1),
                             top_stitches))
        if left_stitches is not None:
            shg.stitch_editors.append(
                StitchEditor(rt.region_name(rt.cur_x - 1, rt.cur_z),
                             left_stitches))
        if bottom_stitches is not None:
            shg.stitch_editors.append(
                StitchEditor(rt.region_name(rt.cur_x, rt.cur_z + 1),
                             bottom_stitches))
        if right_stitches is not None:
            shg.stitch_editors.append(
                StitchEditor(rt.region_name(rt.cur_x + 1, rt.cur_z),
                             right_stitches))
        region.stitch_helper = shg

    region.save()
    print(f'new region {region_name} saved')
Пример #17
0
 def store_data(self):
     main = self.gas_dir.get_or_create_gas_file('main', False).get_gas()
     region_section = main.get_or_create_section('t:region,n:region')
     region_section.set_attr_value('guid', Hex(self.data.id))
     region_section.set_attr_value('mesh_range', Hex(self.data.mesh_range))
     region_section.set_attr_value('scid_range', Hex(self.data.scid_range))