Example #1
0
    def fetch(self, startTime, endTime=None):
        now = int(time.time())
        if endTime is None:
            endTime = now
        else:
            endTime = magic_time(endTime)
        startTime = magic_time(startTime)
        delta = int(endTime) - int(startTime)

        tick = delta / self.step
        noise = SimplexNoise(1024)
        noise.randomize()
        timeInfos = startTime, endTime, self.step
        return timeInfos, [noise.noise2(1, a) for a in range(tick)]
Example #2
0
    def fetch(self, startTime, endTime=None):
        now = int(time.time())
        if endTime is None:
            endTime = now
        else:
            endTime = magic_time(endTime)
        startTime = magic_time(startTime)
        delta = int(endTime) - int(startTime)

        tick = delta / self.step
        noise = SimplexNoise(1024)
        noise.randomize()
        timeInfos = startTime, endTime, self.step
        return timeInfos, [noise.noise2(1, a) for a in range(tick)]
Example #3
0
    def __init__(self, world, seed):
        super(TerrainGeneratorSimple, self).__init__(seed)
        self.world = world
        self.seed = G.SEED  #seed
        self.rand = random.Random(seed)
        perm = range(255)
        self.rand.shuffle(perm)
        self.noise = SimplexNoise(permutation_table=perm).noise2
        #self.noise = PerlinNoise(seed).noise
        self.PERSISTENCE = 2.1379201  #AKA lacunarity
        self.H = 0.836281

        self.biome_generator = BiomeGenerator(seed)

        #Fun things to adjust
        self.OCTAVES = 9  #Higher linearly increases calc time; increases apparent 'randomness'
        self.height_range = 32  #If you raise this, you should shrink zoom_level equally
        self.height_base = 32  #The lowest point the perlin terrain will generate (below is "underground")
        self.island_shore = 38  #below this is sand, above is grass .. island only
        self.water_level = 36  # have water 2 block higher than base, allowing for some rivers...
        self.zoom_level = 0.002  #Smaller will create gentler, softer transitions. Larger is more mountainy

        # ores avaliable on the lowest level, closet to bedrock
        self.lowlevel_ores = ((B.stone_block, ) * 75 +
                              (B.diamondore_block, ) * 2 +
                              (B.sapphireore_block, ) * 2)
        #  ores in the 'mid-level' .. also, the common ore blocks
        self.midlevel_ores = ((B.stone_block, ) * 80 +
                              (B.rubyore_block, ) * 2 +
                              (B.coalore_block, ) * 4 +
                              (B.gravel_block, ) * 5 +
                              (B.ironore_block, ) * 5 +
                              (B.lapisore_block, ) * 2)
        # ores closest to the top level dirt and ground
        self.highlevel_ores = ((B.stone_block, ) * 85 +
                               (B.gravel_block, ) * 5 +
                               (B.coalore_block, ) * 3 +
                               (B.quartz_block, ) * 5)
        self.underwater_blocks = ((B.sand_block, ) * 70 +
                                  (B.gravel_block, ) * 20 +
                                  (B.clay_block, ) * 10)
        #self.world_type_trees = (N.oak_tree, N.birch_tree, N.water_melon, N.pumpkin, N.y_flowers, N.potato, N.carrot, N.rose)
        self.world_type_trees = (N.oak_tree, N.birch_tree, N.jungle_tree)
        self.world_type_plants = (N.pumpkin, N.potato, N.carrot, N.water_melon)
        self.world_type_grass = (N.y_flowers, N.tall_grass, N.rose,
                                 N.tall_grass0, N.tall_grass1, N.cactus,
                                 N.tall_grass2, N.tall_cactus, N.tall_grass3,
                                 N.tall_grass4, N.tall_grass5, N.tall_grass6,
                                 N.tall_grass7, N.dead_bush, N.desert_grass)
        #This is a list of blocks that may leak over from adjacent sectors and whose presence doesn't mean the sector is generated
        self.autogenerated_blocks = N.VEGETATION_BLOCKS
        self.nether = (B.nether_block, B.soulsand_block, B.netherore_block,
                       B.air_block)
        #self.nether = ((B.nether_block,) * 80 + (B.soulsand_block,) * 15 + (B.netherore_block,) * 5 + (B.air_block,) * 10)

        self.weights = [
            self.PERSISTENCE**(-self.H * n) for n in xrange(self.OCTAVES)
        ]
