Пример #1
0
    def generate(self):
        self.map_canvas.clear(Floor)

        room = Room(self.region)
        room.draw_to_canvas(self.map_canvas)

        noise = discrete_perlin_noise_factory(*self.region.size, resolution=5, octaves=4)
        for point in self.region.iter_points():
            # TODO would greatly prefer some architecture types that just have
            # a 'decay' property affecting their rendering, but that would
            # require rendering to be per-entity, and either a method or
            # something that could be updated on the fly
            if self.map_canvas._arch_grid[point] is Wall:
                n = noise(*point)
                if n < 0.7:
                    arch = e.Ruin(Breakable(n / 0.7))
                else:
                    arch = e.Wall
                self.map_canvas.set_architecture(point, arch)
            elif self.map_canvas._arch_grid[point] is Floor:
                n = noise(*point)
                if n < 0.5:
                    arch = e.Rubble(Breakable(n / 0.5))
                else:
                    arch = e.Floor
                self.map_canvas.set_architecture(point, arch)
Пример #2
0
    def _generate_river(self, noise):
        # TODO seriously starting to feel like i need a Feature type for these
        # things?  like, passing `noise` around is a really weird way to go
        # about this.  what would the state even look like though?

        '''
        # TODO i think this needs another flooding algorithm, which probably
        # means it needs to be a lot simpler and faster...
        noise_factory = discrete_perlin_noise_factory(
            *self.region.size, resolution=2, octaves=1)

        noise = {
            point: abs(noise_factory(*point) - 0.5) * 2
            for point in self.region.iter_points()
        }
        for point, n in noise.items():
            if n < 0.2:
                self.map_canvas.set_architecture(point, e.Water)
        return
        '''

        # Build some Blob internals representing the two halves of the river.
        left_side = {}
        right_side = {}
        river = {}

        center_factory = discrete_perlin_noise_factory(
            self.region.height, resolution=3)
        width_factory = discrete_perlin_noise_factory(
            self.region.height, resolution=6, octaves=2)
        center = random_normal_int(
            self.region.center().x, self.region.width / 4 / 3)
        for y in self.region.range_height():
            center += (center_factory(y) - 0.5) * 3
            width = width_factory(y) * 2 + 5
            x0 = int(center - width / 2)
            x1 = int(x0 + width + 0.5)
            for x in range(x0, x1 + 1):
                self.map_canvas.set_architecture(Point(x, y), e.Water)

            left_side[y] = (Span(self.region.left, x0 - 1),)
            right_side[y] = (Span(x1 + 1, self.region.right),)
            river[y] = (Span(x0, x1),)

        return Blob(left_side), Blob(river), Blob(right_side)
Пример #3
0
 def generate(self):
     from flax.noise import discrete_perlin_noise_factory
     noise = discrete_perlin_noise_factory(*self.region.size, resolution=4, octaves=2)
     for point in self.region.iter_points():
         n = noise(*point)
         if n < 0.2:
             arch = Floor
         elif n < 0.4:
             arch = Dirt
         elif n < 0.6:
             arch = CutGrass
         elif n < 0.8:
             arch = Grass
         else:
             arch = Tree
         self.map_canvas.arch_grid[point] = arch
Пример #4
0
    def generate(self):
        # TODO not guaranteed that all the walkable spaces are attached
        noise_factory = discrete_perlin_noise_factory(*self.region.size, resolution=5, octaves=1)
        noise = {point: noise_factory(*point) for point in self.region.iter_points()}
        low_points = set()
        for point, n in noise.items():
            if n < 0.3:
                low_points.add(point)
                arch = CutGrass
            elif n < 0.6:
                arch = Grass
            else:
                arch = Tree
            self.map_canvas.set_architecture(point, arch)

        local_minima = set()
        for point in sorted(low_points, key=lambda pt: noise[pt]):
            n = noise[point]
            if any(noise[npt] < n for npt in point.neighbors if npt in low_points):
                continue
            local_minima.add(point)

        for x in self.region.range_width():
            for y in (self.region.top, self.region.bottom):
                point = Point(x, y)
                n = noise[point]
                if n < noise.get(Point(x - 1, y), 1) and n < noise.get(Point(x + 1, y), 1):
                    local_minima.add(point)
        for y in self.region.range_height():
            for x in (self.region.left, self.region.right):
                point = Point(x, y)
                n = noise[point]
                if n < noise.get(Point(x, y - 1), 1) and n < noise.get(Point(x, y + 1), 1):
                    local_minima.add(point)

        for point in local_minima:
            self.map_canvas.set_architecture(point, e.Dirt)

        # Flood the forest.
        floodzones = dict(zip(local_minima, range(len(local_minima))))
        zone_map = {z: z for z in floodzones.values()}
        pending = defaultdict(set)
        paths = defaultdict(dict)
        for point, zone in floodzones.items():
            for neighbor in point.neighbors:
                if neighbor not in noise:
                    continue
                pending[neighbor].add(zone)
                if zone not in paths[neighbor] or noise[paths[neighbor][zone]] > noise[point]:
                    paths[neighbor][zone] = point
        while pending:
            point = min(pending, key=noise.__getitem__)
            zones = pending.pop(point)
            zones = {zone_map[z] for z in zones}
            if len(zones) > 1:
                # Gasp!  A connection!
                self.map_canvas.set_architecture(point, e.Dirt)
                for zone, pt in paths[point].items():
                    if zone not in zones:
                        continue
                    # TODO it seems this can cause infinite loops,
                    # exacerbated by having more points
                    while pt:
                        self.map_canvas.set_architecture(pt, e.Dirt)
                        # TODO ugly.  should never have multiple branches
                        # though.  maybe?
                        pt = paths[pt].get(zone)
                canon_zone = min(zones)
                zones.remove(canon_zone)
                for from_zone, to_zone in zone_map.items():
                    if from_zone in zones or to_zone in zones:
                        zone_map[from_zone] = canon_zone
                # UGH need to rewrite paths in its entirety
                for pt, zonepoints in paths.items():
                    new_zonepoints = {}
                    for orig_zone, pt2 in zonepoints.items():
                        new_zone = zone_map[orig_zone]
                        if new_zone not in new_zonepoints or noise[new_zonepoints[new_zone]] > noise[pt2]:
                            new_zonepoints[new_zone] = pt2
                    paths[pt] = new_zonepoints
            else:
                canon_zone, = zones
            floodzones[point] = canon_zone

            for neighbor in point.neighbors:
                if neighbor not in noise:
                    continue
                if neighbor in floodzones:
                    continue
                pending[neighbor].add(canon_zone)
                if canon_zone not in paths[neighbor] or noise[paths[neighbor][canon_zone]] > noise[point]:
                    paths[neighbor][canon_zone] = point
