1 """
2 This is the official documentation for python-tdl. A Pythonic port of
3 U{libtcod<http://roguecentral.org/doryen/libtcod/>}.
4
5 You can find the project page on GitHub
6 U{here<https://github.com/HexDecimal/python-tdl>}.
7
8 Report any bugs or issues to the GitHub issue tracker
9 U{here<https://github.com/HexDecimal/python-tdl/issues>}.
10
11 Getting Started
12 ===============
13 Once the library is imported you can load the font you want to use with
14 L{tdl.set_font}.
15 This is optional and when skipped will use a decent default font.
16
17 After that you call L{tdl.init} to set the size of the window and get the
18 root console in return.
19 This console is the canvas to what will appear on the screen.
20
21 Indexing Consoles
22 =================
23 For most methods taking a position you can use Python-style negative
24 indexes to refer to the opposite side of a console with (-1, -1)
25 starting at the bottom right.
26 You can also check if a point is part of a console using containment
27 logic i.e. ((x, y) in console).
28
29 You may also iterate over a console using a for statement. This returns
30 every x,y coordinate available to draw on but it will be extremely slow
31 to actually operate on every coordinate individualy.
32 Try to minimize draws by using an offscreen L{Console}, only drawing
33 what needs to be updated, and using L{Console.blit}.
34
35 Drawing and Colors
36 ==================
37
38 Once you have the root console from L{tdl.init} you can start drawing on
39 it using a method such as L{Console.draw_char}.
40 When using this method you can have the char parameter be an integer or a
41 single character string.
42
43 The fg and bg parameters expect a variety of types.
44 The parameters default to Ellipsis which will tell the function to
45 use the colors previously set by the L{set_colors} method.
46 The colors set by L{Console.set_colors} are per each L{Console}/L{Window}
47 and default to white on black.
48 You can use a 3-item list/tuple of [red, green, blue] with integers in
49 the 0-255 range with [0, 0, 0] being black and [255, 255, 255] being
50 white.
51 You can even use a single integer of 0xRRGGBB if you like.
52
53 Using None in the place of any of the three parameters (char, fg, bg)
54 will tell the function to not overwrite that color or character.
55
56 After the drawing functions are called a call to L{tdl.flush} will update
57 the screen.
58 """
59
60 import sys as _sys
61 import os as _os
62
63 import ctypes as _ctypes
64 import weakref as _weakref
65 import itertools as _itertools
66 import textwrap as _textwrap
67 import struct as _struct
68 import re as _re
69 import warnings as _warnings
70
71 from . import event, map, noise
72 from .__tcod import _lib, _Color, _unpackfile
73 from . import __style as _style
74
75 _IS_PYTHON3 = (_sys.version_info[0] == 3)
76
77 if _IS_PYTHON3:
78 _INTTYPES = (int,)
79 _NUMTYPES = (int, float)
80 _STRTYPES = (str, bytes)
81 else:
82 _INTTYPES = (int, long)
83 _NUMTYPES = (int, long, float)
84 _STRTYPES = (str,)
87 "changes string into bytes if running in python 3, for sending to ctypes"
88 if _IS_PYTHON3 and isinstance(string, str):
89 return string.encode()
90 return string
91
104
105 _fontinitialized = False
106 _rootinitialized = False
107 _rootConsoleRef = None
108
109 _set_char = _lib.TCOD_console_set_char
110 _set_fg = _lib.TCOD_console_set_char_foreground
111 _set_bg = _lib.TCOD_console_set_char_background
112 _put_char_ex = _lib.TCOD_console_put_char_ex
113 _put_char = _lib.TCOD_console_put_char
116 """Used internally.
117 Raise an assertion error if the parameters can not be converted into colors.
118 """
119 for color in colors:
120 assert _iscolor(color), 'a color must be a 3 item tuple, web format, or None, received %s' % repr(color)
121 return True
122
124 """Used internally.
125 A debug function to see if an object can be used as a TCOD color struct.
126 None counts as a parameter to keep the current colors instead.
127
128 This function is often part of an inner-loop and can slow a program down.
129 It has been made to work with assert and can be skipped with the -O flag.
130 Still it's called often and must be optimized.
131 """
132 if color is Ellipsis:
133 return True
134 if color is None:
135 return True
136 if isinstance(color, (tuple, list, _Color)):
137 return len(color) == 3
138 if isinstance(color, _INTTYPES):
139 return True
140 return False
141
142 _formatColor = _Color.parse
145 """Try to get the width and height of a bmp of png image file"""
146 file = open(filename, 'rb')
147 if file.read(8) == b'\x89PNG\r\n\x1a\n':
148 while 1:
149 length, = _struct.unpack('>i', file.read(4))
150 chunkID = file.read(4)
151 if chunkID == '':
152 return None
153 if chunkID == b'IHDR':
154
155 return _struct.unpack('>ii', file.read(8))
156 file.seek(4 + length, 1)
157 file.seek(0)
158 if file.read(8) == b'BM':
159 file.seek(18, 0)
160
161 return _struct.unpack('<ii', file.read(8))
162
165 """
166 The catch all for most TDL specific errors.
167 """
168
170 """
171 Contains methods shared by both the L{Console} and L{Window} classes.
172
173 @undocumented: drawStr drawChar drawFrame drawRect
174 getCursor getSize getChar printStr setColors setMode
175 @group Drawing Methods: draw_*, blit, clear
176 @group Printing Methods: print_*, move, set_colors, set_mode, write, get_cursor
177 """
178 __slots__ = ('width', 'height', 'console', '_cursor', '_fg',
179 '_bg', '_bgblend', '_colorLock', '__weakref__', '__dict__')
180
182 self._cursor = (0, 0)
183 self._scrollMode = 'error'
184 self._fg = _formatColor((255, 255, 255))
185 self._bg = _formatColor((0, 0, 0))
186 self._bgblend = 1
187 self._colorLock = None
188
190 """Check if a point is in bounds and make minor adjustments.
191
192 Respects Pythons negative indexes. -1 starts at the bottom right.
193 Replaces the _drawable function
194 """
195
196 x = int(x)
197 y = int(y)
198
199 assert (-self.width <= x < self.width) and \
200 (-self.height <= y < self.height), \
201 ('(%i, %i) is an invalid postition on %s' % (x, y, self))
202
203
204 return (x % self.width, y % self.height)
205
207 """Check if the rectangle is in bounds and make minor adjustments.
208 raise AssertionError's for any problems
209 """
210 x, y = self._normalizePoint(x, y)
211
212 assert width is None or isinstance(width, _INTTYPES), 'width must be an integer or None, got %s' % repr(width)
213 assert height is None or isinstance(height, _INTTYPES), 'height must be an integer or None, got %s' % repr(height)
214
215
216 if width is None:
217 width = self.width - x
218 elif width < 0:
219 width += self.width
220 width = max(0, width)
221 if height is None:
222 height = self.height - y
223 height = max(0, height)
224 elif height < 0:
225 height += self.height
226
227
228 width = min(width, self.width - x)
229 height = min(height, self.height - y)
230
231 return x, y, width, height
232
234 """return the normalized the cursor position."""
235 width, height = self.get_size()
236 assert width != 0 and height != 0, 'can not print on a console with a width or height of zero'
237 while x >= width:
238 x -= width
239 y += 1
240 while y >= height:
241 if self._scrollMode == 'scroll':
242 y -= 1
243 self.scroll(0, -1)
244 elif self._scrollMode == 'error':
245
246 self._cursor = (0, 0)
247 raise TDLError('Cursor has reached the end of the console')
248 return (x, y)
249
251 """Configure how this console will react to the cursor writing past the
252 end if the console.
253
254 This is for methods that use the virtual cursor, such as L{print_str}.
255
256 @type mode: string
257 @param mode: Possible settings are:
258
259 - 'error' - A TDLError will be raised once the cursor
260 reaches the end of the console. Everything up until
261 the error will still be drawn.
262
263 This is the default setting.
264
265 - 'scroll' - The console will scroll up as stuff is
266 written to the end.
267
268 You can restrict the region with L{tdl.Window} when
269 doing this.
270 @see: L{write}, L{print_str}
271 """
272 MODES = ['error', 'scroll']
273 if mode.lower() not in MODES:
274 raise TDLError('mode must be one of %s, got %s' % (MODES, repr(mode)))
275 self._scrollMode = mode.lower()
276
278 """Sets the colors to be used with the L{print_str} and draw_* methods.
279
280 Values of None will only leave the current values unchanged.
281
282 @type fg: (r, g, b), int, Ellipsis, or None
283 @type bg: (r, g, b), int, Ellipsis, or None
284 @param fg: See Drawing and Colors at the L{module level docs<tdl>}
285 @param bg: See Drawing and Colors at the L{module level docs<tdl>}
286 @see: L{move}, L{print_str}
287 """
288 if fg is not None:
289 self._fg = _formatColor(fg)
290 if bg is not None:
291 self._bg = _formatColor(bg)
292
294 """Print a string at the virtual cursor.
295
296 Handles special characters such as '\\n' and '\\r'.
297 Printing past the bottom of the console will scroll everything upwards
298 if L{set_mode} is set to 'scroll'.
299
300 Colors can be set with L{set_colors} and the virtual cursor can be moved
301 with L{move}.
302
303 @type string: string
304 @param string:
305 @see: L{draw_str}, L{move}, L{set_colors}, L{set_mode}, L{write},
306 L{Window}
307 """
308 x, y = self._cursor
309 for char in string:
310 if char == '\n':
311 x = 0
312 y += 1
313 continue
314 if char == '\r':
315 x = 0
316 continue
317 x, y = self._normalizeCursor(x, y)
318 self.draw_char(x, y, char, self._fg, self._bg)
319 x += 1
320 self._cursor = (x, y)
321
322 - def write(self, string):
323 """This method mimics basic file-like behaviour.
324
325 Because of this method you can replace sys.stdout or sys.stderr with
326 a L{Typewriter} instance.
327
328 This is a convoluted process and behaviour seen now can be excepted to
329 change on later versions.
330
331 @type string: string
332 @see: L{set_colors}, L{set_mode}, L{Window}
333 """
334
335
336
337 x, y = self._normalizeCursor(*self._cursor)
338 width, height = self.get_size()
339 wrapper = _textwrap.TextWrapper(initial_indent=(' '*x), width=width)
340 writeLines = []
341 for line in string.split('\n'):
342 if line:
343 writeLines += wrapper.wrap(line)
344 wrapper.initial_indent = ''
345 else:
346 writeLines.append([])
347
348 for line in writeLines:
349 x, y = self._normalizeCursor(x, y)
350 self.draw_str(x, y, line[x:], self._fg, self._bg)
351 y += 1
352 x = 0
353 y -= 1
354 self._cursor = (x, y)
355
356 - def draw_char(self, x, y, char, fg=Ellipsis, bg=Ellipsis):
357 """Draws a single character.
358
359 @type x: int
360 @param x: X coordinate to draw at.
361 @type y: int
362 @param y: Y coordinate to draw at.
363
364 @type char: int, string, or None
365 @param char: Should be an integer, single character string, or None.
366
367 You can set the char parameter as None if you only want to change
368 the colors of the tile.
369
370 @type fg: (r, g, b), int, Ellipsis, or None
371 @type bg: (r, g, b), int, Ellipsis, or None
372 @param fg: See Drawing and Colors at the L{module level docs<tdl>}
373 @param bg: See Drawing and Colors at the L{module level docs<tdl>}
374
375 @raise AssertionError: Having x or y values that can't be placed inside
376 of the console will raise an AssertionError.
377 You can use always use ((x, y) in console) to
378 check if a tile is drawable.
379 @see: L{get_char}
380 """
381
382 assert _verify_colors(fg, bg)
383 x, y = self._normalizePoint(x, y)
384 x, y = _ctypes.c_int(x), _ctypes.c_int(y)
385 self._set_char(x, y, _formatChar(char),
386 _formatColor(fg), _formatColor(bg))
387
388 - def draw_str(self, x, y, string, fg=Ellipsis, bg=Ellipsis):
389 """Draws a string starting at x and y.
390
391 A string that goes past the right side will wrap around. A string
392 wrapping to below the console will raise a L{TDLError} but will still be
393 written out. This means you can safely ignore the errors with a
394 try... except block if you're fine with partially written strings.
395
396 \\r and \\n are drawn on the console as normal character tiles. No
397 special encoding is done and any string will translate to the character
398 table as is.
399
400 For a string drawing operation that respects special characters see
401 L{print_str}.
402
403 @type x: int
404 @param x: X coordinate to draw at.
405 @type y: int
406 @param y: Y coordinate to draw at.
407
408 @type string: string or iterable
409 @param string: Can be a string or an iterable of numbers.
410
411 Special characters are ignored and rendered as any other
412 character.
413
414 @type fg: (r, g, b), int, Ellipsis, or None
415 @type bg: (r, g, b), int, Ellipsis, or None
416 @param fg: See Drawing and Colors at the L{module level docs<tdl>}
417 @param bg: See Drawing and Colors at the L{module level docs<tdl>}
418
419 @raise AssertionError: Having x or y values that can't be placed inside
420 of the console will raise an AssertionError.
421
422 You can use always use ((x, y) in console) to
423 check if a tile is drawable.
424 @see: L{print_str}
425 """
426
427 x, y = self._normalizePoint(x, y)
428 assert _verify_colors(fg, bg)
429 fg, bg = _formatColor(fg), _formatColor(bg)
430 width, height = self.get_size()
431 batch = []
432 def _drawStrGen(x=x, y=y, string=string, width=width, height=height):
433 """Generator for draw_str
434
435 Iterates over ((x, y), ch) data for _set_batch, raising an
436 error if the end of the console is reached.
437 """
438 for char in string:
439 if y == height:
440 raise TDLError('End of console reached.')
441
442 yield((x, y), _formatChar(char))
443 x += 1
444 if x == width:
445 x = 0
446 y += 1
447 self._set_batch(_drawStrGen(), fg, bg)
448
449 - def draw_rect(self, x, y, width, height, string, fg=Ellipsis, bg=Ellipsis):
450 """Draws a rectangle starting from x and y and extending to width and height.
451
452 If width or height are None then it will extend to the edge of the console.
453
454 @type x: int
455 @param x: x coordinate to draw at.
456 @type y: int
457 @param y: y coordinate to draw at.
458
459 @type width: int or None
460 @param width: Width of the rectangle.
461
462 Can be None to extend to the bottom right of the
463 console or can be a negative number to be sized reltive
464 to the total size of the console.
465 @type height: int or None
466 @param height: Height of the rectangle. See width.
467
468 @type string: int, string, or None
469 @param string: Should be an integer, single character string, or None.
470
471 You can set the char parameter as None if you only want
472 to change the colors of an area.
473
474 @type fg: (r, g, b), int, Ellipsis, or None
475 @type bg: (r, g, b), int, Ellipsis, or None
476 @param fg: See Drawing and Colors at the L{module level docs<tdl>}
477 @param bg: See Drawing and Colors at the L{module level docs<tdl>}
478
479 @raise AssertionError: Having x or y values that can't be placed inside
480 of the console will raise an AssertionError.
481
482 You can use always use ((x, y) in console) to
483 check if a tile is drawable.
484 @see: L{clear}, L{draw_frame}
485 """
486 x, y, width, height = self._normalizeRect(x, y, width, height)
487 assert _verify_colors(fg, bg)
488 fg, bg = _formatColor(fg), _formatColor(bg)
489 char = _formatChar(string)
490
491
492 grid = _itertools.product((_ctypes.c_int(x) for x in range(x, x + width)),
493 (_ctypes.c_int(y) for y in range(y, y + height)))
494
495 batch = zip(grid, _itertools.repeat(char, width * height))
496 self._set_batch(batch, fg, bg, nullChar=(char is None))
497
498 - def draw_frame(self, x, y, width, height, string, fg=Ellipsis, bg=Ellipsis):
499 """Similar to L{draw_rect} but only draws the outline of the rectangle.
500
501 @type x: int
502 @param x: x coordinate to draw at.
503 @type y: int
504 @param y: y coordinate to draw at.
505
506 @type width: int or None
507 @param width: Width of the rectangle.
508
509 Can be None to extend to the bottom right of the
510 console or can be a negative number to be sized reltive
511 to the total size of the console.
512 @type height: int or None
513 @param height: Height of the rectangle. See width.
514
515 @type string: int, string, or None
516 @param string: Should be an integer, single character string, or None.
517
518 You can set the char parameter as None if you only want
519 to change the colors of an area.
520
521 @type fg: (r, g, b), int, Ellipsis, or None
522 @type bg: (r, g, b), int, Ellipsis, or None
523 @param fg: See Drawing and Colors at the L{module level docs<tdl>}
524 @param bg: See Drawing and Colors at the L{module level docs<tdl>}
525
526 @raise AssertionError: Having x or y values that can't be placed inside
527 of the console will raise an AssertionError.
528
529 You can use always use ((x, y) in console) to
530 check if a tile is drawable.
531 @see: L{draw_rect}, L{Window}
532 """
533 x, y, width, height = self._normalizeRect(x, y, width, height)
534 assert _verify_colors(fg, bg)
535 fg, bg = _formatColor(fg), _formatColor(bg)
536 char = _formatChar(string)
537 if width == 1 or height == 1:
538 return self.draw_rect(x, y, width, height, char, fg, bg)
539
540
541 self.draw_rect(x, y, 1, height, char, fg, bg)
542 self.draw_rect(x, y, width, 1, char, fg, bg)
543 self.draw_rect(x + width - 1, y, 1, height, char, fg, bg)
544 self.draw_rect(x, y + height - 1, width, 1, char, fg, bg)
545
546 - def blit(self, source, x=0, y=0, width=None, height=None, srcX=0, srcY=0):
547 """Blit another console or Window onto the current console.
548
549 By default it blits the entire source to the topleft corner.
550
551 @type source: L{Console} or L{Window}
552 @param source: Source window can be a L{Console} or L{Window} instance.
553 It can even blit to itself without any problems.
554
555 @type x: int
556 @param x: X coordinate to blit to.
557 @type y: int
558 @param y: Y coordinate to blit to.
559
560 @type width: int or None
561 @param width: Width of the rectangle.
562
563 Can be None to extend as far as possible to the
564 bottom right corner of the blit area or can be a negative
565 number to be sized reltive to the total size of the
566 B{destination} console.
567 @type height: int or None
568 @param height: Height of the rectangle. See width.
569
570 @type srcX: int
571 @param srcX: The source consoles x coordinate to blit from.
572 @type srcY: int
573 @param srcY: The source consoles y coordinate to blit from.
574 """
575
576 fgalpha=1.0
577 bgalpha=1.0
578
579 assert isinstance(source, (Console, Window)), "source muse be a Window or Console instance"
580
581
582
583
584 x, y, width, height = self._normalizeRect(x, y, width, height)
585 srcX, srcY, width, height = source._normalizeRect(srcX, srcY, width, height)
586
587
588 srcX, srcY = source._translate(srcX, srcY)
589 source = source.console
590 x, y = self._translate(x, y)
591 self = self.console
592
593 if self == source:
594
595
596
597 tmp = Console(width, height)
598 _lib.TCOD_console_blit(source, srcX, srcY, width, height, tmp, 0, 0, fgalpha, bgalpha)
599 _lib.TCOD_console_blit(tmp, 0, 0, width, height, self, x, y, fgalpha, bgalpha)
600 else:
601 _lib.TCOD_console_blit(source, srcX, srcY, width, height, self, x, y, fgalpha, bgalpha)
602
604 """Return the virtual cursor position.
605
606 @rtype: (x, y)
607 @return: Returns (x, y) a 2-integer tuple containing where the next
608 L{addChar} or L{addStr} will start at.
609
610 This can be changed with the L{move} method.
611 @see: L{move}
612 """
613 x, y = self._cursor
614 width, height = self.parent.get_size()
615 while x >= width:
616 x -= width
617 y += 1
618 if y >= height and self.scrollMode == 'scroll':
619 y = height - 1
620 return x, y
621
623 """Return the size of the console as (width, height)
624
625 @rtype: (width, height)
626 """
627 return self.width, self.height
628
630 """Return an iterator with every possible (x, y) value for this console.
631
632 It goes without saying that working on the console this way is a
633 slow process, especially for Python, and should be minimized.
634 @rtype: iter((x, y), ...)
635 """
636 return _itertools.product(range(self.width), range(self.height))
637
638 - def move(self, x, y):
639 """Move the virtual cursor.
640
641 @type x: int
642 @param x: X position to place the cursor.
643 @type y: int
644 @param y: Y position to place the cursor.
645 @see: L{get_cursor}, L{print_str}, L{write}
646 """
647 self._cursor = self._normalizePoint(x, y)
648
679 def getCover(x, length):
680 """return the (x, width) ranges of what is covered and uncovered"""
681 cover = (0, length)
682 uncover = None
683 if x > 0:
684 cover = (x, length - x)
685 uncover = (0, x)
686 elif x < 0:
687 x = abs(x)
688 cover = (0, length - x)
689 uncover = (length - x, x)
690 return cover, uncover
691
692 width, height = self.get_size()
693 if abs(x) >= width or abs(y) >= height:
694 return self.clear()
695
696
697 coverX, uncoverX = getCover(x, width)
698 coverY, uncoverY = getCover(y, height)
699
700
701
702
703
704
705
706 x, width, srcx = getSlide(x, width)
707 y, height, srcy = getSlide(y, height)
708 self.blit(self, x, y, width, height, srcx, srcy)
709 if uncoverX:
710 self.draw_rect(uncoverX[0], coverY[0], uncoverX[1], coverY[1],
711 0x20, self._fg, self._bg)
712 if uncoverY:
713 self.draw_rect(coverX[0], uncoverY[0], coverX[1], uncoverY[1],
714 0x20, self._fg, self._bg)
715 if uncoverX and uncoverY:
716 self.draw_rect(uncoverX[0], uncoverY[0], uncoverX[1], uncoverY[1],
717 0x20, self._fg, self._bg)
718
720 """Return the character and colors of a tile as (ch, fg, bg)
721
722 This method runs very slowly as is not recommended to be called
723 frequently.
724
725 @rtype: (int, (r, g, b), (r, g, b))
726 @returns: Returns a 3-item tuple. The first item is an integer of the
727 character at the position (x, y) the second and third are the
728 foreground and background colors respectfully.
729 @see: L{draw_char}
730 """
731 raise NotImplementedError('Method here only exists for the docstring')
732
734 """Use ((x, y) in console) to check if a position is drawable on this console.
735 """
736 x, y = position
737 return (0 <= x < self.width) and (0 <= y < self.height)
738
740 """Contains character and color data and can be drawn to.
741
742 The console created by the L{tdl.init} function is the root console and is the
743 console that is rendered to the screen with L{flush}.
744
745 Any console created from the Console class is an off-screen console that
746 can be drawn on before being L{blit} to the root console.
747
748 @undocumented: getChar
749 """
750
751 __slots__ = ('_as_parameter_', '_typewriter')
752
769
770
771 @classmethod
782
799
801
802 clone = self.__class__(self.width, self.height)
803 clone.blit(self)
804 return clone
805
811
819
834
836 """Convertion x and y to their position on the root Console for this Window
837
838 Because this is a Console instead of a Window we return the paramaters
839 untouched"""
840 return x, y
841
842 - def clear(self, fg=Ellipsis, bg=Ellipsis):
843 """Clears the entire Console.
844
845 @type fg: (r, g, b)
846 @param fg: Foreground color.
847
848 Must be a 3-item list with integers that range 0-255.
849
850 Unlike most other operations you cannot use None here.
851 To clear only the foreground or background use L{draw_rect}.
852 @type bg: (r, g, b)
853 @param bg: Background color. See fg.
854 @see: L{draw_rect}
855 """
856 assert _verify_colors(fg, bg)
857 assert fg is not None and bg is not None, 'Can not use None with clear'
858 self._typewriter = None
859 if fg is Ellipsis:
860 fg = self._fg
861 else:
862 fg = _formatColor(fg)
863 if bg is Ellipsis:
864 bg = self._bg
865 else:
866 bg = _formatColor(bg)
867 _lib.TCOD_console_set_default_foreground(self, fg)
868 _lib.TCOD_console_set_default_background(self, bg)
869 _lib.TCOD_console_clear(self)
870
871
872 - def _set_char(self, x, y, char, fg=Ellipsis, bg=Ellipsis, bgblend=1):
873 """
874 Sets a character.
875 This is called often and is designed to be as fast as possible.
876
877 Because of the need for speed this function will do NO TYPE CHECKING
878 AT ALL, it's up to the drawing functions to use the functions:
879 _formatChar and _formatColor before passing to this."""
880
881 console = self._as_parameter_
882 if char is not None and fg is not None and bg is not None:
883 if fg is bg is Ellipsis:
884
885
886 _put_char(console, x, y, char, bgblend)
887 return
888
889
890 if fg is Ellipsis:
891 fg = self._fg
892 if bg is Ellipsis:
893 bg = self._bg
894
895 _put_char_ex(console, x, y, char, fg, bg)
896 return
897
898
899
900 if char is not None:
901 _set_char(console, x, y, char)
902 if fg is not None:
903 if fg is Ellipsis:
904 fg = self._fg
905 _set_fg(console, x, y, fg)
906 if bg is not None:
907 if bg is Ellipsis:
908 bg = self._bg
909 _set_bg(console, x, y, bg, bgblend)
910
911 - def _set_batch(self, batch, fg, bg, bgblend=1, nullChar=False):
912 """
913 Try to perform a batch operation otherwise fall back to _set_char.
914 If fg and bg are defined then this is faster but not by very
915 much.
916
917 if any character is None then nullChar is True
918
919 batch is a iterable of [(x, y), ch] items
920 """
921 if fg is Ellipsis:
922 fg = self._fg
923 if bg is Ellipsis:
924 bg = self._bg
925
926 if fg and not nullChar:
927
928 self._typewriter = None
929 console = self._as_parameter_
930 bgblend = _ctypes.c_int(bgblend)
931
932 if not bg:
933 bgblend = 0
934 else:
935 _lib.TCOD_console_set_default_background(console, bg)
936 _lib.TCOD_console_set_default_foreground(console, fg)
937 _putChar = _lib.TCOD_console_put_char
938 for (x, y), char in batch:
939 _putChar(console, x, y, char, bgblend)
940
941 else:
942 for (x, y), char in batch:
943 self._set_char(x, y, char, fg, bg, bgblend)
944
946
947 x, y = self._normalizePoint(x, y)
948 char = _lib.TCOD_console_get_char(self, x, y)
949 bg = _lib.TCOD_console_get_char_background_wrapper(self, x, y)
950 fg = _lib.TCOD_console_get_char_foreground_wrapper(self, x, y)
951 return char, tuple(fg), tuple(bg)
952
954 return "<Console (Width=%i Height=%i)>" % (self.width, self.height)
955
956
957 -class Window(_BaseConsole):
958 """A Window contains a small isolated part of a Console.
959
960 Drawing on the Window draws on the Console.
961
962 Making a Window and setting its width or height to None will extend it to
963 the edge of the console.
964
965 @undocumented: getChar
966 """
967
968 __slots__ = ('parent', 'x', 'y')
969
970 - def __init__(self, console, x, y, width, height):
971 """Isolate part of a L{Console} or L{Window} instance.
972
973 @type console: L{Console} or L{Window}
974 @param console: The parent object which can be a L{Console} or another
975 L{Window} instance.
976
977 @type x: int
978 @param x: X coordinate to place the Window.
979
980 This follows the normal rules for indexing so you can use a
981 negative integer to place the Window relative to the bottom
982 right of the parent Console instance.
983 @type y: int
984 @param y: Y coordinate to place the Window.
985
986 See x.
987
988 @type width: int or None
989 @param width: Width of the Window.
990
991 Can be None to extend as far as possible to the
992 bottom right corner of the parent Console or can be a
993 negative number to be sized reltive to the Consoles total
994 size.
995 @type height: int or None
996 @param height: Height of the Window.
997
998 See width.
999 """
1000 _BaseConsole.__init__(self)
1001 assert isinstance(console, (Console, Window)), 'console parameter must be a Console or Window instance, got %s' % repr(console)
1002 self.parent = console
1003 self.x, self.y, self.width, self.height = console._normalizeRect(x, y, width, height)
1004 if isinstance(console, Console):
1005 self.console = console
1006 else:
1007 self.console = self.parent.console
1008
1010 """Convertion x and y to their position on the root Console"""
1011
1012 return self.parent._translate((x + self.x), (y + self.y))
1013
1014 - def clear(self, fg=Ellipsis, bg=Ellipsis):
1015 """Clears the entire Window.
1016
1017 fg and bg can not be None for this function, use L{draw_rect}.
1018
1019 @type fg: (r, g, b), int, Ellipsis, or None
1020 @type bg: (r, g, b), int, Ellipsis, or None
1021 @param fg: See Drawing and Colors at the L{module level docs<tdl>}
1022 @param bg: See fg
1023 @see: L{draw_rect}
1024 """
1025 assert _verify_colors(fg, bg)
1026 assert fg is not None and bg is not None, 'Can not use None with clear'
1027 if fg is Ellipsis:
1028 fg = self._fg
1029 if bg is Ellipsis:
1030 bg = self._bg
1031 self.draw_rect(0, 0, None, None, 0x20, fg, bg)
1032
1033 - def _set_char(self, x, y, char=None, fg=None, bg=None, bgblend=1):
1035
1037
1038 myX = self.x
1039 myY = self.y
1040 self.parent._set_batch((((x + myX, y + myY), ch)
1041 for ((x, y), ch) in batch), *args, **kargs)
1042
1043
1044 - def draw_char(self, x, y, char, fg=Ellipsis, bg=Ellipsis):
1045
1046 x, y = self._normalizePoint(x, y)
1047 if fg is Ellipsis:
1048 fg = self._fg
1049 if bg is Ellipsis:
1050 bg = self._bg
1051 self.parent.draw_char(x + self.x, y + self.y, char, fg, bg)
1052
1053 - def draw_rect(self, x, y, width, height, string, fg=Ellipsis, bg=Ellipsis):
1054
1055 x, y, width, height = self._normalizeRect(x, y, width, height)
1056 if fg is Ellipsis:
1057 fg = self._fg
1058 if bg is Ellipsis:
1059 bg = self._bg
1060 self.parent.draw_rect(x + self.x, y + self.y, width, height,
1061 string, fg, bg)
1062
1063 - def draw_frame(self, x, y, width, height, string, fg=Ellipsis, bg=Ellipsis):
1064
1065 x, y, width, height = self._normalizeRect(x, y, width, height)
1066 if fg is Ellipsis:
1067 fg = self._fg
1068 if bg is Ellipsis:
1069 bg = self._bg
1070 self.parent.draw_frame(x + self.x, y + self.y, width, height,
1071 string, fg, bg)
1072
1077
1079 return "<Window(X=%i Y=%i Width=%i Height=%i)>" % (self.x, self.y,
1080 self.width,
1081 self.height)
1082
1083
1084
1085
1086 -def init(width, height, title=None, fullscreen=False, renderer='OPENGL'):
1087 """Start the main console with the given width and height and return the
1088 root console.
1089
1090 Call the consoles drawing functions. Then remember to use L{tdl.flush} to
1091 make what's drawn visible on the console.
1092
1093 @type width: int
1094 @param width: width of the root console (in tiles)
1095
1096 @type height: int
1097 @param height: height of the root console (in tiles)
1098
1099 @type title: string
1100 @param title: Text to display as the window title.
1101
1102 If left None it defaults to the running scripts filename.
1103
1104 @type fullscreen: boolean
1105 @param fullscreen: Can be set to True to start in fullscreen mode.
1106
1107 @type renderer: string
1108 @param renderer: Can be one of 'GLSL', 'OPENGL', or 'SDL'.
1109
1110 Due to way Python works you're unlikely to see much of an
1111 improvement by using 'GLSL' over 'OPENGL' as most of the
1112 time Python is slow interacting with the console and the
1113 rendering itself is pretty fast even on 'SDL'.
1114
1115 @rtype: L{Console}
1116 @return: The root console. Only what is drawn on the root console is
1117 what's visible after a call to L{tdl.flush}.
1118 After the root console is garbage collected, the window made by
1119 this function will close.
1120 @see: L{Console}, L{set_font}
1121 """
1122 RENDERERS = {'GLSL': 0, 'OPENGL': 1, 'SDL': 2}
1123 global _rootinitialized, _rootConsoleRef
1124 if not _fontinitialized:
1125 set_font(_unpackfile('terminal8x8.png'), None, None, True, True)
1126
1127 if renderer.upper() not in RENDERERS:
1128 raise TDLError('No such render type "%s", expected one of "%s"' % (renderer, '", "'.join(RENDERERS)))
1129 renderer = RENDERERS[renderer.upper()]
1130
1131
1132 if _rootConsoleRef and _rootConsoleRef():
1133 oldroot = _rootConsoleRef()
1134 rootreplacement = Console(oldroot.width, oldroot.height)
1135 rootreplacement.blit(oldroot)
1136 oldroot._replace(rootreplacement)
1137 del rootreplacement
1138
1139 if title is None:
1140 if _sys.argv:
1141
1142 title = _os.path.basename(_sys.argv[0])
1143 else:
1144 title = 'python-tdl'
1145
1146 _lib.TCOD_console_init_root(width, height, _encodeString(title), fullscreen, renderer)
1147
1148
1149
1150
1151 event._eventsflushed = False
1152 _rootinitialized = True
1153 rootconsole = Console._newConsole(_ctypes.c_void_p())
1154 _rootConsoleRef = _weakref.ref(rootconsole)
1155
1156 return rootconsole
1157
1159 """Make all changes visible and update the screen.
1160
1161 Remember to call this function after drawing operations.
1162 Calls to flush will enfore the frame rate limit set by L{tdl.set_fps}.
1163
1164 This function can only be called after L{tdl.init}
1165 """
1166 if not _rootinitialized:
1167 raise TDLError('Cannot flush without first initializing with tdl.init')
1168
1169 event.get()
1170 _lib.TCOD_console_flush()
1171
1172 -def set_font(path, columns=None, rows=None, columnFirst=False,
1173 greyscale=False, altLayout=False):
1174 """Changes the font to be used for this session.
1175 This should be called before L{tdl.init}
1176
1177 If the font specifies its size in its filename (i.e. font_NxN.png) then this
1178 function can auto-detect the tileset formatting and the parameters columns
1179 and rows can be left None.
1180
1181 While it's possible you can change the font mid program it can sometimes
1182 break in rare circumstances. So use caution when doing this.
1183
1184 @type path: string
1185 @param path: Must be a string filepath where a bmp or png file is found.
1186
1187 @type columns: int
1188 @param columns: Number of columns in the tileset.
1189
1190 Can be left None for auto-detection.
1191
1192 @type rows: int
1193 @param rows: Number of rows in the tileset.
1194
1195 Can be left None for auto-detection.
1196
1197 @type columnFirst: boolean
1198 @param columnFirst: Defines if the characer order goes along the rows or
1199 colomns.
1200 It should be True if the charater codes 0-15 are in the
1201 first column.
1202 And should be False if the characters 0-15
1203 are in the first row.
1204
1205 @type greyscale: boolean
1206 @param greyscale: Creates an anti-aliased font from a greyscale bitmap.
1207 Otherwise it uses the alpha channel for anti-aliasing.
1208
1209 Unless you actually need anti-aliasing from a font you
1210 know uses a smooth greyscale channel you should leave
1211 this on False.
1212
1213 @type altLayout: boolean
1214 @param altLayout: An alternative layout with space in the upper left
1215 corner.
1216 The colomn parameter is ignored if this is True,
1217 find examples of this layout in the font/libtcod/
1218 directory included with the python-tdl source.
1219
1220 @raise TDLError: Will be raised if no file is found at path or if auto-
1221 detection fails.
1222
1223 @note: A png file that's been optimized can fail to load correctly on
1224 MAC OS X creating a garbled mess when rendering.
1225 Don't use a program like optipng or just use bmp files instead if
1226 you want your program to work on macs.
1227 """
1228
1229 FONT_LAYOUT_ASCII_INCOL = 1
1230 FONT_LAYOUT_ASCII_INROW = 2
1231 FONT_TYPE_GREYSCALE = 4
1232 FONT_LAYOUT_TCOD = 8
1233 global _fontinitialized
1234 _fontinitialized = True
1235 flags = 0
1236 if altLayout:
1237 flags |= FONT_LAYOUT_TCOD
1238 elif columnFirst:
1239 flags |= FONT_LAYOUT_ASCII_INCOL
1240 else:
1241 flags |= FONT_LAYOUT_ASCII_INROW
1242 if greyscale:
1243 flags |= FONT_TYPE_GREYSCALE
1244 if not _os.path.exists(path):
1245 raise TDLError('no file exists at: "%s"' % path)
1246 path = _os.path.abspath(path)
1247
1248
1249 imgSize = _getImageSize(path)
1250 if imgSize:
1251 imgWidth, imgHeight = imgSize
1252
1253 match = _re.match('.*?([0-9]+)[xX]([0-9]+)', _os.path.basename(path))
1254 if match:
1255 fontWidth, fontHeight = match.groups()
1256 fontWidth, fontHeight = int(fontWidth), int(fontHeight)
1257
1258
1259 estColumns, remC = divmod(imgWidth, fontWidth)
1260 estRows, remR = divmod(imgHeight, fontHeight)
1261 if remC or remR:
1262 _warnings.warn("Font may be incorrectly formatted.")
1263
1264 if not columns:
1265 columns = estColumns
1266 if not rows:
1267 rows = estRows
1268 else:
1269
1270 if not (columns and rows):
1271
1272 raise TDLError('%s has no font size in filename' % _os.path.basename(path))
1273
1274 if columns and rows:
1275
1276 if (fontWidth * columns != imgWidth or
1277 fontHeight * rows != imgHeight):
1278 _warnings.warn("set_font parameters are set as if the image size is (%d, %d) when the detected size is actually (%i, %i)"
1279 % (fontWidth * columns, fontHeight * rows,
1280 imgWidth, imgHeight))
1281 else:
1282 _warnings.warn("%s is probably not an image." % _os.path.basename(path))
1283
1284 if not (columns and rows):
1285
1286 raise TDLError('Can not auto-detect the tileset of %s' % _os.path.basename(path))
1287
1288 _lib.TCOD_console_set_custom_font(_encodeString(path), flags, columns, rows)
1289
1291 """Returns True if program is fullscreen.
1292
1293 @rtype: boolean
1294 @return: Returns True if the window is in fullscreen mode.
1295 Otherwise returns False.
1296 """
1297 if not _rootinitialized:
1298 raise TDLError('Initialize first with tdl.init')
1299 return _lib.TCOD_console_is_fullscreen()
1300
1302 """Changes the fullscreen state.
1303
1304 @type fullscreen: boolean
1305 """
1306 if not _rootinitialized:
1307 raise TDLError('Initialize first with tdl.init')
1308 _lib.TCOD_console_set_fullscreen(fullscreen)
1309
1311 """Change the window title.
1312
1313 @type title: string
1314 """
1315 if not _rootinitialized:
1316 raise TDLError('Not initilized. Set title with tdl.init')
1317 _lib.TCOD_console_set_window_title(_encodeString(title))
1318
1320 """Capture the screen and save it as a png file
1321
1322 @type path: string
1323 @param path: The filepath to save the screenshot.
1324
1325 If path is None then the image will be placed in the current
1326 folder with the names:
1327 screenshot001.png, screenshot002.png, ...
1328 """
1329 if not _rootinitialized:
1330 raise TDLError('Initialize first with tdl.init')
1331 if isinstance(path, str):
1332 _lib.TCOD_sys_save_screenshot(_encodeString(path))
1333 elif path is None:
1334 filelist = _os.listdir('.')
1335 n = 1
1336 filename = 'screenshot%.3i.png' % n
1337 while filename in filelist:
1338 n += 1
1339 filename = 'screenshot%.3i.png' % n
1340 _lib.TCOD_sys_save_screenshot(_encodeString(filename))
1341 else:
1342
1343 tmpname = _os.tempnam()
1344 _lib.TCOD_sys_save_screenshot(_encodeString(tmpname))
1345 with tmpname as tmpfile:
1346 path.write(tmpfile.read())
1347 _os.remove(tmpname)
1348
1352 """Set the maximum frame rate.
1353
1354 @type frameRate: int
1355 @param frameRate: Further calls to L{tdl.flush} will limit the speed of
1356 the program to run at <frameRate> frames per second. Can
1357 also be set to 0 to run without a limit.
1358
1359 Defaults to None.
1360 """
1361 if frameRate is None:
1362 frameRate = 0
1363 assert isinstance(frameRate, _INTTYPES), 'frameRate must be an integer or None, got: %s' % repr(frameRate)
1364 _lib.TCOD_sys_set_fps(frameRate)
1365
1367 """Return the current frames per second of the running program set by
1368 L{set_fps}
1369
1370 @rtype: int
1371 @return: Returns the frameRate set by set_fps.
1372 If set to no limit, this will return 0.
1373 """
1374 return _lib.TCOD_sys_get_fps()
1375
1377 """Change the fullscreen resoulution
1378
1379 @type width: int
1380 @type height: int
1381 """
1382 _lib.TCOD_sys_force_fullscreen_resolution(width, height)
1383
1384
1385 __all__ = [_var for _var in locals().keys() if _var[0] != '_']
1386 __all__ += ['_BaseConsole']
1387
1388
1389 _BaseConsole.setMode = _style.backport(_BaseConsole.set_mode)
1390 _BaseConsole.setColors = _style.backport(_BaseConsole.set_colors)
1391 _BaseConsole.printStr = _style.backport(_BaseConsole.print_str)
1392 _BaseConsole.drawChar = _style.backport(_BaseConsole.draw_char)
1393 _BaseConsole.drawStr = _style.backport(_BaseConsole.draw_str)
1394 _BaseConsole.drawRect = _style.backport(_BaseConsole.draw_rect)
1395 _BaseConsole.drawFrame = _style.backport(_BaseConsole.draw_frame)
1396 _BaseConsole.getCursor = _style.backport(_BaseConsole.get_cursor)
1397 _BaseConsole.getSize = _style.backport(_BaseConsole.get_size)
1398 _BaseConsole.getChar = _style.backport(_BaseConsole.get_char)
1399
1400 Console.getChar = _style.backport(Console.get_char)
1401
1402 Window.drawChar = _style.backport(Window.draw_char)
1403 Window.drawRect = _style.backport(Window.draw_rect)
1404 Window.drawFrame = _style.backport(Window.draw_frame)
1405 Window.getChar = _style.backport(Window.get_char)
1406
1407 setFont = _style.backport(set_font)
1408 getFullscreen = _style.backport(get_fullscreen)
1409 setFullscreen = _style.backport(set_fullscreen)
1410 setTitle = _style.backport(set_title)
1411 setFPS = _style.backport(set_fps)
1412 getFPS = _style.backport(get_fps)
1413 forceResolution = _style.backport(force_resolution)
1414
1415 __license__ = "New BSD License"
1416 __author__ = 'Kyle Stewart'
1417 __contact__ = "4b796c65+pythonTDL@gmail.com"
1418 __email__ = "4b796c65+pythonTDL@gmail.com"
1419 __version__ = '1.2.0'
1420