Example #4
0
def get_noise(loc: Vec, noise_func: SimplexNoise):
    """Generate a number between 0 and 1.

    This is used to determine where tiles are placed.
    """
    # Average between the neighbouring locations, to smooth out changes.
    return sum(
        # + 1 / 2 fixes the value range (originally -1,1 -> 0,1)
        (noise_func.noise3(loc.x + x, loc.y + y, loc.z) + 1) / 2
        for x in (-1, 0, 1) for y in (-1, 0, 1)) / 9
Example #5
0
    def __init__(self, seed, octaves=6, zoom_level=0.002):  # octaves = 6,
        perm = range(255)
        random.Random(seed).shuffle(perm)
        self.noise = SimplexNoise(permutation_table=perm).noise2

        self.PERSISTENCE = 2.1379201 # AKA lacunarity
        self.H = 0.836281
        self.OCTAVES = octaves       # Higher linearly increases calc time; increases apparent 'randomness'
        self.weights = [self.PERSISTENCE ** (-self.H * n) for n in xrange(self.OCTAVES)]

        self.zoom_level = zoom_level # Smaller will create gentler, softer transitions. Larger is more mountainy
Example #6
0
def get_noise(loc: Vec, noise_func: SimplexNoise):
    """Generate a number between 0 and 1.

    This is used to determine where tiles are placed.
    """
    # Average between the neighbouring locations, to smooth out changes.
    return sum(
        # + 1 / 2 fixes the value range (originally -1,1 -> 0,1)
        (noise_func.noise3(loc.x + x, loc.y + y, loc.z) + 1) / 2
        for x in (-1, 0, 1)
        for y in (-1, 0, 1)
    ) / 9
Example #7
0
    def __init__(self):

        # current builder state message
        self.message: str = ''
        self.completion: int = 0  # out of 100%

        # current map status
        self.map_ready: bool = False

        # map being worked on
        self.wmap: WorldMap = None

        # map builder tools
        noise_generator = SimplexNoise()
        self.floor_shaper = FloorShaper(noise_generator)
        self.cavern_shaper = CavernShaper(noise_generator)
        self.forest_shaper = ForestShaper(noise_generator)
        self.ore_shaper = OreShaper(noise_generator)

        self.single_wall_spawner = SingleWallSpawner()
        self.single_wall_replacer = SingleWallReplacer()

        self.plank_shack_generator = PlankShackGenerator()
        self.mountain_fortress_generator = MountainFortressGenerator()
