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