Package tdl |
[frames] | no frames]

# Source Code for Package tdl

       1  """
       2      The documentation for python-tdl.  A Pythonic port of
       3      U{libtcod<http://doryen.eptalys.net/libtcod/>}.
       4
       5      You can find the project page on Google Code
       6      U{here<http://code.google.com/p/python-tdl/>}.
       7
       8      Getting Started
       9      ===============
      10        Once the library is imported you can load the font you want to use with
      11        L{tdl.setFont}.
      12        This is optional and when skipped will use a decent default font.
      13
      14        After that you call L{tdl.init} to set the size of the window and get the
      15        root console in return.
      16        This console is the canvas to what will appear on the screen.
      17
      18      Indexing Consoles
      19      =================
      20        For most methods taking a position you can use Python-style negative
      21        indexes to refer to the opposite side of a console with (-1, -1)
      22        starting at the bottom right.
      23        You can also check if a point is part of a console using containment
      24        logic i.e. ((x, y) in console).
      25
      26      Drawing
      27      =======
      28        Once you have the root console from L{tdl.init} you can start drawing on
      29        it using a method such as L{Console.drawChar}.
      30        When using this method you can have the char parameter be an integer or a
      31        single character string.
      32        The fgcolor and bgcolor parameters expect a three item list
      33        [red, green, blue] with integers in the 0-255 range with [0, 0, 0] being
      34        black and [255, 255, 255] being white.
      35        Or instead you can use None for any of the three parameters to tell the
      36        library to keep what is at that spot instead of overwriting it.
      37        After the drawing functions are called a call to L{tdl.flush} will update
      38        the screen.
      39  """
      40
      41  import sys
      42  import os
      43  import ctypes
      44  import weakref
      45  import array
      46  import itertools
      47  import textwrap
      48
      49  from . import event, map, noise
      50  from .__tcod import _lib, _Color, _unpackfile
      51
      52  _IS_PYTHON3 = (sys.version_info[0] == 3)
      53
      54  if _IS_PYTHON3: # some type lists to use with isinstance
      55      _INTTYPES = (int,)
      56      _NUMTYPES = (int, float)
      57      _STRTYPES = (str, bytes)
      58  else:
      59      _INTTYPES = (int, long)
      60      _NUMTYPES = (int, long, float)
      61      _STRTYPES = (str,)
      62
      63 -def _encodeString(string): # still used for filepaths, and that's about it
      64      "changes string into bytes if running in python 3, for sending to ctypes"
      65      if _IS_PYTHON3 and isinstance(string, str):
      66          return string.encode()
      67      return string
      68
      69  #def _formatString(string):
      70  #    pass
      71
      72 -def _formatChar(char):
      73      """Prepares a single character for passing to ctypes calls, needs to return
      74      an integer but can also pass None which will keep the current character
      75      instead of overwriting it.
      76
      77      This is called often and needs to be optimized whenever possible.
      78      """
      79      if char is None:
      80          return None
      81      if isinstance(char, _INTTYPES):
      82          return char
      83      if isinstance(char, _STRTYPES) and len(char) == 1:
      84          return ord(char)
      85      raise TypeError('Expected char parameter to be a single character string, number, or None, got: %s' % repr(char))
      86
      87  _fontinitialized = False
      88  _rootinitialized = False
      89  _rootConsoleRef = None
      90  # remove dots from common functions
      91  _setchar = _lib.TCOD_console_set_char
      92  _setfore = _lib.TCOD_console_set_char_foreground
      93  _setback = _lib.TCOD_console_set_char_background
      94  _setcharEX = _lib.TCOD_console_put_char_ex
      95 -def _verify_colors(*colors):
      96      """Used internally.
      97      Raise an assertion error if the parameters can not be converted into colors.
      98      """
      99      for color in colors:
     100          assert _iscolor(color), 'a color must be a 3 item tuple, web format, or None, received %s' % repr(color)
     101      return True
     102
     103 -def _iscolor(color):
     104      """Used internally.
     105      A debug function to see if an object can be used as a TCOD color struct.
     106      None counts as a parameter to keep the current colors instead.
     107
     108      This function is often part of an inner-loop and can slow a program down.
     109      It has been made to work with assert and can be skipped with the -O flag.
     110      Still it's called often and must be optimized.
     111      """
     112      if color is None:
     113          return True
     114      if isinstance(color, (tuple, list, _Color)):
     115          return len(color) == 3
     116      if isinstance(color, _INTTYPES):
     117          return True
     118      return False
     119
     120  ## not using this for now
     121  #class Color(object):
     122  #
     123  #    def __init__(self, r, g, b):
     124  #        self._color = (r, g, b)
     125  #        self._ctype = None
     126  #
     127  #    def _getCType(self):
     128  #        if not self._ctype:
     129  #            self._ctype = _Color(*self._color)
     130  #        return self._ctype
     131  #
     132  #    def __len__(self):
     133  #        return 3
     134
     135 -def _formatColor(color):
     136      """Format the color to ctypes
     137      """
     138      if color is None:
     139          return None
     140      if isinstance(color, _Color):
     141          return color
     142      #if isinstance(color, Color):
     143      #    return color._getCType()
     144      if isinstance(color, _INTTYPES):
     145          # format a web style color with the format 0xRRGGBB
     146          return _Color(color >> 16 & 0xff, color >> 8 & 0xff, color & 0xff)
     147      return _Color(*color)
     148
     149 -class TDLError(Exception):
     150      """
     151      The catch all for most TDL specific errors.
     152      """
     153
     154 -class _MetaConsole(object):
     155      """
     156      Contains methods shared by both the L{Console} and L{Window} classes.
     157      """
     158      __slots__ = ('width', 'height', 'console', '__weakref__', '__dict__')
     159
     160 -    def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     161          """Draws a single character.
     162
     163          @type x: int
     164          @param x: X coordinate to draw at.
     165          @type y: int
     166          @param y: Y coordinate to draw at.
     167
     168          @type char: int, string, or None
     169          @param char: Should be an integer, single character string, or None.
     170
     171                       You can set the char parameter as None if you only want to change
     172                       the colors of the tile.
     173
     174          @type fgcolor: (r, g, b) or None
     175          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     176                          integers ranging 0-255 or None.
     177
     178                          None will keep the current color at this position unchanged.
     179          @type bgcolor: (r, g, b) or None
     180          @param bgcolor: Background color.  See fgcolor
     181
     182          @raise AssertionError: Having x or y values that can't be placed inside
     183                                 of the console will raise an AssertionError.
     184                                 You can use always use ((x, y) in console) to
     185                                 check if a tile is drawable.
     186          """
     187
     188          assert _verify_colors(fgcolor, bgcolor)
     189          x, y = self._normalizePoint(x, y)
     190          x, y = ctypes.c_int(x), ctypes.c_int(y)
     191          self._setChar(x, y, _formatChar(char),
     192                        _formatColor(fgcolor), _formatColor(bgcolor))
     193
     194 -    def drawStr(self, x, y, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     195          """Draws a string starting at x and y.  Optinally colored.
     196
     197          A string that goes past the right side will wrap around.  A string
     198          wraping to below the console will raise a L{TDLError} but will still be
     199          written out.  This means you can safely ignore the errors with a
     200          try... except block if you're fine with partily written strings.
     201
     202          \\r and \\n are drawn on the console as normal character tiles.  No
     203          special encoding is done and any string will translate to the character
     204          table as is.
     205
     206          For a string drawing operation that respects special characters see the
     207          L{Typewriter} class.
     208
     209          @type x: int
     210          @param x: X coordinate to draw at.
     211          @type y: int
     212          @param y: Y coordinate to draw at.
     213
     214          @type string: string or iterable
     215          @param string: Can be a string or an iterable of numbers.
     216
     217                         Special characters are ignored and rendered as any other
     218                         character.
     219
     220          @type fgcolor: (r, g, b) or None
     221          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     222                          integers ranging 0-255 or None.
     223
     224                          None will keep the current color at this position unchanged.
     225          @type bgcolor: (r, g, b) or None
     226          @param bgcolor: Background color.  See fgcolor
     227
     228          @raise AssertionError: Having x or y values that can't be placed inside
     229                                 of the console will raise an AssertionError.
     230
     231                                 You can use always use ((x, y) in console) to
     232                                 check if a tile is drawable.
     233          """
     234
     235          x, y = self._normalizePoint(x, y)
     236          assert _verify_colors(fgcolor, bgcolor)
     237          fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
     238          width, height = self.getSize()
     239          batch = [] # prepare a batch operation
     240          def _drawStrGen(x=x, y=y, string=string, width=width, height=height):
     241              """Generator for drawStr
     242
     243              Iterates over ((x, y), ch) data for _setCharBatch, raising an
     244              error if the end of the console is reached.
     245              """
     246              for char in string:
     247                  if y == height:
     248                      raise TDLError('End of console reached.')
     249                  #batch.append(((x, y), _formatChar(char))) # ((x, y), ch)
     250                  yield((x, y), _formatChar(char))
     251                  x += 1 # advance cursor
     252                  if x == width: # line break
     253                      x = 0
     254                      y += 1
     255          self._setCharBatch(_drawStrGen(), fgcolor, bgcolor)
     256
     257 -    def drawRect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     258          """Draws a rectangle starting from x and y and extending to width and height.
     259
     260          If width or height are None then it will extend to the edge of the console.
     261
     262          @type x: int
     263          @param x: x coordinate to draw at.
     264          @type y: int
     265          @param y: y coordinate to draw at.
     266
     267          @type width: int or None
     268          @param width: Width of the rectangle.
     269
     270                        Can be None to extend to the bottom right of the
     271                        console or can be a negative number to be sized reltive
     272                        to the total size of the console.
     273          @type height: int or None
     274          @param height: Height of the rectangle.  See width.
     275
     276          @type string: int, string, or None
     277          @param string: Should be an integer, single character string, or None.
     278
     279                         You can set the char parameter as None if you only want
     280                         to change the colors of an area.
     281
     282          @type fgcolor: (r, g, b) or None
     283          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     284                          integers ranging 0-255 or None.
     285
     286                          None will keep the current color at this position unchanged.
     287          @type bgcolor: (r, g, b) or None
     288          @param bgcolor: Background color.  See fgcolor
     289
     290          @raise AssertionError: Having x or y values that can't be placed inside
     291                                 of the console will raise an AssertionError.
     292
     293                                 You can use always use ((x, y) in console) to
     294                                 check if a tile is drawable.
     295          """
     296          x, y, width, height = self._normalizeRect(x, y, width, height)
     297          assert _verify_colors(fgcolor, bgcolor)
     298          fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
     299          char = _formatChar(string)
     300          # use itertools to make an x,y grid
     301          # using ctypes here reduces type converstions later
     302          grid = itertools.product((ctypes.c_int(x) for x in range(x, x + width)),
     303                                   (ctypes.c_int(y) for y in range(y, y + height)))
     304          # zip the single character in a batch variable
     305          batch = zip(grid, itertools.repeat(char, width * height))
     306          self._setCharBatch(batch, fgcolor, bgcolor, nullChar=(char is None))
     307
     308 -    def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     309          """Similar to L{drawRect} but only draws the outline of the rectangle.
     310
     311          @type x: int
     312          @param x: x coordinate to draw at.
     313          @type y: int
     314          @param y: y coordinate to draw at.
     315
     316          @type width: int or None
     317          @param width: Width of the rectangle.
     318
     319                        Can be None to extend to the bottom right of the
     320                        console or can be a negative number to be sized reltive
     321                        to the total size of the console.
     322          @type height: int or None
     323          @param height: Height of the rectangle.  See width.
     324
     325          @type string: int, string, or None
     326          @param string: Should be an integer, single character string, or None.
     327
     328                         You can set the char parameter as None if you only want
     329                         to change the colors of an area.
     330
     331          @type fgcolor: (r, g, b) or None
     332          @param fgcolor: For fgcolor and bgcolor you use a 3 item list with
     333                          integers ranging 0-255 or None.
     334
     335                          None will keep the current color at this position unchanged.
     336          @type bgcolor: (r, g, b) or None
     337          @param bgcolor: Background color.  See fgcolor
     338
     339          @raise AssertionError: Having x or y values that can't be placed inside
     340                                 of the console will raise an AssertionError.
     341
     342                                 You can use always use ((x, y) in console) to
     343                                 check if a tile is drawable.
     344          """
     345          x, y, width, height = self._normalizeRect(x, y, width, height)
     346          assert _verify_colors(fgcolor, bgcolor)
     347          fgcolor, bgcolor = _formatColor(fgcolor), _formatColor(bgcolor)
     348          char = _formatChar(string)
     349          if width == 1 or height == 1: # it's just a single width line here
     350              return self.drawRect(x, y, width, height, char, fgcolor, bgcolor)
     351
     352          # draw sides of frame with drawRect
     353          self.drawRect(x, y, 1, height, char, fgcolor, bgcolor)
     354          self.drawRect(x, y, width, 1, char, fgcolor, bgcolor)
     355          self.drawRect(x + width - 1, y, 1, height, char, fgcolor, bgcolor)
     356          self.drawRect(x, y + height - 1, width, 1, char, fgcolor, bgcolor)
     357
     358 -    def _normalizePoint(self, x, y):
     359          """Check if a point is in bounds and make minor adjustments.
     360
     361          Respects Pythons negative indexes.  -1 starts at the bottom right.
     362          Replaces the _drawable function
     363          """
     364          assert isinstance(x, _INTTYPES), 'x must be an integer, got %s' % repr(x)
     365          assert isinstance(y, _INTTYPES), 'y must be an integer, got %s' % repr(y)
     366
     367          assert (-self.width <= x < self.width) and (-self.height <= y < self.height), \
     368                  ('(%i, %i) is an invalid postition on %s' % (x, y, self))
     369
     370          # handle negative indexes
     371          if x < 0:
     372              x += self.width
     373          if y < 0:
     374              y += self.height
     375          return (x, y)
     376
     377 -    def _normalizeRect(self, x, y, width, height):
     378          """Check if the rectangle is in bounds and make minor adjustments.
     379          raise AssertionError's for any problems
     380          """
     381          x, y = self._normalizePoint(x, y) # inherit _normalizePoint logic
     382
     383          assert width is None or isinstance(width, _INTTYPES), 'width must be an integer or None, got %s' % repr(width)
     384          assert height is None or isinstance(height, _INTTYPES), 'height must be an integer or None, got %s' % repr(height)
     385
     386          # if width or height are None then extend them to the edge
     387          if width is None:
     388              width = self.width - x
     389          elif width < 0: # handle negative numbers
     390              width += self.width
     391              width = max(0, width) # a 'too big' negative is clamped zero
     392          if height is None:
     393              height = self.height - y
     394              height = max(0, height)
     395          elif height < 0:
     396              height += self.height
     397
     398          # reduce rect size to bounds
     399          width = min(width, self.width - x)
     400          height = min(height, self.height - y)
     401
     402          return x, y, width, height
     403
     404 -    def blit(self, source, x=0, y=0, width=None, height=None, srcX=0, srcY=0):
     405          """Blit another console or Window onto the current console.
     406
     407          By default it blits the entire source to the topleft corner.
     408
     409          @type source: L{Console} or L{Window}
     410          @param source: Source window can be a L{Console} or L{Window} instance.
     411                         It can even blit to itself without any problems.
     412
     413          @type x: int
     414          @param x: X coordinate to blit to.
     415          @type y: int
     416          @param y: Y coordinate to blit to.
     417
     418          @type width: int or None
     419          @param width: Width of the rectangle.
     420
     421                        Can be None to extend as far as possible to the
     422                        bottom right corner of the blit area or can be a negative
     423                        number to be sized reltive to the total size of the
     424                        B{destination} console.
     425          @type height: int or None
     426          @param height: Height of the rectangle.  See width.
     427
     428          @type srcX: int
     429          @param srcX: The source consoles x coordinate to blit from.
     430          @type srcY: int
     431          @param srcY: The source consoles y coordinate to blit from.
     432          """
     433          # hardcode alpha settings for now
     434          fgalpha=1.0
     435          bgalpha=1.0
     436
     437          assert isinstance(source, (Console, Window)), "source muse be a Window or Console instance"
     438
     439          # handle negative indexes and rects
     440          # negative width and height will be set realtive to the destination
     441          # and will also be clamped to the smallest Console
     442          x, y, width, height = self._normalizeRect(x, y, width, height)
     443          srcX, srcY, width, height = source._normalizeRect(srcX, srcY, width, height)
     444
     445          # translate source and self if any of them are Window instances
     446          srcX, srcY = source._translate(srcX, srcY)
     447          source = source.console
     448          x, y = self._translate(x, y)
     449          self = self.console
     450
     451          if self == source:
     452              # if we are the same console then we need a third console to hold
     453              # onto the data, otherwise it tries to copy into itself and
     454              # starts destroying everything
     455              tmp = Console(width, height)
     456              _lib.TCOD_console_blit(source, srcX, srcY, width, height, tmp, 0, 0, fgalpha, bgalpha)
     457              _lib.TCOD_console_blit(tmp, 0, 0, width, height, self, x, y, fgalpha, bgalpha)
     458          else:
     459              _lib.TCOD_console_blit(source, srcX, srcY, width, height, self, x, y, fgalpha, bgalpha)
     460
     461 -    def getSize(self):
     462          """Return the size of the console as (width, height)
     463
     464          @rtype: (int, int)
     465          """
     466          return self.width, self.height
     467
     468 -    def scroll(self, x, y):
     469          """Scroll the contents of the console in the direction of x,y.
     470
     471          Uncovered areas will be cleared.
     472          @type x: int
     473          @param x: Distance to scroll along x-axis
     474          @type y: int
     475          @param y: Distance to scroll along y-axis
     476          """
     477          assert isinstance(x, _INTTYPES), "x must be an integer, got %s" % repr(x)
     478          assert isinstance(y, _INTTYPES), "y must be an integer, got %s" % repr(x)
     479          def getSlide(x, length):
     480              """get the parameters needed to scroll the console in the given
     481              direction with x
     482              returns (x, length, srcx)
     483              """
     484              if x > 0:
     485                  srcx = 0
     486                  length -= x
     487              elif x < 0:
     488                  srcx = abs(x)
     489                  x = 0
     490                  length -= srcx
     491              else:
     492                  srcx = 0
     493              return x, length, srcx
     494          def getCover(x, length):
     495              """return the (x, width) ranges of what is covered and uncovered"""
     496              cover = (0, length) # everything covered
     497              uncover = None  # nothing uncovered
     498              if x > 0: # left side uncovered
     499                  cover = (x, length - x)
     500                  uncover = (0, x)
     501              elif x < 0: # right side uncovered
     502                  x = abs(x)
     503                  cover = (0, length - x)
     504                  uncover = (length - x, x)
     505              return cover, uncover
     506
     507          width, height = self.getSize()
     508          if abs(x) >= width or abs(y) >= height:
     509              return self.clear() # just clear the console normally
     510
     511          # get the ranges of the areas that will be uncovered
     512          coverX, uncoverX = getCover(x, width)
     513          coverY, uncoverY = getCover(y, height)
     514          # so at this point we know that coverX and coverY makes a rect that
     515          # encases the area that we end up blitting to.  uncoverX/Y makes a
     516          # rect in the corner of the uncovered area.  So we need to combine
     517          # the uncoverX/Y with coverY/X to make what's left of the uncovered
     518          # area.  Explaining it makes it mush easier to do now.
     519
     520          # But first we need to blit.
     521          x, width, srcx = getSlide(x, width)
     522          y, height, srcy = getSlide(y, height)
     523          self.blit(self, x, y, width, height, srcx, srcy)
     524
     525          if uncoverX: # clear sides (0x20 is space)
     526              self.drawRect(uncoverX[0], coverY[0], uncoverX[1], coverY[1], 0x20)
     527          if uncoverY: # clear top/bottom
     528              self.drawRect(coverX[0], uncoverY[0], coverX[1], uncoverY[1], 0x20)
     529          if uncoverX and uncoverY: # clear corner
     530              self.drawRect(uncoverX[0], uncoverY[0], uncoverX[1], uncoverY[1], 0x20)
     531
     532 -    def getChar(self, x, y):
     533          """Return the character and colors of a tile as (ch, fg, bg)
     534
     535          This method runs very slowly as is not recommended to be called
     536          frequently.
     537
     538          @rtype: (int, (r, g, b), (r, g, b))
     539          @returns: Returns a 3-item tuple.  The first item is an integer of the
     540                    character at the position (x, y) the second and third are the
     541                    foreground and background colors respectfully.
     542          """
     543          raise NotImplementedError('Method here only exists for the docstring')
     544
     545 -    def __contains__(self, position):
     546          """Use ((x, y) in console) to check if a position is drawable on this console.
     547          """
     548          x, y = position
     549          return (0 <= x < self.width) and (0 <= y < self.height)
     550
     551 -class Console(_MetaConsole):
     552      """Contains character and color data and can be drawn to.
     553
     554      The console created by the L{tdl.init} function is the root console and is the
     555      console that is rendered to the screen with L{flush}.
     556
     557      Any console created from the Console class is an off-screen console that
     558      can be drawn on before being L{blit} to the root console.
     559      """
     560
     561      __slots__ = ('_as_parameter_', '_typewriter')
     562
     563 -    def __init__(self, width, height):
     564          """Create a new offscreen console.
     565
     566          @type width: int
     567          @param width: Width of the console in tiles
     568          @type height: int
     569          @param height: Height of the console in tiles
     570          """
     571          if not _rootinitialized:
     572              raise TDLError('Can not create Console\'s before tdl.init')
     573          self._as_parameter_ = _lib.TCOD_console_new(width, height)
     574          self.console = self
     575          self.width = width
     576          self.height = height
     577          self._typewriter = None # "typewriter lock", makes sure the colors are set to the typewriter
     578
     579      @classmethod
     580 -    def _newConsole(cls, console):
     581          """Make a Console instance, from a console ctype"""
     582          self = cls.__new__(cls)
     583          self._as_parameter_ = console
     584          self.console = self
     585          self.width = _lib.TCOD_console_get_width(self)
     586          self.height = _lib.TCOD_console_get_height(self)
     587          self._typewriter = None
     588          return self
     589
     590 -    def __del__(self):
     591          """
     592          If the main console is garbage collected then the window will be closed as well
     593          """
     594          # If this is the root console the window will close when collected
     595          try:
     596              if isinstance(self._as_parameter_, ctypes.c_void_p):
     597                  global _rootinitialized, _rootConsoleRef
     598                  _rootinitialized = False
     599                  _rootConsoleRef = None
     600              _lib.TCOD_console_delete(self)
     601          except StandardError:
     602              pass # I forget why I put this here but I'm to afraid to delete it
     603
     604 -    def _replace(self, console):
     605          """Used internally
     606
     607          Mostly used just to replace this Console object with the root console
     608          If another Console object is used then they are swapped
     609          """
     610          if isinstance(console, Console):
     611              self._as_parameter_, console._as_parameter_ = \
     612                console._as_parameter_, self._as_parameter_ # swap tcod consoles
     613          else:
     614              self._as_parameter_ = console
     615          self.width = _lib.TCOD_console_get_width(self)
     616          self.height = _lib.TCOD_console_get_height(self)
     617          return self
     618
     619 -    def _translate(self, x, y):
     620          """Convertion x and y to their position on the root Console for this Window
     621
     622          Because this is a Console instead of a Window we return the paramaters
     623          untouched"""
     624          return x, y
     625
     626 -    def clear(self, fgcolor=(0, 0, 0), bgcolor=(0, 0, 0)):
     627          """Clears the entire Console.
     628
     629          @type fgcolor: (r, g, b)
     630          @param fgcolor: Foreground color.
     631
     632                          Must be a 3-item list with integers that range 0-255.
     633
     634                          Unlike most other operations you can not use None here.
     635          @type bgcolor: (r, g, b)
     636          @param bgcolor: Background color.  See fgcolor.
     637          """
     638          assert _verify_colors(fgcolor, bgcolor)
     639          assert fgcolor and bgcolor, 'Can not use None with clear'
     640          self._typewriter = None
     641          _lib.TCOD_console_set_default_background(self, _formatColor(bgcolor))
     642          _lib.TCOD_console_set_default_foreground(self, _formatColor(fgcolor))
     643          _lib.TCOD_console_clear(self)
     644
     645 -    def _setChar(self, x, y, char, fgcolor=None, bgcolor=None, bgblend=1):
     646          """
     647          Sets a character.
     648          This is called often and is designed to be as fast as possible.
     649
     650          Because of the need for speed this function will do NO TYPE CHECKING
     651          AT ALL, it's up to the drawing functions to use the functions:
     652          _formatChar and _formatColor before passing to this."""
     653          # buffer values as ctypes objects
     654          console = self._as_parameter_
     655
     656          if char is not None and fgcolor is not None and bgcolor is not None:
     657              _setcharEX(console, x, y, char, fgcolor, bgcolor)
     658              return
     659          if char is not None:
     660              _setchar(console, x, y, char)
     661          if fgcolor is not None:
     662              _setfore(console, x, y, fgcolor)
     663          if bgcolor is not None:
     664              _setback(console, x, y, bgcolor, bgblend)
     665
     666 -    def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1, nullChar=False):
     667          """
     668          Try to perform a batch operation otherwise fall back to _setChar.
     669          If fgcolor and bgcolor are defined then this is faster but not by very
     670          much.
     671
     672          batch is a iterable of [(x, y), ch] items
     673          """
     674          if fgcolor and not nullChar:
     675              # buffer values as ctypes objects
     676              self._typewriter = None # clear the typewriter as colors will be set
     677              console = self._as_parameter_
     678              if not bgcolor:
     679                  bgblend = 0
     680              bgblend = ctypes.c_int(bgblend)
     681
     682              _lib.TCOD_console_set_default_background(console, bgcolor)
     683              _lib.TCOD_console_set_default_foreground(console, fgcolor)
     684              _putChar = _lib.TCOD_console_put_char # remove dots and make local
     685              for (x, y), char in batch:
     686                  _putChar(console, x, y, char, bgblend)
     687          else:
     688              for (x, y), char in batch:
     689                  self._setChar(x, y, char, fgcolor, bgcolor, bgblend)
     690
     691 -    def getChar(self, x, y):
     692          # inherit docstring
     693          x, y = self._normalizePoint(x, y)
     694          char = _lib.TCOD_console_get_char(self, x, y)
     695          bgcolor = _lib.TCOD_console_get_char_background_wrapper(self, x, y)
     696          fgcolor = _lib.TCOD_console_get_char_foreground_wrapper(self, x, y)
     697          return char, tuple(fgcolor), tuple(bgcolor)
     698
     699 -    def __repr__(self):
     700          return "<Console (Width=%i Height=%i)>" % (self.width, self.height)
     701
     702
     703 -class Window(_MetaConsole):
     704      """A Window contains a small isolated part of a Console.
     705
     706      Drawing on the Window draws on the Console.
     707
     708      Making a Window and setting its width or height to None will extend it to
     709      the edge of the console.
     710      """
     711
     712      __slots__ = ('parent', 'x', 'y')
     713
     714 -    def __init__(self, console, x, y, width, height):
     715          """Isolate part of a L{Console} or L{Window} instance.
     716
     717          @type console: L{Console} or L{Window}
     718          @param console: The parent object which can be a L{Console} or another
     719                          L{Window} instance.
     720
     721          @type x: int
     722          @param x: X coordinate to place the Window.
     723
     724                    This follows the normal rules for indexing so you can use a
     725                    negative integer to place the Window relative to the bottom
     726                    right of the parent Console instance.
     727          @type y: int
     728          @param y: Y coordinate to place the Window.
     729
     730                    See x.
     731
     732          @type width: int or None
     733          @param width: Width of the Window.
     734
     735                        Can be None to extend as far as possible to the
     736                        bottom right corner of the parent Console or can be a
     737                        negative number to be sized reltive to the Consoles total
     738                        size.
     739          @type height: int or None
     740          @param height: Height of the Window.
     741
     742                         See width.
     743          """
     744          assert isinstance(console, (Console, Window)), 'console parameter must be a Console or Window instance, got %s' % repr(console)
     745          self.parent = console
     746          self.x, self.y, self.width, self.height = console._normalizeRect(x, y, width, height)
     747          if isinstance(console, Console):
     748              self.console = console
     749          else:
     750              self.console = self.parent.console
     751
     752 -    def _translate(self, x, y):
     753          """Convertion x and y to their position on the root Console"""
     754          # we add our position relative to our parent and then call then next parent up
     755          return self.parent._translate((x + self.x), (y + self.y))
     756
     757 -    def clear(self, fgcolor=(0, 0, 0), bgcolor=(0, 0, 0)):
     758          """Clears the entire Window.
     759
     760          @type fgcolor: (r, g, b)
     761          @param fgcolor: Foreground color.
     762
     763                          Must be a 3-item list with integers that range 0-255.
     764
     765                          Unlike most other operations you can not use None here.
     766          @type bgcolor: (r, g, b)
     767          @param bgcolor: Background color.  See fgcolor.
     768          """
     769          assert _verify_colors(fgcolor, bgcolor)
     770          assert fgcolor and bgcolor, 'Can not use None with clear'
     771          self.draw_rect(0, 0, None, None, 0x20, fgcolor, bgcolor)
     772
     773 -    def _setChar(self, x, y, char=None, fgcolor=None, bgcolor=None, bgblend=1):
     774          self.parent._setChar((x + self.x), (y + self.y), char, fgcolor, bgcolor, bgblend)
     775
     776 -    def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1):
     777          myX = self.x # remove dots for speed up
     778          myY = self.y
     779          self.parent._setCharBatch((((x + myX, y + myY), ch) for ((x, y), ch) in batch),
     780                                    fgcolor, bgcolor, bgblend)
     781
     782
     783 -    def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     784          # inherit docstring
     785          x, y = self._normalizePoint(x, y)
     786          self.parent.drawChar(x + self.x, y + self.y, char, fgcolor, bgcolor)
     787
     788 -    def drawRect(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     789          # inherit docstring
     790          x, y, width, height = self._normalizeRect(x, y, width, height)
     791          self.parent.drawRect(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
     792
     793 -    def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
     794          # inherit docstring
     795          x, y, width, height = self._normalizeRect(x, y, width, height)
     796          self.parent.drawFrame(x + self.x, y + self.y, width, height, string, fgcolor, bgcolor)
     797
     798 -    def getChar(self, x, y):
     799          # inherit docstring
     800          x, y = self._normalizePoint(x, y)
     801          return self.console.getChar(self._translate(x, y))
     802
     803 -    def __repr__(self):
     804          return "<Window(X=%i Y=%i Width=%i Height=%i)>" % (self.x, self.y,
     805                                                            self.width,
     806                                                            self.height)
     807
     808
     809 -class Typewriter(object):
     810      """Converts a console into a scrolling text log that respects special
     811      characters.
     812
     813      This class works best on a L{Window} or off-screen L{Console} instance.
     814      In a L{Window} for example the scrolling text is limited to the L{Window}'s
     815      isolated area.
     816      """
     817
     818 -    def __init__(self, console):
     819          """Add a virtual cursor to a L{Console} or L{Window} instance.
     820
     821          @type console: L{Console} or L{Window}
     822          """
     823          assert isinstance(console, (Console, Window)), 'console parameter must be a Console or Window instance, got %s' % repr(console)
     824          self.parent = console
     825          if isinstance(self.parent, Console):
     826              self.console = self.parent
     827          else:
     828              self.console = self.parent.console
     829          self.cursor = (0, 0) # cursor position
     830          self.scrollMode = 'scroll' #can be 'scroll', 'error'
     831          self.fgcolor = _formatColor((255, 255, 255))
     832          self.bgcolor = _formatColor((0, 0, 0))
     833          self._bgblend = 1 # SET
     834
     835 -    def _normalize(self, x, y):
     836          """return the normalized the cursor position."""
     837          width, height = self.parent.getSize()
     838          while x >= width:
     839              x -= width
     840              y += 1
     841          while y >= height:
     842              if self.scrollMode == 'scroll':
     843                  y -= 1
     844                  self.parent.scroll(0, -1)
     845              elif self.scrollMode == 'error':
     846                  # reset the cursor on error
     847                  self.cursor = (0, 0)
     848                  raise TDLError('Typewriter cursor has reached the end of the console')
     849          return (x, y)
     850
     851 -    def getCursor(self):
     852          """Return the virtual cursor position.
     853
     854          @rtype: (int, int)
     855          @return: Returns (x, y) a 2-integer tuple containing where the next
     856                   L{addChar} or L{addStr} will start at.
     857
     858                   This can be changed with the L{move} method."""
     859          x, y = self.cursor
     860          width, height = self.parent.getSize()
     861          while x >= width:
     862              x -= width
     863              y += 1
     864          if y >= height and self.scrollMode == 'scroll':
     865              y = height - 1
     866          return x, y
     867
     868 -    def move(self, x, y):
     869          """Move the virtual cursor.
     870
     871          @type x: int
     872          @param x: X position to place the cursor.
     873          @type y: int
     874          @param y: Y position to place the cursor.
     875          """
     876          self.cursor = self.parent._normalizePoint(x, y)
     877
     878 -    def setFG(self, color):
     879          """Change the foreground color"""
     880          assert _iscolor(color)
     881          assert color is not None
     882          self.fgcolor = _formatColor(color)
     883          if self.console._typewriter is self:
     884              _lib.TCOD_console_set_default_foreground(self.console, self.fgcolor)
     885
     886 -    def setBG(self, color):
     887          """Change the background color"""
     888          assert _iscolor(color)
     889          assert color is not None
     890          self.bgcolor = _formatColor(color)
     891          if self.console._typewriter is self:
     892              _lib.TCOD_console_set_default_background(self.console, self.bgcolor)
     893
     894 -    def _updateConsole(self):
     895          """Make sure the colors on a console match the Typewriter instance"""
     896          if self.console._typewriter is not self:
     897              self.console._typewriter = self
     898
     899              _lib.TCOD_console_set_default_background(self.console, self.bgcolor)
     900              _lib.TCOD_console_set_default_foreground(self.console, self.fgcolor)
     901
     902
     903 -    def addChar(self, char):
     904          """Draw a single character at the cursor."""
     905          if char == '\n': # line break
     906              x = 0
     907              y += 1
     908              return
     909          if char == '\r': # return
     910              x = 0
     911              return
     912          x, y = self._normalize(*self.cursor)
     913          self.cursor = [x + 1, y] # advance cursor on next draw
     914          self._updateConsole()
     915          x, y = self.parent._translate(x, y)
     916          _lib.TCOD_console_put_char(self.console._as_parameter_, x, y, _formatChar(char), self._bgblend)
     917
     918
     919 -    def addStr(self, string):
     920          """Write a string at the cursor.  Handles special characters such as newlines.
     921
     922          @type string: string
     923          @param string:
     924          """
     925          x, y = self.cursor
     926          for char in string:
     927              if char == '\n': # line break
     928                  x = 0
     929                  y += 1
     930                  continue
     931              if char == '\r': # return
     932                  x = 0
     933                  continue
     934              x, y = self._normalize(x, y)
     935              self.parent.drawChar(x, y, char, self.fgcolor, self.bgcolor)
     936              x += 1
     937          self.cursor = (x, y)
     938
     939 -    def write(self, string):
     940          """This method mimics basic file-like behaviour.
     941
     942          Because of this method you can replace sys.stdout or sys.stderr with
     943          a L{Typewriter} instance.
     944
     945          @type string: string
     946          """
     947          # some 'basic' line buffer stuff.
     948          # there must be an easier way to do this.  The textwrap module didn't
     949          # help much.
     950          x, y = self._normalize(*self.cursor)
     951          width, height = self.parent.getSize()
     952          wrapper = textwrap.TextWrapper(initial_indent=(' '*x), width=width)
     953          writeLines = []
     954          for line in string.split('\n'):
     955              if line:
     956                  writeLines += wrapper.wrap(line)
     957                  wrapper.initial_indent = ''
     958              else:
     959                  writeLines.append([])
     960
     961          for line in writeLines:
     962              x, y = self._normalize(x, y)
     963              self.parent.drawStr(x, y, line[x:], self.fgcolor, self.bgcolor)
     964              y += 1
     965              x = 0
     966          y -= 1
     967          self.cursor = (x, y)
     968
     969
     970 -def init(width, height, title=None, fullscreen=False, renderer='OPENGL'):
     971      """Start the main console with the given width and height and return the
     972      root console.
     973
     974      Call the consoles drawing functions.  Then remember to use L{tdl.flush} to
     975      make what's drawn visible on the console.
     976
     977      @type width: int
     978      @param width: width of the root console (in tiles)
     979
     980      @type height: int
     981      @param height: height of the root console (in tiles)
     982
     983      @type title: string
     984      @param title: Text to display as the window title.
     985
     986                    If left None it defaults to the running scripts filename.
     987
     988      @type fullscreen: boolean
     989      @param fullscreen: Can be set to True to start in fullscreen mode.
     990
     991      @type renderer: string
     992      @param renderer: Can be one of 'GLSL', 'OPENGL', or 'SDL'.
     993
     994                       Due to way Python works you're unlikely to see much of an
     995                       improvement by using 'GLSL' or 'OPENGL' as most of the
     996                       time Python is slow interacting with the console and the
     997                       rendering itself is pretty fast even on 'SDL'.
     998
     999                       This should be left at default or switched to 'SDL' for
    1000                       better reliability and an instantaneous start up time.
    1001
    1002      @rtype: L{Console}
    1003      @return: The root console.  Only what is drawn on the root console is
    1004               what's visible after a call to L{tdl.flush}.
    1005               After the root console is garbage collected, the window made by
    1006               this function will close.
    1007      """
    1008      RENDERERS = {'GLSL': 0, 'OPENGL': 1, 'SDL': 2}
    1009      global _rootinitialized, _rootConsoleRef
    1010      if not _fontinitialized: # set the default font to the one that comes with tdl
    1011          setFont(_unpackfile('terminal.png'), 16, 16, colomn=True)
    1012
    1013      if renderer.upper() not in RENDERERS:
    1014          raise TDLError('No such render type "%s", expected one of "%s"' % (renderer, '", "'.join(RENDERERS)))
    1015      renderer = RENDERERS[renderer.upper()]
    1016
    1017      # If a console already exists then make a clone to replace it
    1018      if _rootConsoleRef and _rootConsoleRef():
    1019          oldroot = _rootConsoleRef()
    1020          rootreplacement = Console(oldroot.width, oldroot.height)
    1021          rootreplacement.blit(oldroot)
    1022          oldroot._replace(rootreplacement)
    1023          del rootreplacement
    1024
    1025      if title is None: # use a default title
    1026          if sys.argv:
    1027              # Use the script filename as the title.
    1028              title = os.path.basename(sys.argv[0])
    1029          else:
    1030              title = 'python-tdl'
    1031
    1032      _lib.TCOD_console_init_root(width, height, _encodeString(title), fullscreen, renderer)
    1033
    1034      #event.get() # flush the libtcod event queue to fix some issues
    1035      # issues may be fixed already
    1036
    1037      event._eventsflushed = False
    1038      _rootinitialized = True
    1039      rootconsole = Console._newConsole(ctypes.c_void_p())
    1040      _rootConsoleRef = weakref.ref(rootconsole)
    1041
    1042      return rootconsole
    1043
    1044 -def flush():
    1045      """Make all changes visible and update the screen.
    1046
    1047      Remember to call this function after drawing operations.
    1048      Calls to flush will enfore the frame rate limit set by L{tdl.setFPS}.
    1049
    1050      This function can only be called after L{tdl.init}
    1051      """
    1052      if not _rootinitialized:
    1053          raise TDLError('Cannot flush without first initializing with tdl.init')
    1054
    1055      _lib.TCOD_console_flush()
    1056
    1057 -def setFont(path, tileWidth, tileHeight, colomn=False,
    1058              greyscale=False, altLayout=False):
    1059      """Changes the font to be used for this session.
    1060      This should be called before L{tdl.init}
    1061
    1062      While it's possible you can change the font mid program it can sometimes
    1063      break in rare circumstances.  So use caution when doing this.
    1064
    1065      @type path: string
    1066      @param path: Must be a string filepath where a bmp or png file is found.
    1067
    1068      @type tileWidth: int
    1069      @param tileWidth: The width of an individual tile.
    1070
    1071      @type tileHeight: int
    1072      @param tileHeight: The height of an individual tile.
    1073
    1074      @type colomn: boolean
    1075      @param colomn: Defines if the characer order goes along the rows or
    1076                     colomns.
    1077                     It should be True if the charater codes 0-15 are in the
    1078                     first column.  And should be False if the characters 0-15
    1079                     are in the first row.
    1080
    1081      @type greyscale: boolean
    1082      @param greyscale: Creates an anti-aliased font from a greyscale bitmap.
    1083                        Otherwise it uses the alpha channel for anti-aliasing.
    1084
    1085      @type altLayout: boolean
    1086      @param altLayout: An alternative layout with space in the upper left
    1087                        corner.  The colomn parameter is ignored if this is
    1088                        True, find examples of this layout in the font/
    1089                        directory included with the python-tdl source.
    1090
    1091      @raise TDLError: Will be raised if no file is found at path.
    1092
    1093      @note: A png file that's been optimized can fail to load correctly on
    1094             MAC OS X creating a garbled mess when rendering.
    1095             Don't use a program like optipng or just use bmp files instead if
    1096             you want your program to work on macs.
    1097      """
    1098      # put up some constants that are only used here
    1099      FONT_LAYOUT_ASCII_INCOL = 1
    1100      FONT_LAYOUT_ASCII_INROW = 2
    1101      FONT_TYPE_GREYSCALE = 4
    1102      FONT_LAYOUT_TCOD = 8
    1103      global _fontinitialized
    1104      _fontinitialized = True
    1105      flags = 0
    1106      if altLayout:
    1107          flags |= FONT_LAYOUT_TCOD
    1108      elif colomn:
    1109          flags |= FONT_LAYOUT_ASCII_INCOL
    1110      else:
    1111          flags |= FONT_LAYOUT_ASCII_INROW
    1112      if greyscale:
    1113          flags |= FONT_TYPE_GREYSCALE
    1114      if not os.path.exists(path):
    1115          raise TDLError('no file exists at: "%s"' % path)
    1116      _lib.TCOD_console_set_custom_font(_encodeString(path), flags, tileWidth, tileHeight)
    1117
    1118 -def getFullscreen():
    1119      """Returns True if program is fullscreen.
    1120
    1121      @rtype: boolean
    1122      @return: Returns True if the window is in fullscreen mode.
    1123               Otherwise returns False.
    1124      """
    1125      if not _rootinitialized:
    1126          raise TDLError('Initialize first with tdl.init')
    1127      return _lib.TCOD_console_is_fullscreen()
    1128
    1129 -def setFullscreen(fullscreen):
    1130      """Changes the fullscreen state.
    1131
    1132      @type fullscreen: boolean
    1133      """
    1134      if not _rootinitialized:
    1135          raise TDLError('Initialize first with tdl.init')
    1136      _lib.TCOD_console_set_fullscreen(fullscreen)
    1137
    1138 -def setTitle(title):
    1139      """Change the window title.
    1140
    1141      @type title: string
    1142      """
    1143      if not _rootinitialized:
    1144          raise TDLError('Not initilized.  Set title with tdl.init')
    1145      _lib.TCOD_console_set_window_title(_encodeString(title))
    1146
    1147 -def screenshot(path=None):
    1148      """Capture the screen and save it as a png file
    1149
    1150      @type path: string
    1151      @param path: The filepath to save the screenshot.
    1152
    1153                   If path is None then the image will be placed in the current
    1154                   folder with the names:
    1155                   screenshot001.png, screenshot002.png, ...
    1156      """
    1157      if not _rootinitialized:
    1158          raise TDLError('Initialize first with tdl.init')
    1159      if isinstance(fileobj, str):
    1160          _lib.TCOD_sys_save_screenshot(_encodeString(fileobj))
    1161      elif isinstance(fileobj, file): # save to temp file and copy to file-like obj
    1162          tmpname = os.tempnam()
    1163          _lib.TCOD_sys_save_screenshot(_encodeString(tmpname))
    1164          with tmpname as tmpfile:
    1165              fileobj.write(tmpfile.read())
    1166          os.remove(tmpname)
    1167      elif fileobj is None: # save to screenshot001.png, screenshot002.png, ...
    1168          filelist = os.listdir('.')
    1169          n = 1
    1170          filename = 'screenshot%.3i.png' % n
    1171          while filename in filelist:
    1172              n += 1
    1173              filename = 'screenshot%.4i.png' % n
    1174          _lib.TCOD_sys_save_screenshot(_encodeString(filename))
    1175      else:
    1176          raise TypeError('fileobj is an invalid type: %s' % type(fileobj))
    1177
    1178 -def setFPS(frameRate):
    1179      """Set the maximum frame rate.
    1180
    1181      @type frameRate: int
    1182      @param frameRate: Further calls to L{tdl.flush} will limit the speed of
    1183                        the program to run at <frameRate> frames per second. Can
    1184                        also be set to 0 to run without a limit.
    1185
    1186                        Defaults to None.
    1187      """
    1188      if frameRate is None:
    1189          frameRate = 0
    1190      assert isinstance(frameRate, _INTTYPES), 'frameRate must be an integer or None, got: %s' % repr(frameRate)
    1191      _lib.TCOD_sys_set_fps(frameRate)
    1192
    1193 -def getFPS():
    1194      """Return the current frames per second of the running program set by
    1195      L{setFPS}
    1196
    1197      @rtype: int
    1198      @return: Returns the frameRate set by setFPS.
    1199               If set to no limit, this will return 0.
    1200      """
    1201      return _lib.TCOD_sys_get_fps()
    1202
    1203 -def forceResolution(width, height):
    1204      """Change the fullscreen resoulution
    1205
    1206      @type width: int
    1207      @type height: int
    1208      """
    1209      _lib.TCOD_sys_force_fullscreen_resolution(width, height)
    1210
    1211  __all__ = [_var for _var in locals().keys() if _var[0] != '_' and _var not in ['sys', 'os', 'ctypes', 'array', 'weakref', 'itertools', 'textwrap']]
    1212  __all__ += ['_MetaConsole'] # keep this object public to show the documentation in epydoc
    1213