Example #8
0
def res_cutout_tile(vmf: srctools.VMF, res: Property):
    """Generate random quarter tiles, like in Destroyed or Retro maps.

    - `MarkerItem` is the instance file to look for (`<ITEM_BEE2_CUTOUT_TILE>`)
    - `floor_chance`: The percentage change for a segment in the middle of the floor to be a normal tile.
    - `floor_glue_chance`: The chance for any tile to be glue - this should be higher than the regular chance, as that overrides this.
    - `rotateMax` is the maximum angle to rotate squarebeam models.
    - `squarebeamsSkin` sets the skin to use for the squarebeams floor frame.
    - `dispBase`, if true makes the floor a displacement with random alpha.
    - `Materials` blocks specify the possible materials to use:
          - `squarebeams` is the squarebeams variant to use.
          - `ceilingwalls` are the sides of the ceiling section.
          - `floorbase` is the texture under floor sections.
            If `dispBase` is True this is a displacement material.
          - `tile_glue` is used on top of a thinner tile segment.
          - `clip` is the player_clip texture used over floor segments.
            (This allows customising the surfaceprop.)

    """
    marker_filenames = instanceLocs.resolve(res['markeritem'])

    # TODO: Reimplement cutout tiles.
    for inst in vmf.by_class['func_instance']:
        if inst['file'].casefold() in marker_filenames:
            inst.remove()
    return

    x: float
    y: float
    max_x: float
    max_y: float

    INST_LOCS = {}  # Map targetnames -> surface loc
    CEIL_IO = []  # Pairs of ceil inst corners to cut out.
    FLOOR_IO = []  # Pairs of floor inst corners to cut out.

    overlay_ids = {}  # When we replace brushes, we need to fix any overlays
    # on that surface.

    MATS.clear()
    floor_edges = []  # Values to pass to add_floor_sides() at the end

    sign_locs = set()
    # If any signage is present in the map, we need to force tiles to
    # appear at that location!
    for over in vmf.by_class['info_overlay']:
        if (over['material'].casefold() in FORCE_TILE_MATS and
                # Only check floor/ceiling overlays
                over['basisnormal'] in ('0 0 1', '0 0 -1')):
            add_signage_loc(sign_locs, Vec.from_str(over['origin']))

    for item in connections.ITEMS.values():
        for ind_pan in item.ind_panels:
            loc = Vec(0, 0, -64)
            loc.localise(
                Vec.from_str(ind_pan['origin']),
                Vec.from_str(ind_pan['angles']),
            )
            add_signage_loc(sign_locs, loc)

    SETTINGS = {
        'floor_chance':
        srctools.conv_int(res['floorChance', '100'], 100),
        'ceil_chance':
        srctools.conv_int(res['ceilingChance', '100'], 100),
        'floor_glue_chance':
        srctools.conv_int(res['floorGlueChance', '0']),
        'ceil_glue_chance':
        srctools.conv_int(res['ceilingGlueChance', '0']),
        'rotate_beams':
        int(srctools.conv_float(res['rotateMax', '0']) * BEAM_ROT_PRECISION),
        'beam_skin':
        res['squarebeamsSkin', '0'],
        'base_is_disp':
        srctools.conv_bool(res['dispBase', '0']),
        'quad_floor':
        res['FloorSize', '4x4'].casefold() == '2x2',
        'quad_ceil':
        res['CeilingSize', '4x4'].casefold() == '2x2',
    }

    random.seed(vbsp.MAP_RAND_SEED + '_CUTOUT_TILE_NOISE')
    noise = SimplexNoise(period=4 * 40)  # 4 tiles/block, 50 blocks max

    # We want to know the number of neighbouring tile cutouts before
    # placing tiles - blocks away from the sides generate fewer tiles.
    # all_floors[z][x,y] = count
    floor_neighbours = defaultdict(
        dict)  # type: Dict[float, Dict[Tuple[float, float], int]]

    for mat_prop in res.find_key('Materials', []):
        MATS[mat_prop.name].append(mat_prop.value)

    if SETTINGS['base_is_disp']:
        # We want the normal brushes to become nodraw.
        MATS['floorbase_disp'] = MATS['floorbase']
        MATS['floorbase'] = ['tools/toolsnodraw']

        # Since this uses random data for initialisation, the alpha and
        # regular will use slightly different patterns.
        alpha_noise = SimplexNoise(period=4 * 50)
    else:
        alpha_noise = None

    for key, default in TEX_DEFAULT:
        if key not in MATS:
            MATS[key] = [default]

    # Find our marker ents
    for inst in vmf.by_class['func_instance']:
        if inst['file'].casefold() not in marker_filenames:
            continue
        targ = inst['targetname']
        normal = Vec(0, 0, 1).rotate_by_str(inst['angles', '0 0 0'])
        # Check the orientation of the marker to figure out what to generate
        if normal == (0, 0, 1):
            io_list = FLOOR_IO
        else:
            io_list = CEIL_IO

        # Reuse orient to calculate where the solid face will be.
        loc = Vec.from_str(inst['origin']) - 64 * normal
        INST_LOCS[targ] = loc

        item = connections.ITEMS[targ]
        item.delete_antlines()

        if item.outputs:
            for conn in list(item.outputs):
                if conn.to_item.inst['file'].casefold() in marker_filenames:
                    io_list.append((targ, conn.to_item.name))
                else:
                    LOGGER.warning('Cutout tile connected to non-cutout!')
                conn.remove()  # Delete the connection.
        else:
            # If the item doesn't have any connections, 'connect'
            # it to itself so we'll generate a 128x128 tile segment.
            io_list.append((targ, targ))

        # Remove all traces of this item (other than in connections lists).
        inst.remove()
        del connections.ITEMS[targ]

    for start_floor, end_floor in FLOOR_IO:
        box_min = Vec(INST_LOCS[start_floor])
        box_min.min(INST_LOCS[end_floor])

        box_max = Vec(INST_LOCS[start_floor])
        box_max.max(INST_LOCS[end_floor])

        if box_min.z != box_max.z:
            continue  # They're not in the same level!
        z = box_min.z

        if SETTINGS['rotate_beams']:
            # We have to generate 1 model per 64x64 block to do rotation...
            gen_rotated_squarebeams(
                vmf,
                box_min - (64, 64, 0),
                box_max + (64, 64, -8),
                skin=SETTINGS['beam_skin'],
                max_rot=SETTINGS['rotate_beams'],
            )
        else:
            # Make the squarebeams props, using big models if possible
            gen_squarebeams(vmf,
                            box_min + (-64, -64, 0),
                            box_max + (64, 64, -8),
                            skin=SETTINGS['beam_skin'])

        # Add a player_clip brush across the whole area
        vmf.add_brush(
            vmf.make_prism(
                p1=box_min - (64, 64, FLOOR_DEPTH),
                p2=box_max + (64, 64, 0),
                mat=MATS['clip'][0],
            ).solid)

        # Add a noportal_volume covering the surface, in case there's
        # room for a portal.
        noportal_solid = vmf.make_prism(
            # Don't go all the way to the sides, so it doesn't affect wall
            # brushes.
            p1=box_min - (63, 63, 9),
            p2=box_max + (63, 63, 0),
            mat='tools/toolsinvisible',
        ).solid
        noportal_ent = vmf.create_ent(
            classname='func_noportal_volume',
            origin=box_min.join(' '),
        )
        noportal_ent.solids.append(noportal_solid)

        if SETTINGS['base_is_disp']:
            # Use displacements for the base instead.
            make_alpha_base(
                vmf,
                box_min + (-64, -64, 0),
                box_max + (64, 64, 0),
                noise=alpha_noise,
            )

        for x, y in utils.iter_grid(
                min_x=int(box_min.x),
                max_x=int(box_max.x) + 1,
                min_y=int(box_min.y),
                max_y=int(box_max.y) + 1,
                stride=128,
        ):
            # Build the set of all positions..
            floor_neighbours[z][x, y] = -1

        # Mark borders we need to fill in, and the angle (for func_instance)
        # The wall is the face pointing inwards towards the bottom brush,
        # and the ceil is the ceiling of the block above the bordering grid
        # points.
        for x in range(int(box_min.x), int(box_max.x) + 1, 128):
            # North
            floor_edges.append(
                BorderPoints(
                    wall=Vec(x, box_max.y + 64, z - 64),
                    ceil=Vec_tuple(x, box_max.y + 128, z),
                    rot=270,
                ))
            # South
            floor_edges.append(
                BorderPoints(
                    wall=Vec(x, box_min.y - 64, z - 64),
                    ceil=Vec_tuple(x, box_min.y - 128, z),
                    rot=90,
                ))

        for y in range(int(box_min.y), int(box_max.y) + 1, 128):
            # East
            floor_edges.append(
                BorderPoints(
                    wall=Vec(box_max.x + 64, y, z - 64),
                    ceil=Vec_tuple(box_max.x + 128, y, z),
                    rot=180,
                ))

            # West
            floor_edges.append(
                BorderPoints(
                    wall=Vec(box_min.x - 64, y, z - 64),
                    ceil=Vec_tuple(box_min.x - 128, y, z),
                    rot=0,
                ))

    # Now count boundaries near tiles, then generate them.

    # Do it separately for each z-level:
    for z, xy_dict in floor_neighbours.items():
        for x, y in xy_dict:
            # We want to count where there aren't any tiles
            xy_dict[x, y] = (((x - 128, y - 128) not in xy_dict) +
                             ((x - 128, y + 128) not in xy_dict) +
                             ((x + 128, y - 128) not in xy_dict) +
                             ((x + 128, y + 128) not in xy_dict) +
                             ((x - 128, y) not in xy_dict) +
                             ((x + 128, y) not in xy_dict) +
                             ((x, y - 128) not in xy_dict) +
                             ((x, y + 128) not in xy_dict))

        max_x = max_y = 0

        weights = {}
        # Now the counts are all correct, compute the weight to apply
        # for tiles.
        # Adding the neighbouring counts will make a 5x5 area needed to set
        # the center to 0.

        for (x, y), cur_count in xy_dict.items():
            max_x = max(x, max_x)
            max_y = max(y, max_y)

            # Orthrogonal is worth 0.2, diagonal is worth 0.1.
            # Not-present tiles would be 8 - the maximum
            tile_count = (0.8 * cur_count + 0.1 * xy_dict.get(
                (x - 128, y - 128), 8) + 0.1 * xy_dict.get(
                    (x - 128, y + 128), 8) + 0.1 * xy_dict.get(
                        (x + 128, y - 128), 8) + 0.1 * xy_dict.get(
                            (x + 128, y + 128), 8) + 0.2 * xy_dict.get(
                                (x - 128, y), 8) + 0.2 * xy_dict.get(
                                    (x, y - 128), 8) + 0.2 * xy_dict.get(
                                        (x, y + 128), 8) + 0.2 * xy_dict.get(
                                            (x + 128, y), 8))
            # The number ranges from 0 (all tiles) to 12.8 (no tiles).
            # All tiles should still have a small chance to generate tiles.
            weights[x, y] = min((tile_count + 0.5) / 8, 1)

        # Share the detail entity among same-height tiles..
        detail_ent = vmf.create_ent(classname='func_detail', )

        for x, y in xy_dict:
            convert_floor(
                vmf,
                Vec(x, y, z),
                overlay_ids,
                MATS,
                SETTINGS,
                sign_locs,
                detail_ent,
                noise_weight=weights[x, y],
                noise_func=noise,
            )

    add_floor_sides(vmf, floor_edges)

    return conditions.RES_EXHAUSTED
