Unicode support.
authorNeil Moore <neil@s-z.org>
Sat, 31 May 2008 20:51:52 +0000 (16:51 -0400)
committerNeil Moore <neil@s-z.org>
Sat, 31 May 2008 21:49:40 +0000 (17:49 -0400)
New module appui.  Eventually AppUI will move there; for now, it
  contains just the hasuni (Unicode supported) flag.

Test for window.addwstr, and enable hasuni if it exists.

Modify render() functions to return an (ascii, unicode, attr) triple.

Add "uni" (unicode character) attribute to each item class.

Level.draw_level(), AppUI.draw_inventory(): draw with Unicode if
  possible.

Note that Unicode support will not work with stock Python: it needs
  the 'addwstr' curses window method, which is a local customization.

appui.py [new file with mode: 0644]
creature.py
items/items.itm
level.py
loc.py
roguelike.py
thing.py

diff --git a/appui.py b/appui.py
new file mode 100644 (file)
index 0000000..c0de62e
--- /dev/null
+++ b/appui.py
@@ -0,0 +1,20 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright © 2008 Neil Moore <neil@s-z.org>.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#  
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+pass
index db3bcaf..e3c1bbb 100644 (file)
@@ -109,7 +109,7 @@ class Creature(thing.Thing):
     def can_swim(self):
         return random.random() < .5
     def render(self, pic=None):
-        return ( "?", ui.Color('BrightRed').cp )
+        return ( "?", u"⚗", ui.Color('BrightRed').cp )
     def move_north(self):
         return self.try_move(self.location.north())
     def move_south(self):
@@ -173,7 +173,7 @@ class Player (Creature):
         super(Player,self).__init__(location)
         self.active = False
     def render(self, pic=None):
-        return ("@", ui.Color(('Bright' if self.active else '') + 'Magenta').cp)
+        return ("@", u"@", ui.Color(('Bright' if self.active else '') + 'Magenta').cp)
     def can_swim(self):
         return random.random() < .5
     def format_intrinsics(self):
