changeset 0:93ed83bdf8e9

Initial commit
author Radomir Dopieralski <sheep-devel@sheep.art.pl>
date Sun, 31 Mar 2013 18:02:50 +0200
parents
children fc8568408eb0
files .hgignore rrr/__init__.py rrr/display_curses.py rrr/light.py run.py
diffstat 5 files changed, 270 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore	Sun Mar 31 18:02:50 2013 +0200
@@ -0,0 +1,4 @@
+syntax: glob
+
+.*.swp
+*.pyc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rrr/__init__.py	Sun Mar 31 18:02:50 2013 +0200
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rrr/display_curses.py	Sun Mar 31 18:02:50 2013 +0200
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import curses
+import collections
+
+
+class Display(object):
+    def __init__(self, stdscr):
+        self.init_windows(stdscr)
+        self.init_colors()
+        self.init_keys()
+        self.margin = 9
+        self.xmap, self.ymap = 0, 0
+        self.maxmapy, self.maxmapx = self.map.getmaxyx()
+
+    def init_windows(self, stdscr):
+        maxy, maxx = stdscr.getmaxyx()
+        self.stats = curses.newwin(1, 16, 0, maxx - 16)
+        self.stats.scrollok(0)
+        self.stats.immedok(0)
+        self.map = curses.newwin(maxy, maxx - 16, 0, 0)
+        self.map.keypad(1)
+        self.map.immedok(0)
+        self.map.leaveok(0)
+        self.sidebar = curses.newwin(maxy - 1, 16, 1, maxx - 16)
+        self.sidebar.immedok(0)
+        self.sidebar.scrollok(1)
+        curses.meta(1)
+
+    def init_colors(self):
+        self.colors = collections.defaultdict(lambda: curses.A_NORMAL)
+        if not curses.has_colors():
+            return
+        curses.start_color()
+        curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_BLACK)
+        curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
+        curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
+        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)
+        self.colors.update({
+            'gray': curses.color_pair(0),
+            'white': curses.color_pair(0) | curses.A_BOLD,
+            'black': curses.color_pair(1) | curses.A_BOLD,
+            'lime': curses.color_pair(2) | curses.A_BOLD,
+            'green': curses.color_pair(2),
+            'yellow': curses.color_pair(3) | curses.A_BOLD,
+            'brown': curses.color_pair(3),
+            'red': curses.color_pair(5) | curses.A_BOLD,
+            'maroon': curses.color_pair(5),
+            'aqua': curses.color_pair(6) | curses.A_BOLD,
+            'cyan': curses.color_pair(6),
+            'magenta': curses.color_pair(4),
+        })
+
+    def init_keys(self):
+        self.keys = {
+            "KEY_UP":      ('arrow', ( 0,-1)),
+            "KEY_DOWN":    ('arrow', ( 0, 1)),
+            "KEY_LEFT":    ('arrow', (-1, 0)),
+            "KEY_RIGHT":   ('arrow', ( 1, 0)),
+            "KEY_A1":      ('arrow', (-1,-1)),
+            "KEY_A3":      ('arrow', ( 1,-1)),
+            "KEY_C1":      ('arrow', (-1, 1)),
+            "KEY_C3":      ('arrow', ( 1, 1)),
+            "KEY_HOME":    ('arrow', (-1,-1)),
+            "KEY_FIND":    ('arrow', (-1,-1)),
+            "KEY_PPAGE":   ('arrow', ( 1,-1)),
+            "KEY_END":     ('arrow', (-1, 1)),
+            "KEY_SELECT":  ('arrow', (-1, 1)),
+            "KEY_NPAGE":   ('arrow', ( 1, 1)),
+            '8':           ('arrow', ( 0,-1)),
+            '2':           ('arrow', ( 0, 1)),
+            '4':           ('arrow', (-1, 0)),
+            '6':           ('arrow', ( 1, 0)),
+            '7':           ('arrow', (-1,-1)),
+            '9':           ('arrow', ( 1,-1)),
+            '1':           ('arrow', (-1, 1)),
+            '3':           ('arrow', ( 1, 1)),
+            'KEY_B2':      ("wait", None),
+            '5':           ("wait", None),
+            'KEY_ESC':     ("esc", None),
+            ' ':           ("more", None),
+        }
+
+
+    def message(self, message, color='green'):
+        self.sidebar.addstr(message, self.colors[color])
+
+    def map_draw(self, (x, y), char, color=0):
+        x -= self.xmap
+        y -= self.ymap
+        if not (0 <= x < self.maxmapx and 0 <= y < self.maxmapy):
+            return
+        c = ord(char) | self.colors[color]
+        self.map.addch(y, x, c)
+
+
+    def stats(self, stats):
+        self.stats.clear()
+        self.stats.addstr(0, 0, stats)
+
+    def unselect(self):
+        self.map.move(0, 0)
+        curses.curs_set(0)
+
+    def select(self, (x, y)):
+        x -= self.xmap
+        y -= self.ymap
+        if not (0 <= x < self.maxmapx and 0 <= y < self.maxmapy):
+            self.unselect()
+            return
+        curses.curs_set(1)
+        self.map.move(y, x)
+
+    def center_map(self, (x, y)):
+        sy, sx = self.map.getmaxyx()
+        self.xmap = -sx // 2 + x
+        self.ymap = -sy // 2 + y
+
+    def scroll_map(self, (x, y)):
+        maxy, maxx = self.map.getmaxyx()
+        if x - self.xmap < self.margin:
+            self.xmap -= self.margin
+        elif y - self.ymap < self.margin:
+            self.ymap -= self.margin
+        elif x - self.xmap > self.maxmapx - self.margin:
+            self.xmap += self.margin
+        elif y - self.ymap > self.maxmapy - self.margin:
+            self.ymap += self.margin
+        else:
+            return False
+        return True
+
+    def clear(self):
+        self.msg.clear()
+        self.map.clear()
+        self.inv.clear()
+        self.stat.clear()
+
+    def refresh(self):
+        self.sidebar.noutrefresh()
+        self.stats.noutrefresh()
+        self.map.noutrefresh()
+        curses.doupdate()
+
+    def get_command(self, timeout=-1):
+        self.map.timeout(timeout)
+        #try:
+        key = self.map.getkey()
+        #except:
+        #    return (None, None)
+        if key=="-1":
+            return None, None
+        try:
+            return self.keys[key]
+        except KeyError:
+            return 'key', key
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rrr/light.py	Sun Mar 31 18:02:50 2013 +0200
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import math
+
+
+class LightRaycasting(object):
+    """
+    This is an implementation of a raycasting field of vision algorithm,
+    not unlike the one used by Angband.
+    """
+
+    def __init__(self, max_radius=12, max_rays=255):
+        self.max_radius = max_radius
+        self.max_rays = max_rays
+        self.ray_max = []
+        self.ray_min = []
+        for y in xrange(max_radius):
+            line_max = []
+            line_min = []
+            for x in xrange(max_radius):
+                line_max.append(0)
+                line_min.append(max_rays)
+            self.ray_max.append(line_max)
+            self.ray_min.append(line_min)
+            for ray in xrange(max_rays):
+                an = (math.pi * ray) / (self.max_rays * 2)
+                s = math.sin(an) / 4
+                c = math.cos(an) / 4
+                d = 0
+                x = 0
+                y = 0
+                while 1:
+                    x = int(math.floor(s * d))
+                    y = int(math.floor(c * d))
+                    d += 1
+                    if x < max_radius and y < max_radius:
+                        self.ray_min[x][y] = min(self.ray_min[x][y], ray)
+                        self.ray_max[x][y] = max(self.ray_max[x][y], ray)
+                    else:
+                        break
+
+    def _quart(self, pos, r, is_opaque, dx, dy):
+        max_rays = self.max_rays
+        ray_max = self.ray_max
+        ray_min = self.ray_min
+        xpos, ypos = pos
+        lit = set()
+        ox = pos[0]
+        rays = [0] * max_rays
+        corner = r
+        for y in xrange(r):
+            if 2 * y >= r:
+                corner -= 1
+            for x in xrange(corner):
+                rayweight = max_rays / (ray_max[x][y] - ray_min[x][y])
+                shadow = sum(
+                    rayweight
+                    for ray in rays[ray_min[x][y]:ray_max[x][y]]
+                    if ray
+                )
+                if 3 * shadow < 2 * max_rays:
+                    lit.add((xpos, ypos))
+                if is_opaque((xpos, ypos)):
+                    rays[ray_min[x][y]:ray_max[x][y]] = 1
+                xpos += dx
+            ypos += dy
+            xpos = ox
+        return lit
+
+    def cast(self, pos, radius, is_opaque):
+        """Returns a set of all the coordinates visible from position ``pos``
+           within the radius ``radius``, using ``is_opaque`` function to check
+           if the given coordinate blocks light.
+        """
+        r = min(self.max_radius, radius)
+        return (
+            pos |
+            self._quart(pos, r, is_opaque, 1, 1) |
+            self._quart(pos, r, is_opaque, -1, 1) |
+            self._quart(pos, r, is_opaque, 1, -1) |
+            self._quart(pos, r, is_opaque, -1, -1)
+        )
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/run.py	Sun Mar 31 18:02:50 2013 +0200
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import curses
+
+from rrr.display_curses import Display
+from rrr.light import LightRaycasting as Light
+
+def main(stdscr):
+    display = Display(stdscr)
+    display.message('Hello world!')
+    display.unselect()
+    display.center_map((0, 0))
+    display.map_draw((0, 0), '@', 'white')
+    display.refresh()
+    display.get_command()
+
+if __name__ == '__main__':
+    curses.wrapper(main)