Example #9
0
    def generate_world(self, num_foods):
        """
        generate_world(int, int) -> None
        Initializes the world grid to a grid of cells with smoothly varying
        height and the specified number of randomly placed foods and water sources.
        @todo read in a real bay model (@DataBay_MD)
        """
        # Fill grid cells - picking the specified number of food and water sources
        noise = SimplexNoise()
        noise.randomize(16)

        # First generate world full of plain land cells
        index = 0
        if False:
            for row_num in range(self.world_height):
                row_list = []
                for col_num in range(self.world_width):
                    # Uses Perlin noise to generate elevation
                    # https://pypi.python.org/pypi/noise
                    elev = (1.0 + noise.noise2(col_num / TERRAIN_SMOOTHNESS, row_num / TERRAIN_SMOOTHNESS)) * 5.0 / 2
                    cell = LandCell(self, [row_num, col_num], elev)
                    row_list.append(cell)
                    index += 1
                self.grid.append(row_list)
        else:
            lines = open(DATA_FILE).readlines()
            nxy = self.world_height*self.world_width
            if len(lines) != nxy:
                print "bad file ",nxy,len(lines)
            for row_num in range(self.world_height):
                row_list = []
                for col_num in range(self.world_width):
                    # lines have X,Y,ELEV, for now we ignore X,Y and assume listed in the right order
                    elev = float(lines[index].strip().split()[2])
                    cell = LandCell(self, [row_num, col_num], elev)
                    row_list.append(cell)
                    index += 1
                self.grid.append(row_list)

        # Calculate elevation extrema
        for row_num in range(self.world_height):
            for col_num in range(self.world_width):
                c = self.grid[row_num][col_num]
                if c.elevation < self.elevation_min:
                    self.elevation_min = c.elevation
                if c.elevation > self.elevation_max:
                    self.elevation_max = c.elevation

        # write to a dump file
        if False:
            for row_num in range(self.world_height):
                for col_num in range(self.world_width):
                    c = self.grid[row_num][col_num]
                    print row_num, col_num, c.elevation

        # Add some buildings (replace some cells with buildings)
        num_buildings = NUM_BUILDINGS
        while num_buildings > 0:
            row_num = random.randint(0, self.world_height - 1)
            col_num = random.randint(0, self.world_width - 1)
            cell = self.grid[row_num][col_num]
            elevation = cell.get_elevation()
            if INIT_WATER_LEVEL < elevation < INIT_WATER_LEVEL + 1:
                building_cell = BuildingCell(self, [row_num, col_num], elevation)
                self.grid[row_num][col_num] = building_cell
                num_buildings -= 1

        # Add some plants (replace some cells with plants)
        while num_foods > 0:
            row_num = random.randint(0, self.world_height - 1)
            col_num = random.randint(0, self.world_width - 1)
            cell = self.grid[row_num][col_num]
            elevation = cell.get_elevation()
            if INIT_WATER_LEVEL < elevation < self.elevation_max - 1:
                plant_cell = ArableLandCell(self, [row_num, col_num], elevation)
                self.grid[row_num][col_num] = plant_cell
                num_foods -= 1

        # Add water sources (replace some cells with water)
        for water_source_loc in WATER_SOURCES:
            row_num = water_source_loc[ROW_INDEX]
            col_num = water_source_loc[COL_INDEX]
            cell = self.grid[row_num][col_num]
            elevation = cell.get_elevation()
            if INIT_WATER_LEVEL < elevation:
                water_cell = WaterSourceCell(self, [row_num, col_num], elevation)
                self.grid[row_num][col_num] = water_cell

        # Add the crabs (replace some water cells with creatures)
        num_crabs = NUM_CRABS
        while num_crabs > 0:
            row_num = random.randint(0, self.world_height - 1)
            col_num = random.randint(0, self.world_width - 1)
            cell = self.grid[row_num][col_num]
            elevation = cell.get_elevation()
            if elevation < INIT_WATER_LEVEL - 1:
                crab = Crab([row_num, col_num])
                cell.crab = crab
                self.num_crabs += 1
                num_crabs -= 1
