3636
3737_IS_PYTHON3 = (sys .version_info [0 ] == 3 )
3838
39+ if _IS_PYTHON3 : # some type lists to use with isinstance
40+ _INTTYPES = (int ,)
41+ _NUMTYPES = (int , float )
42+ _STRTYPES = (str , bytes )
43+ else :
44+ _INTTYPES = (int , long )
45+ _NUMTYPES = (int , long , float )
46+ _STRTYPES = (str ,)
47+
3948def _encodeString (string ): # still used for filepaths, and that's about it
4049 "changes string into bytes if running in python 3, for sending to ctypes"
4150 if _IS_PYTHON3 and isinstance (string , str ):
@@ -54,9 +63,9 @@ def _formatChar(char):
5463 """
5564 if char is None :
5665 return None
57- if isinstance (char , int ) or not _IS_PYTHON3 and isinstance ( char , long ):
66+ if isinstance (char , _INTTYPES ):
5867 return char
59- if isinstance (char , ( str , bytes ) ) and len (char ) == 1 :
68+ if isinstance (char , _STRTYPES ) and len (char ) == 1 :
6069 return ord (char )
6170 raise TypeError ('Expected char parameter to be a single character string, number, or None, got: %s' % repr (char ))
6271
@@ -89,7 +98,7 @@ def _iscolor(color):
8998 return True
9099 if isinstance (color , (tuple , list , _Color )):
91100 return len (color ) == 3
92- if isinstance (color , int ) or not _IS_PYTHON3 and isinstance ( color , long ):
101+ if isinstance (color , _INTTYPES ):
93102 return True
94103 return False
95104
@@ -100,7 +109,7 @@ def _formatColor(color):
100109 return None
101110 if isinstance (color , _Color ):
102111 return color
103- if isinstance (color , int ) or not _IS_PYTHON3 and isinstance ( color , long ):
112+ if isinstance (color , _INTTYPES ):
104113 # format a web style color with the format 0xRRGGBB
105114 return _Color (color >> 16 & 0xff , color >> 8 & 0xff , color & 0xff )
106115 return _Color (* color )
@@ -139,7 +148,7 @@ def drawChar(self, x, y, char, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
139148 """
140149
141150 assert _verify_colors (fgcolor , bgcolor )
142- assert self ._drawable (x , y )
151+ x , y = self ._normalizePoint (x , y )
143152
144153 self ._setChar (ctypes .c_int (x ), ctypes .c_int (y ), _formatChar (char ),
145154 _formatColor (fgcolor ), _formatColor (bgcolor ))
@@ -166,7 +175,7 @@ def drawStr(self, x, y, string, fgcolor=(255, 255, 255), bgcolor=(0, 0, 0)):
166175
167176 """
168177
169- assert self ._drawable (x , y )
178+ x , y = self ._normalizePoint (x , y )
170179 assert _verify_colors (fgcolor , bgcolor )
171180 fgcolor , bgcolor = _formatColor (fgcolor ), _formatColor (bgcolor )
172181 width , height = self .getSize ()
@@ -237,64 +246,50 @@ def drawFrame(self, x, y, width, height, string, fgcolor=(255, 255, 255), bgcolo
237246 self .drawRect (x + width - 1 , y , 1 , height , char , fgcolor , bgcolor )
238247 self .drawRect (x , y + height - 1 , width , 1 , char , fgcolor , bgcolor )
239248
249+ def _normalizePoint (self , x , y ):
250+ """Check if a point is in bounds and make minor adjustments.
251+
252+ Respects Pythons negative indexes. -1 starts at the bottom right.
253+ Replaces the _drawable function
254+ """
255+ assert isinstance (x , _INTTYPES ), 'x must be an integer, got %s' % repr (x )
256+ assert isinstance (y , _INTTYPES ), 'y must be an integer, got %s' % repr (y )
257+
258+ assert (- self .width <= x < self .width ) and (- self .height <= y < self .height ), \
259+ ('(%i, %i) is an invalid postition on %s' % (x , y , self ))
260+
261+ # handle negative indexes
262+ if x < 0 :
263+ x += self .width
264+ if y < 0 :
265+ y += self .height
266+ return (x , y )
240267
241268 def _normalizeRect (self , x , y , width , height ):
242269 """Check if the rectangle is in bounds and make minor adjustments.
243270 raise AssertionError's for any problems
244271 """
245- old = x , y , width , height
246- assert isinstance (x , int ), 'x must be an integer, got %s' % repr (x )
247- assert isinstance (y , int ), 'y must be an integer, got %s' % repr (y )
248- if width == None : # if width or height are None then extend them to the edge
249- width = self .width - x
250- if height == None :
251- height = self .height - y
252- assert isinstance (width , int ), 'width must be an integer or None, got %s' % repr (width )
253- assert isinstance (height , int ), 'height must be an integer or None, got %s' % repr (height )
254-
255- assert width >= 0 and height >= 0 , 'width and height cannot be negative'
256- # later idea, negative numbers work like Python list indexing
257-
258- assert x >= 0 and y >= 0 and x + width <= self .width and y + height <= self .height , \
259- 'Rect is out of bounds at (x=%i y=%i width=%i height=%i), Console bounds are (width=%i, height=%i)' % (old + self .getSize ())
260- return x , y , width , height
261-
262- def _rectInBounds (self , x , y , width , height ):
263- "check the rect so see if it's within the bounds of this console"
264- if width is None :
265- width = 0
266- if height is None :
267- height = 0
268- if (x < 0 or y < 0 or
269- x + width > self .width or y + height > self .height ):
270- return False
271-
272- return True
273-
274- def _clampRect (self , x = 0 , y = 0 , width = None , height = None ):
275- """Alter a rectange to fit inside of this console
276-
277- width and hight of None will extend to the edge and
278- an area out of bounds will end with a width and height of 0
279- """
280- # extend any width and height of None to the end of the console
272+ x , y = self ._normalizePoint (x , y ) # inherit _normalizePoint logic
273+
274+ assert width is None or isinstance (width , _INTTYPES ), 'width must be an integer or None, got %s' % repr (width )
275+ assert height is None or isinstance (height , _INTTYPES ), 'height must be an integer or None, got %s' % repr (height )
276+
277+ # if width or height are None then extend them to the edge
281278 if width is None :
282279 width = self .width - x
280+ elif width < 0 : # handle negative numbers
281+ width += self .width
282+ width = max (0 , width ) # a 'too big' negative is clamped zero
283283 if height is None :
284284 height = self .height - y
285- # move x and y within bounds, shrinking the width and height to match
286- if x < 0 :
287- width += x
288- x = 0
289- if y < 0 :
290- height += y
291- y = 0
292- # move width and height within bounds
285+ height = max (0 , height )
286+ elif height < 0 :
287+ height += self .height
288+
289+ # reduce rect size to bounds
293290 width = min (width , self .width - x )
294291 height = min (height , self .height - y )
295- # a rect that was out of bounds will have a 0 or negative width or height at this point
296- if width <= 0 or height <= 0 :
297- width = height = 0
292+
298293 return x , y , width , height
299294
300295 def blit (self , source , x = 0 , y = 0 , width = None , height = None , srcX = 0 , srcY = 0 ):
@@ -316,26 +311,17 @@ def blit(self, source, x=0, y=0, width=None, height=None, srcX=0, srcY=0):
316311
317312 assert isinstance (source , (Console , Window )), "source muse be a Window or Console instance"
318313
319- assert width is None or isinstance (width , (int )), "width must be a number or None, got %s" % repr (width )
320- assert height is None or isinstance (height , (int )), "height must be a number or None, got %s" % repr (height )
321-
322- # fill in width, height
323- if width == None :
324- width = min (self .width - x , source .width - srcX )
325- if height == None :
326- height = min (self .height - y , source .height - srcY )
327-
314+ # handle negative indexes and rects
315+ # negative width and height will be set realtive to the destination
316+ # and will also be clamped to the smallest Console
328317 x , y , width , height = self ._normalizeRect (x , y , width , height )
329318 srcX , srcY , width , height = source ._normalizeRect (srcX , srcY , width , height )
330319
331320 # translate source and self if any of them are Window instances
332- if isinstance (source , Window ):
333- srcX , srcY = source ._translate (srcX , srcY )
334- source = source .console
335-
336- if isinstance (self , Window ):
337- x , y = self ._translate (x , y )
338- self = self .console
321+ srcX , srcY = source ._translate (srcX , srcY )
322+ source = source .console
323+ x , y = self ._translate (x , y )
324+ self = self .console
339325
340326 if self == source :
341327 # if we are the same console then we need a third console to hold
@@ -361,8 +347,8 @@ def scroll(self, x, y):
361347 @type x: int
362348 @type y: int
363349 """
364- assert isinstance (x , int ), "x must be an integer, got %s" % repr (x )
365- assert isinstance (y , int ), "y must be an integer, got %s" % repr (x )
350+ assert isinstance (x , _INTTYPES ), "x must be an integer, got %s" % repr (x )
351+ assert isinstance (y , _INTTYPES ), "y must be an integer, got %s" % repr (x )
366352 def getSlide (x , length ):
367353 """get the parameters needed to scroll the console in the given
368354 direction with x
@@ -422,19 +408,6 @@ def __contains__(self, position):
422408 x , y = position
423409 return (0 <= x < self .width ) and (0 <= y < self .height )
424410
425- def _drawable (self , x , y ):
426- """Used internally
427- Checks if a cell is part of the console.
428- Raises an AssertionError if it can not be used.
429- """
430- assert isinstance (x , int ), 'x must be an integer, got %s' % repr (x )
431- assert isinstance (y , int ), 'y must be an integer, got %s' % repr (y )
432-
433- assert (0 <= x < self .width ) and (0 <= y < self .height ), \
434- ('(%i, %i) is an invalid postition. %s size is (%i, %i)' %
435- (x , y , self .__class__ .__name__ , self .width , self .height ))
436- return True
437-
438411class Console (_MetaConsole ):
439412 """The Console is the main class of the tdl library.
440413
@@ -453,16 +426,18 @@ def __init__(self, width, height):
453426 if not _rootinitialized :
454427 raise TDLError ('Can not create Console\' s before tdl.init' )
455428 self ._as_parameter_ = _lib .TCOD_console_new (width , height )
429+ self .console = self
456430 self .width = width
457431 self .height = height
458- self ._typewriter = None # "typewriter" lock, makes sure the colors are set to the typewriter
432+ self ._typewriter = None # "typewriter lock" , makes sure the colors are set to the typewriter
459433 #self.clear()
460434
461435 @classmethod
462436 def _newConsole (cls , console ):
463437 """Make a Console instance, from a console ctype"""
464438 self = cls .__new__ (cls )
465439 self ._as_parameter_ = console
440+ self .console = self
466441 self .width = _lib .TCOD_console_get_width (self )
467442 self .height = _lib .TCOD_console_get_height (self )
468443 self ._typewriter = None
@@ -549,13 +524,13 @@ def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1, nullChar=False):
549524 """
550525 if fgcolor and bgcolor and not nullChar :
551526 # buffer values as ctypes objects
552- self ._typewriter = None
527+ self ._typewriter = None # clear the typewriter as colors will be set
553528 console = self ._as_parameter_
554529 bgblend = ctypes .c_int (bgblend )
555530
556531 _lib .TCOD_console_set_default_background (console , bgcolor )
557532 _lib .TCOD_console_set_default_foreground (console , fgcolor )
558- _putChar = _lib .TCOD_console_put_char # remove dots
533+ _putChar = _lib .TCOD_console_put_char # remove dots and make local
559534 for (x , y ), char in batch :
560535 _putChar (console , x , y , char , bgblend )
561536 else :
@@ -571,7 +546,8 @@ def getChar(self, x, y):
571546
572547 @rtype: (int, 3-item tuple, 3-item tuple)
573548 """
574- assert self ._drawable (x , y )
549+ #assert self._drawable(x, y)
550+ x , y = self ._normalizePoint (x , y )
575551 char = _lib .TCOD_console_get_char (self , x , y )
576552 bgcolor = _lib .TCOD_console_get_char_background_wrapper (self , x , y )
577553 fgcolor = _lib .TCOD_console_get_char_foreground_wrapper (self , x , y )
@@ -627,7 +603,7 @@ def _setCharBatch(self, batch, fgcolor, bgcolor, bgblend=1):
627603
628604
629605 def drawChar (self , x , y , char , fgcolor = (255 , 255 , 255 ), bgcolor = (0 , 0 , 0 )):
630- assert self ._drawable (x , y )
606+ x , y = self ._normalizePoint (x , y )
631607 self .parent .drawChar (x + self .x , y + self .y , char , fgcolor , bgcolor )
632608
633609 def drawRect (self , x , y , width , height , string , fgcolor = (255 , 255 , 255 ), bgcolor = (0 , 0 , 0 )):
@@ -643,7 +619,7 @@ def getChar(self, x, y):
643619
644620 @rtype: (int, 3-item tuple, 3-item tuple)
645621 """
646- self ._drawable (x , y )
622+ x , y = self ._normalizePoint (x , y )
647623 return self .console .getChar (self ._translate (x , y ))
648624
649625 def __repr__ (self ):
@@ -695,8 +671,7 @@ def getCursor(self):
695671 return x , y
696672
697673 def move (self , x , y ):
698- assert self .parent ._drawable (x , y )
699- self .cursor = (x , y )
674+ self .cursor = self .parent ._normalizePoint (x , y )
700675
701676 def setFG (self , color ):
702677 assert _iscolor (color )
@@ -711,6 +686,7 @@ def setBG(self, color):
711686 _lib .TCOD_console_set_default_background (self .console , self .bgcolor )
712687
713688 def _updateConsole (self ):
689+ """Make sure the colors on a console match the Typewriter instance"""
714690 if self .console ._typewriter is not self :
715691 self .console ._typewriter = self
716692
@@ -726,8 +702,9 @@ def addChar(self, char):
726702 _lib .TCOD_console_put_char (self .console ._as_parameter_ , x , y , _formatChar (char ), self ._bgblend )
727703
728704
729- def addStr (self , string , lineBreak = ' \n ' ):
705+ def addStr (self , string ):
730706 x , y = self .cursor
707+ lineBreak = '\n '
731708 for char in string :
732709 if char == lineBreak :
733710 x = 0
@@ -967,7 +944,7 @@ def setFPS(frameRate):
967944 """
968945 if frameRate is None :
969946 frameRate = 0
970- assert isinstance (frameRate , int ), 'frameRate must be an integer or None, got: %s' % repr (frameRate )
947+ assert isinstance (frameRate , _INTTYPES ), 'frameRate must be an integer or None, got: %s' % repr (frameRate )
971948 _lib .TCOD_sys_set_fps (frameRate )
972949
973950def getFPS ():
0 commit comments