changeset 5:6666dbf909be

Fractal level generator
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Mon, 01 Apr 2013 21:44:59 +0200
parents c290a652a783
children c98a36b9393e
files rrr/display_curses.py rrr/level.py run.py
diffstat 3 files changed, 111 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/rrr/display_curses.py	Sun Mar 31 23:55:41 2013 +0200
+++ b/rrr/display_curses.py	Mon Apr 01 21:44:59 2013 +0200
@@ -23,6 +23,7 @@
         self.map.keypad(1)
         self.map.immedok(0)
         self.map.leaveok(0)
+        self.map.scrollok(0)
         self.sidebar = curses.newwin(maxy - 1, 16, 1, maxx - 16)
         self.sidebar.immedok(0)
         self.sidebar.scrollok(1)
@@ -39,6 +40,7 @@
         curses.init_pair(4, curses.COLOR_MAGENTA, curses.COLOR_BLACK)
         curses.init_pair(5, curses.COLOR_RED, curses.COLOR_BLACK)
         curses.init_pair(6, curses.COLOR_CYAN, curses.COLOR_BLACK)
+        curses.init_pair(7, curses.COLOR_BLUE, curses.COLOR_BLACK)
         self.colors.update({
             'gray': curses.color_pair(0),
             'white': curses.color_pair(0) | curses.A_BOLD,
@@ -51,6 +53,9 @@
             'maroon': curses.color_pair(5),
             'aqua': curses.color_pair(6) | curses.A_BOLD,
             'cyan': curses.color_pair(6),
+            'blue': curses.color_pair(7) | curses.A_BOLD,
+            'navy': curses.color_pair(7),
+            'pink': curses.color_pair(4) | curses.A_BOLD,
             'magenta': curses.color_pair(4),
         })
 
@@ -81,12 +86,13 @@
             'KEY_B2':      ("wait", None),
             '5':           ("wait", None),
             'KEY_ESC':     ("esc", None),
+            '\x1b':        ("esc", None),
             ' ':           ("more", None),
         }
 
 
     def message(self, message, color='green'):
-        self.sidebar.addstr(message, self.colors[color])
+        self.sidebar.addstr(message + '\n', self.colors[color])
 
     def map_draw(self, (x, y), char, color=0):
         x -= self.xmap
@@ -94,8 +100,12 @@
         if not (0 <= x < self.maxmapx and 0 <= y < self.maxmapy):
             return
         c = ord(char) | self.colors[color]
-        self.map.addch(y, x, c)
-
+        try:
+            self.map.addch(y, x, c)
+        except curses.error:
+            # For some reason it throws an error when drawing the character
+            # in the lower right corner of the map window. We ignore it here.
+            pass
 
     def stats_draw(self, stats):
         self.stats.clear()
@@ -134,10 +144,9 @@
         return True
 
     def clear(self):
-        self.msg.clear()
+        self.sidebar.clear()
         self.map.clear()
-        self.inv.clear()
-        self.stat.clear()
+        self.stats.clear()
 
     def refresh(self):
         self.sidebar.noutrefresh()
--- a/rrr/level.py	Sun Mar 31 23:55:41 2013 +0200
+++ b/rrr/level.py	Mon Apr 01 21:44:59 2013 +0200
@@ -1,34 +1,86 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 
+import random
+import math
+
+
+TERRAIN = [
+    {
+        'name': 'invalid',
+        'symbol': '?',
+        'color': 'magenta',
+        'opaque': True,
+        'block': True,
+    },
+    {
+        'name': 'ground',
+        'symbol': '.',
+        'color': 'black',
+        'opaque': False,
+        'block': False,
+    },
+    {
+        'name': 'ground',
+        'symbol': '.',
+        'color': 'gray',
+        'opaque': False,
+        'block': False,
+    },
+    {
+        'name': 'rocks',
+        'symbol': ':',
+        'color': 'gray',
+        'opaque': False,
+        'block': False,
+    },
+    {
+        'name': 'wall',
+        'symbol': '#',
+        'color': 'gray',
+        'opaque': True,
+        'block': True,
+    },
+
+]
+
 class Level(object):