index 0e2c42c..663137e 100644 (file)
@@ -1,15 +1,18 @@
 gem {
        ascii: "*",
+       uni: "★",
        material: glass,
 },
 
 corpse {
        ascii: "%",
+       uni: "☠",
        material: flesh,
 },
 
 money {
        ascii: "$",
+       uni: "$",
 },
 
 "gold piece" {
@@ -25,6 +28,7 @@ wieldable {
 weapon {
        isa: wieldable,
        ascii: ")",
+       uni: "⚔",
        slot: hand,
 },
 
@@ -36,12 +40,14 @@ sword {
 armor {
        isa: wieldable,
        ascii: "[",
+       uni: "[",
        slot: body,
 },
 
 shield {
        isa: armor,
        material: wood,
+       uni: "⨄",
        slot: arm,
 },
 
@@ -53,6 +59,7 @@ helmet {
 mask {
        isa: armor,
        slot: head,
+       uni: "☻",
 },
 
 "mask of water breathing" {
index adb4c6b..e6ec9ff 100644 (file)
--- a/level.py
+++ b/level.py
@@ -23,6 +23,7 @@ import math
 import re
 from cStringIO import StringIO
 
+import appui
 import loc
 import thing
 import cacher
@@ -497,8 +498,11 @@ class Level (object):
         vis &= (self.invalid_tiles | self.active_tiles)
         vis |= self.important_repaints
         for (y,x) in vis:
-            (char, color) = self.grid[y][x].render()
-            self.boardwin.addstr(y, x, char, color)
+            (char, unic, color) = self.grid[y][x].render()
+            if appui.hasuni:
+                self.boardwin.addwstr(y, x, unic, color)
+            else:
+                self.boardwin.addstr(y, x, char, color)
         self.invalid_tiles -= vis
         self.important_repaints.clear()
 
diff --git a/loc.py b/loc.py
index 00c9809..695d72a 100644 (file)
--- a/loc.py
+++ b/loc.py
@@ -177,10 +177,12 @@ class Floor(Tile):
     "A passable Tile."
     def __init__(self, lvl, y, x, objlist=[]):
         Tile.__init__(self, lvl, y, x)
-        self.pic = random.choice("  ."), random.choice((
-            ui.Color('BrightBlack').cp, ui.Color('Brown').cp,
-            ui.Color('BrightGreen').cp, ui.Color('Green').cp,
-            ))
+        self.pic = (random.choice("  ."),
+                random.choice(u" ·."),
+                random.choice((
+                    ui.Color('BrightBlack').cp, ui.Color('Brown').cp,
+                    ui.Color('BrightGreen').cp, ui.Color('Green').cp,
+                    )))
         for o in objlist:
             o.place(self)
     def passable_by(self, thing):
@@ -194,7 +196,7 @@ class Stair(Floor):
     oneway = False
     def __init__(self, lvl, y, x, tgt = None, oneway = False, objlist=[]):
         Floor.__init__(self, lvl, y, x, objlist)
-        self.pic = ">", ui.Color('White').cp
+        self.pic = ">", u">", ui.Color('White').cp
         if tgt:
             self.tgt = tgt
         if oneway:
@@ -261,9 +263,15 @@ class Door(Tile):
         return self.opened
     def getpic(self):
         if self.hidden:
-            return "=" if self.opened else "#", ui.Color('Default').cp
+            if self.opened:
+                return "=", u"□", ui.Color('Default').cp
+            else:
+                return "#", u"▒", ui.Color('Default').cp
         else:
-            return "-" if self.opened else "+", ui.Color('Brown').cp
+            if self.opened:
+                return "-", u"-", ui.Color('Brown').cp
+            else:
+                return "+", u"+", ui.Color('Brown').cp
     def open(self):
         if self.opened:
             return False
@@ -312,7 +320,7 @@ class Wall(Tile):
     "An impassable Tile."
     def __init__(self, lvl, y, x):
         Tile.__init__(self, lvl, y, x)
-        self.pic = "#", ui.Color('Default').cp
+        self.pic = "#", u"▒", ui.Color('Default').cp
     def transparent(self):
         return False
     def passable_by(self, thing):
@@ -342,12 +350,14 @@ class Water(Tile):
             #   '     `
             angle = math.atan2(self.flowy, self.flowx)
             idx = int(round(4.0 * angle / math.pi)) % 8
-            self.flowsign = (">\\v/<\\^/")[idx]
+            self.flowascii = (">\\v/<\\^/")[idx]
+            self.flowuni = (u"→↘↓↙←↖↑↗")[idx]
         else:
-            self.flowsign = '~'
+            self.flowascii = '~'
+            self.flowuni = u'~'
 
     def render_bg(self):
-        return (self.flowsign, random.choice((
+        return (self.flowascii, self.flowuni, random.choice((
             ui.Color('Blue').cp, ui.Color('Blue').cp, ui.Color('Blue').cp,
             ui.Color('Cyan').cp, ui.Color('BrightBlue').cp, ui.Color('BrightBlue').cp,
             ui.Color('BrightCyan').cp, ui.Color('White').cp,
index a6bd54b..175a9b9 100755 (executable)
@@ -22,8 +22,10 @@ import curses
 import curses.ascii
 import curses.panel
 import cProfile
+import locale
 
 import thing
+import appui
 import creature
 import ai
 import level
@@ -188,8 +190,11 @@ class AppUI:
                 item = inv[i]
                 win.addnstr(i+1, 2, "%s. %s" % (chr(i+ord('A')), item.name),
                         w-6)
-                (ascii, attr) = item.render()
-                win.addstr(i+1, w-3, ascii, attr)
+                (ascii, uni, attr) = item.render()
+                if appui.hasuni:
+                    win.addwstr(i+1, w-3, uni, attr)
+                else:
+                    win.addstr(i+1, w-3, ascii, attr)
                 if item.wielded:
                     slotabbr = creature.slotabbrevs[item.wielded[1]]
                     win.addstr(i+1, w-2, slotabbr, ui.Color('Cyan').cp)
@@ -368,6 +373,13 @@ class AppUI:
 
 if __name__ == '__main__':
     def main(stdscr):
+        try:
+            stdscr.addwstr(0, 0, u"Ŧəşţıŋǧ ẅídè—¢ħãř śūρṗǫƦť")
+            stdscr.getch()
+            appui.hasuni = True
+        except:
+            appui.hasuni = False
+
         me = creature.Player()
         him = creature.Player()
         it = ai.Monster()
@@ -384,6 +396,7 @@ if __name__ == '__main__':
 
         ui.event_loop()
 
+    locale.setlocale(locale.LC_ALL, "")
     if do_profile:
         cProfile.run('curses.wrapper(main)', 'roguelike.prof')
     else:
index 202efa3..55332f4 100644 (file)
--- a/thing.py
+++ b/thing.py
@@ -117,6 +117,10 @@ class ItemClass (type):
         if hasstr('isa'):
             base = ItemClass(props['isa'])
             del props['isa']
+
+        if hasstr('uni'):
+            props['uni'] = unicode(props['uni'], "utf-8")
+
         return type.__new__(cls, name + " item", (base,), props)
 
     @classmethod 
@@ -185,7 +189,7 @@ class Item(Thing):
     def is_item(self):
         return True
     def render(self, pic=None):
-        return (self.ascii, self.material.attr())
+        return (self.ascii, self.uni, self.material.attr())
     def wieldable(self):
         return self.slot is not None
     def place(self, location):