Example #10
0
def res_cutout_tile(res: Property):
    """Generate random quarter tiles, like in Destroyed or Retro maps.

    - "MarkerItem" is the instance to look for.
    - "TileSize" can be "2x2" or "4x4".
    - rotateMax is the amount of degrees to rotate squarebeam models.

    Materials:
    - "squarebeams" is the squarebeams variant to use.
    - "ceilingwalls" are the sides of the ceiling section.
    - "floorbase" is the texture under floor sections.
    - "tile_glue" is used on top of a thinner tile segment.
    - "clip" is the player_clip texture used over floor segments.
        (This allows customising the surfaceprop.)
    - "Floor4x4Black", "Ceil2x2White" and other combinations can be used to
       override the textures used.
    """
    item = instanceLocs.resolve(res['markeritem'])

    INST_LOCS = {}  # Map targetnames -> surface loc
    CEIL_IO = []  # Pairs of ceil inst corners to cut out.
    FLOOR_IO = []  # Pairs of floor inst corners to cut out.

    overlay_ids = {}  # When we replace brushes, we need to fix any overlays
    # on that surface.

    MATS.clear()
    floor_edges = []  # Values to pass to add_floor_sides() at the end

    sign_loc = set(FORCE_LOCATIONS)
    # If any signage is present in the map, we need to force tiles to
    # appear at that location!
    for over in conditions.VMF.by_class['info_overlay']:
        if (over['material'].casefold() in FORCE_TILE_MATS and
                # Only check floor/ceiling overlays
                over['basisnormal'] in ('0 0 1', '0 0 -1')):
            loc = Vec.from_str(over['origin'])
            # Sometimes (light bridges etc) a sign will be halfway between
            # tiles, so in that case we need to force 2 tiles.
            loc_min = (loc - (15, 15, 0)) // 32 * 32  # type: Vec
            loc_max = (loc + (15, 15, 0)) // 32 * 32  # type: Vec
            loc_min += (16, 16, 0)
            loc_max += (16, 16, 0)
            FORCE_LOCATIONS.add(loc_min.as_tuple())
            FORCE_LOCATIONS.add(loc_max.as_tuple())

    SETTINGS = {
        'floor_chance':
        srctools.conv_int(res['floorChance', '100'], 100),
        'ceil_chance':
        srctools.conv_int(res['ceilingChance', '100'], 100),
        'floor_glue_chance':
        srctools.conv_int(res['floorGlueChance', '0']),
        'ceil_glue_chance':
        srctools.conv_int(res['ceilingGlueChance', '0']),
        'rotate_beams':
        int(srctools.conv_float(res['rotateMax', '0']) * BEAM_ROT_PRECISION),
        'beam_skin':
        res['squarebeamsSkin', '0'],
        'base_is_disp':
        srctools.conv_bool(res['dispBase', '0']),
        'quad_floor':
        res['FloorSize', '4x4'].casefold() == '2x2',
        'quad_ceil':
        res['CeilingSize', '4x4'].casefold() == '2x2',
    }

    random.seed(vbsp.MAP_RAND_SEED + '_CUTOUT_TILE_NOISE')
    noise = SimplexNoise(period=4 * 40)  # 4 tiles/block, 50 blocks max

    # We want to know the number of neighbouring tile cutouts before
    # placing tiles - blocks away from the sides generate fewer tiles.
    floor_neighbours = defaultdict(dict)  # all_floors[z][x,y] = count

    for mat_prop in res['Materials', []]:
        MATS[mat_prop.name].append(mat_prop.value)

    if SETTINGS['base_is_disp']:
        # We want the normal brushes to become nodraw.
        MATS['floorbase_disp'] = MATS['floorbase']
        MATS['floorbase'] = ['tools/toolsnodraw']

        # Since this uses random data for initialisation, the alpha and
        # regular will use slightly different patterns.
        alpha_noise = SimplexNoise(period=4 * 50)
    else:
        alpha_noise = None

    for key, default in TEX_DEFAULT:
        if key not in MATS:
            MATS[key] = [default]

    # Find our marker ents
    for inst in conditions.VMF.by_class['func_instance']:  # type: VLib.Entity
        if inst['file'].casefold() not in item:
            continue
        targ = inst['targetname']
        orient = Vec(0, 0, 1).rotate_by_str(inst['angles', '0 0 0'])
        # Check the orientation of the marker to figure out what to generate
        if orient == (0, 0, 1):
            io_list = FLOOR_IO
        else:
            io_list = CEIL_IO

        # Reuse orient to calculate where the solid face will be.
        loc = (orient * -64) + Vec.from_str(inst['origin'])
        INST_LOCS[targ] = loc

        for out in inst.output_targets():
            io_list.append((targ, out))

        if not inst.outputs and inst.fixup['$connectioncount'] == '0':
            # If the item doesn't have any connections, 'connect'
            # it to itself so we'll generate a 128x128 tile segment.
            io_list.append((targ, targ))
        inst.remove()  # Remove the instance itself from the map.

    for start_floor, end_floor in FLOOR_IO:
        if end_floor not in INST_LOCS:
            # Not a marker - remove this and the antline.
            for toggle in conditions.VMF.by_target[end_floor]:
                conditions.remove_ant_toggle(toggle)
            continue

        box_min = Vec(INST_LOCS[start_floor])
        box_min.min(INST_LOCS[end_floor])

        box_max = Vec(INST_LOCS[start_floor])
        box_max.max(INST_LOCS[end_floor])

        if box_min.z != box_max.z:
            continue  # They're not in the same level!
        z = box_min.z

        if SETTINGS['rotate_beams']:
            # We have to generate 1 model per 64x64 block to do rotation...
            gen_rotated_squarebeams(
                box_min - (64, 64, 0),
                box_max + (64, 64, -8),
                skin=SETTINGS['beam_skin'],
                max_rot=SETTINGS['rotate_beams'],
            )
        else:
            # Make the squarebeams props, using big models if possible
            gen_squarebeams(box_min + (-64, -64, 0),
                            box_max + (64, 64, -8),
                            skin=SETTINGS['beam_skin'])

        # Add a player_clip brush across the whole area
        conditions.VMF.add_brush(
            conditions.VMF.make_prism(
                p1=box_min - (64, 64, FLOOR_DEPTH),
                p2=box_max + (64, 64, 0),
                mat=MATS['clip'][0],
            ).solid)

        # Add a noportal_volume covering the surface, in case there's
        # room for a portal.
        noportal_solid = conditions.VMF.make_prism(
            # Don't go all the way to the sides, so it doesn't affect wall
            # brushes.
            p1=box_min - (63, 63, 9),
            p2=box_max + (63, 63, 0),
            mat='tools/toolsinvisible',
        ).solid
        noportal_ent = conditions.VMF.create_ent(
            classname='func_noportal_volume',
            origin=box_min.join(' '),
        )
        noportal_ent.solids.append(noportal_solid)

        if SETTINGS['base_is_disp']:
            # Use displacements for the base instead.
            make_alpha_base(
                box_min + (-64, -64, 0),
                box_max + (64, 64, 0),
                noise=alpha_noise,
            )

        for x, y in utils.iter_grid(
                min_x=int(box_min.x),
                max_x=int(box_max.x) + 1,
                min_y=int(box_min.y),
                max_y=int(box_max.y) + 1,
                stride=128,
        ):
            # Build the set of all positions..
            floor_neighbours[z][x, y] = -1

        # Mark borders we need to fill in, and the angle (for func_instance)
        # The wall is the face pointing inwards towards the bottom brush,
        # and the ceil is the ceiling of the block above the bordering grid
        # points.
        for x in range(int(box_min.x), int(box_max.x) + 1, 128):
            # North
            floor_edges.append(
                BorderPoints(
                    wall=Vec(x, box_max.y + 64, z - 64),
                    ceil=Vec_tuple(x, box_max.y + 128, z),
                    rot=270,
                ))
            # South
            floor_edges.append(
                BorderPoints(
                    wall=Vec(x, box_min.y - 64, z - 64),
                    ceil=Vec_tuple(x, box_min.y - 128, z),
                    rot=90,
                ))

        for y in range(int(box_min.y), int(box_max.y) + 1, 128):
            # East
            floor_edges.append(
                BorderPoints(
                    wall=Vec(box_max.x + 64, y, z - 64),
                    ceil=Vec_tuple(box_max.x + 128, y, z),
                    rot=180,
                ))

            # West
            floor_edges.append(
                BorderPoints(
                    wall=Vec(box_min.x - 64, y, z - 64),
                    ceil=Vec_tuple(box_min.x - 128, y, z),
                    rot=0,
                ))

    # Now count boundries near tiles, then generate them.

    # Do it seperately for each z-level:
    for z, xy_dict in floor_neighbours.items():  # type: float, dict
        for x, y in xy_dict:  # type: float, float
            # We want to count where there aren't any tiles
            xy_dict[x, y] = (((x - 128, y - 128) not in xy_dict) +
                             ((x - 128, y + 128) not in xy_dict) +
                             ((x + 128, y - 128) not in xy_dict) +
                             ((x + 128, y + 128) not in xy_dict) +
                             ((x - 128, y) not in xy_dict) +
                             ((x + 128, y) not in xy_dict) +
                             ((x, y - 128) not in xy_dict) +
                             ((x, y + 128) not in xy_dict))

        max_x = max_y = 0

        weights = {}
        # Now the counts are all correct, compute the weight to apply
        # for tiles.
        # Adding the neighbouring counts will make a 5x5 area needed to set
        # the center to 0.

        for (x, y), cur_count in xy_dict.items():
            max_x = max(x, max_x)
            max_y = max(y, max_y)

            # Orthrogonal is worth 0.2, diagonal is worth 0.1.
            # Not-present tiles would be 8 - the maximum
            tile_count = (0.8 * cur_count + 0.1 * xy_dict.get(
                (x - 128, y - 128), 8) + 0.1 * xy_dict.get(
                    (x - 128, y + 128), 8) + 0.1 * xy_dict.get(
                        (x + 128, y - 128), 8) + 0.1 * xy_dict.get(
                            (x + 128, y + 128), 8) + 0.2 * xy_dict.get(
                                (x - 128, y), 8) + 0.2 * xy_dict.get(
                                    (x, y - 128), 8) + 0.2 * xy_dict.get(
                                        (x, y + 128), 8) + 0.2 * xy_dict.get(
                                            (x + 128, y), 8))
            # The number ranges from 0 (all tiles) to 12.8 (no tiles).
            # All tiles should still have a small chance to generate tiles.
            weights[x, y] = min((tile_count + 0.5) / 8, 1)

        # Share the detail entity among same-height tiles..
        detail_ent = conditions.VMF.create_ent(classname='func_detail', )

        for x, y in xy_dict:
            convert_floor(
                Vec(x, y, z),
                overlay_ids,
                MATS,
                SETTINGS,
                sign_loc,
                detail_ent,
                noise_weight=weights[x, y],
                noise_func=noise,
            )

    add_floor_sides(floor_edges)

    conditions.reallocate_overlays(overlay_ids)

    return conditions.RES_EXHAUSTED