-    def __init__(self):
-        self.map = [
-            '...#############',
-            '...#...........#',
-            '#..#...........#',
-            '#..#...........#',
-            '#..............#',
-            '#..............#',
-            '#....####......#',
-            '#....#.........#',
-            '#....#.........#',
-            '#....######....#',
-            '#..............#',
-            '#..............#',
-            '#..............#',
-            '###########.#..#',
-            '#...........#..#',
-            '################',
+    def __init__(self, width, height):
+        self.width = width
+        self.height = height
+        self.explored = set()
+        self.map = self.fractal_fill([1, 1, 2, 2, 2, 3, 3, 4, 4])
+
+    def fractal_fill(self, gradient):
+        max_power = int(math.ceil(math.log(max(self.width, self.height), 2)))
+        frac = [[0] * 2**max_power for y in xrange(2**max_power)]
+        frac[0][0] = 4
+        for i in xrange(max_power + 1):
+            size = 2 ** i
+            for y in xrange(size - 1, -1, -1):
+                for x in xrange(size - 1, -1, -1):
+                    frac[y][x] = max(0, min(len(gradient) - 1,
+                        frac[y // 2][x // 2] + random.choice([-1, 0, 1])
+                    ))
+        return [
+            [gradient[cell] for cell in row[:self.width]]
+            for row in frac[:self.height]
         ]
 
-    def is_opaque(self, (x, y)):
-        if not (0<=x<16 and 0<=y<16):
-            return True
-        return self.map[y][x] == '#'
+    def is_opaque(self, pos):
+        t = self.get_terrain(pos)
+        return t['opaque']
+
+    def get_char(self, pos):
+        t = self.get_terrain(pos)
+        return t['symbol'], t['color']
 
-    def get_char(self, (x, y)):
-        if not (0<=x<16 and 0<=y<16):
-            return 'X'
-        return self.map[y][x]
-
+    def get_terrain(self, (x, y)):
+        if not (0 <= x < self.width and 0 <= y < self.height):
+            t = TERRAIN[0]
+        else:
+            try:
+                t = TERRAIN[self.map[y][x]]
+            except IndexError:
+                t = TERRAIN[0]
+        return t
--- a/run.py	Sun Mar 31 23:55:41 2013 +0200
+++ b/run.py	Mon Apr 01 21:44:59 2013 +0200
@@ -11,7 +11,7 @@
 def main(stdscr):
     display = Display(stdscr)
     light = Light()
-    level = Level()
+    level = Level(64, 64)
     x, y = 0, 0
     display.stats_draw('HP 100/100')
     display.message('Hello world!')
@@ -19,23 +19,28 @@
     display.center_map((x, y))
     lit = set()
     while True:
-        for p in lit:
-            display.map_draw(p, level.get_char(p), 'magenta')
-        lit = light.cast((x, y), 12, level.is_opaque)
+        oldlit = lit
+        if display.scroll_map((x, y)):
+            display.map.clear()
+            for p in level.explored:
+                ch, c = level.get_char(p)
+                display.map_draw(p, ch, 'magenta')
+        lit = light.cast((x, y), 8, level.is_opaque)
+        level.explored |= lit
+        for p in oldlit - lit:
+            ch, c = level.get_char(p)
+            display.map_draw(p, ch, 'magenta')
         for p in lit:
-            c = level.get_char(p)
-            if c == '.':
-                color = 'black'
-            else:
-                color = 'gray'
-            display.map_draw(p, c, color)
+            ch, c = level.get_char(p)
+            display.map_draw(p, ch, c)
         display.map_draw((x, y), '@', 'white')
         display.refresh()
         command, params = display.get_command()
         if command == 'arrow':
             dx, dy = params
-            x += dx
-            y += dy
+            if not level.get_terrain((x + dx, y + dy))['block']:
+                x += dx
+                y += dy
         elif command == 'key' and params == 'q':
             raise SystemExit()