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