Пример #5
0
    def generate(self):
        # This noise is interpreted roughly as the inverse of "frequently
        # travelled" -- low values are walked often (and are thus short grass),
        # high values are left alone (and thus are trees).
        noise_factory = discrete_perlin_noise_factory(
            *self.region.size, resolution=6)
        noise = {
            point: noise_factory(*point)
            for point in self.region.iter_points()
        }
        local_minima = set()
        for point, n in noise.items():
            # We want to ensure that each "walkable region" is connected.
            # First step is to collect all local minima -- any walkable tile is
            # guaranteed to be conneted to one.
            if all(noise[npt] >= n for npt in point.neighbors if npt in noise):
                local_minima.add(point)

            if n < 0.3:
                arch = CutGrass
            elif n < 0.6:
                arch = Grass
            else:
                arch = Tree
            self.map_canvas.set_architecture(point, arch)

        left_bank, river_blob, right_bank = self._generate_river(noise)

        # Decide where bridges should go.  They can only cross where there's
        # walkable space on both sides, so find all such areas.
        # TODO maybe a nicer api for testing walkability here
        # TODO this doesn't detect a walkable area on one side that has no
        # walkable area on the other side, and tbh i'm not sure what to do in
        # such a case anyway.  could forcibly punch a path through the trees, i
        # suppose?  that's what i'll have to do anyway, right?
        # TODO this will break if i ever add a loop in the river, but tbh i
        # have no idea how to draw bridges in that case
        new_block = True
        start = None
        end = None
        blocks = []
        for y, (span,) in river_blob.spans.items():
            if self.map_canvas._arch_grid[Point(span.start - 1, y)] is not Tree and \
                    self.map_canvas._arch_grid[Point(span.end + 1, y)] is not Tree:
                if new_block:
                    start = y
                    end = y
                    new_block = False
                else:
                    end = y
            else:
                if not new_block:
                    blocks.append((start, end))
                new_block = True
        if not new_block:
            blocks.append((start, end))

        for start, end in blocks:
            y = random_normal_range(start, end)
            span = river_blob.spans[y][0]
            local_minima.add(Point(span.start - 1, y))
            local_minima.add(Point(span.end + 1, y))
            for x in span:
                self.map_canvas.set_architecture(Point(x, y), e.Bridge)

        # Consider all local minima along the edges, as well.
        for x in self.region.range_width():
            for y in (self.region.top, self.region.bottom):
                point = Point(x, y)
                n = noise[point]
                if (n < noise.get(Point(x - 1, y), 1) and
                        n < noise.get(Point(x + 1, y), 1)):
                    local_minima.add(point)
        for y in self.region.range_height():
            for x in (self.region.left, self.region.right):
                point = Point(x, y)
                n = noise[point]
                if (n < noise.get(Point(x, y - 1), 1) and
                        n < noise.get(Point(x, y + 1), 1)):
                    local_minima.add(point)

        for point in local_minima:
            if point not in river_blob:
                self.map_canvas.set_architecture(point, e.Dirt)

        for blob in (left_bank, right_bank):
            paths = self.flood_valleys(blob, local_minima, noise)

            for path_point in paths:
                self.map_canvas.set_architecture(path_point, e.Dirt)

        # Whoops time for another step: generating a surrounding cave wall.
        for edge in Direction.orthogonal:
            width = self.region.edge_length(edge)
            wall_noise = discrete_perlin_noise_factory(width, resolution=6)
            for n in self.region.edge_span(edge):
                offset = int(wall_noise(n) * 4 + 1)
                for m in range(offset):
                    point = self.region.edge_point(edge, n, m)
                    self.map_canvas.set_architecture(point, e.CaveWall)