Mercurial > rrr
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)