Example #11
0
import ImageFilter
from perlin import SimplexNoise

OUTPUT_DIR = "../heightmaps/"
OUTPUT_PATH = OUTPUT_DIR+"generated.png"

IMAGE_WIDTH  = 1024
IMAGE_HEIGHT = 1024
frequencies = [300.0, 100.0, 75.0, 50.0, 5.0, 1.0]
amplitudes  = [1.0,     0.6,  0.3,  0.2, 0.1, 0.05]
octaveData = zip(frequencies, amplitudes)

img = Image.new('RGB', (IMAGE_WIDTH, IMAGE_HEIGHT), "black")
pixelBuffer = img.load()

noise = SimplexNoise()
noise.randomize()

print ("Generating heightmap")
for x in range(IMAGE_WIDTH):
    for y in range(IMAGE_HEIGHT):
        def octaveContributionToPixel(frequencyAmplitudePair):
            frequency, amplitude = frequencyAmplitudePair
            xCoordinate = x / frequency
            yCoordinate = y / frequency
            return noise.noise2(xCoordinate, yCoordinate) * amplitude

        contributionsToPixel = map(octaveContributionToPixel, octaveData)
        # each amplitude is the maximum amount that octave can contribute to
        # the pixel, so this gives a pixelValue in the range [-1, 1]
        pixelValue = sum(contributionsToPixel) / sum(amplitudes)