FastRenderSample.on_draw()   F
last analyzed

Complexity

Conditions 12

Size

Total Lines 99

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 12
dl 0
loc 99
rs 3.3927
c 3
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like FastRenderSample.on_draw() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python
2
#
3
# libtcod python samples
4
# This code demonstrates various usages of libtcod modules
5
# It's in the public domain.
6
#
7
from __future__ import division
8
9
import os
10
11
import math
12
13
import numpy as np
14
import tcod as libtcod
15
16
SAMPLE_SCREEN_WIDTH = 46
17
SAMPLE_SCREEN_HEIGHT = 20
18
SAMPLE_SCREEN_X = 20
19
SAMPLE_SCREEN_Y = 10
20
font = os.path.join('data/fonts/consolas10x10_gs_tc.png')
21
libtcod.console_set_custom_font(
22
    font, libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)
23
root_console = libtcod.console_init_root(80, 50, "tcod python sample", False)
24
sample_console = libtcod.console_new(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT)
25
26
class Sample():
27
    def __init__(self, name='', func=None):
28
        self.name = name
29
        self.func = func
30
31
    def on_enter(self):
32
        pass
33
34
    def on_draw(self, delta_time):
35
        pass
36
37
    def on_key(self, key):
38
        pass
39
40
    def on_mouse(self, mouse):
41
        pass
42
43
#############################################
44
# true color sample
45
#############################################
46
class TrueColorSample(Sample):
47
48
    def __init__(self):
49
        self.name = "True colors"
50
        # corner colors
51
        self.colors = np.array([(50, 40, 150), (240, 85, 5),
52
                                (50, 35, 240), (10, 200, 130)], dtype=np.int16)
53
        # color shift direction
54
        self.slide_dir = np.array([[1, 1, 1], [-1, -1, 1],
55
                                   [1, -1, 1], [1, 1, -1]], dtype=np.int16)
56
        # corner indexes
57
        self.corners = np.array([0, 1, 2, 3])
58
        # sample screen mesh-grid
59
        self.mgrid = np.mgrid[0:1:SAMPLE_SCREEN_HEIGHT * 1j,
60
                              0:1:SAMPLE_SCREEN_WIDTH * 1j]
61
62
    def on_enter(self):
63
        libtcod.sys_set_fps(0)
64
        sample_console.clear()
65
66
    def on_draw(self, delta_time):
67
        self.slide_corner_colors()
68
        self.interpolate_corner_colors()
69
        self.darken_background_characters()
70
        self.randomize_sample_conole()
71
        self.print_banner()
72
73
    def slide_corner_colors(self):
74
        # pick random RGB channels for each corner
75
        rand_channels = np.random.randint(low=0, high=3, size=4)
76
77
        # shift picked color channels in the direction of slide_dir
78
        self.colors[self.corners, rand_channels] += \
79
            self.slide_dir[self.corners, rand_channels] * 5
80
81
        # reverse slide_dir values when limits are reached
82
        self.slide_dir[self.colors[:] == 255] = -1
83
        self.slide_dir[self.colors[:] == 0] = 1
84
85
    def interpolate_corner_colors(self):
86
        # interpolate corner colors across the sample console
87
        for i in range(3): # for each color channel
88
            left = ((self.colors[2, i] - self.colors[0, i]) * self.mgrid[0] +
89
                    self.colors[0, i])
90
            right = ((self.colors[3, i] - self.colors[1, i]) * self.mgrid[0] +
91
                     self.colors[1, i])
92
            sample_console.bg[:, :, i] = (right - left) * self.mgrid[1] + left
93
94
    def darken_background_characters(self):
95
        # darken background characters
96
        sample_console.fg[:] = sample_console.bg[:]
97
        sample_console.fg[:] //= 2
98
99
    def randomize_sample_conole(self):
100
        # randomize sample console characters
101
        sample_console.ch[:] = np.random.randint(
102
            low=ord('a'), high=ord('z') + 1,
103
            size=sample_console.ch.size,
104
            dtype=np.intc,
105
            ).reshape(sample_console.ch.shape)
106
107
    def print_banner(self):
108
        # print text on top of samples
109
        sample_console.default_bg = libtcod.grey
110
        sample_console.print_rect(
111
            x=sample_console.width // 2, y=5,
112
            width=sample_console.width - 2, height=sample_console.height - 1,
113
            string="The Doryen library uses 24 bits colors, for both "
114
                   "background and foreground.",
115
            bg_blend=libtcod.BKGND_MULTIPLY, alignment=libtcod.CENTER,
116
            )
117
118
#############################################
119
# offscreen console sample
120
#############################################
121
122
class OffscreenConsoleSample(Sample):
123
124
    def __init__(self):
125
        self.name = 'Offscreen console'
126
        self.secondary = libtcod.console.Console(sample_console.width // 2,
127
                                                 sample_console.height // 2)
128
        self.screenshot = libtcod.console.Console(sample_console.width,
129
                                                  sample_console.height)
130
        self.counter = 0
131
        self.x = 0
132
        self.y = 0
133
        self.xdir = 1
134
        self.ydir = 1
135
136
        self.secondary.print_frame(
137
            0, 0, sample_console.width // 2, sample_console.height // 2,
138
            "Offscreen console",
139
            False,
140
            libtcod.BKGND_NONE
141
            )
142
143
        self.secondary.print_rect(
144
            sample_console.width // 4, 2,
145
            sample_console.width // 2 - 2, sample_console.height // 2,
146
            "You can render to an offscreen console and blit in on another "
147
            "one, simulating alpha transparency.",
148
            libtcod.BKGND_NONE, libtcod.CENTER
149
            )
150
151
    def on_enter(self):
152
        libtcod.sys_set_fps(0)
153
        # get a "screenshot" of the current sample screen
154
        sample_console.blit(0, 0, sample_console.width, sample_console.height,
155
                            self.screenshot, 0, 0)
156
157
    def on_draw(self, delta_time):
158
        self.counter += delta_time * 1.5
159
        if self.counter >= 1:
160
            self.counter -= 1
161
            self.x += self.xdir
162
            self.y += self.ydir
163
            if self.x == sample_console.width / 2 + 5:
164
                self.xdir = -1
165
            elif self.x == -5:
166
                self.xdir = 1
167
            if self.y == sample_console.height / 2 + 5:
168
                self.ydir = -1
169
            elif self.y == -5:
170
                self.ydir = 1
171
        self.screenshot.blit(0, 0, sample_console.width, sample_console.height,
172
                             sample_console, 0, 0)
173
        self.secondary.blit(
174
            0, 0, sample_console.width // 2, sample_console.height // 2,
175
            sample_console, self.x, self.y, 1.0, 0.75
176
            )
177
178
#############################################
179
# line drawing sample
180
#############################################
181
182
class LineDrawingSample(Sample):
183
184
    FLAG_NAMES = [
185
        'BKGND_NONE',
186
        'BKGND_SET',
187
        'BKGND_MULTIPLY',
188
        'BKGND_LIGHTEN',
189
        'BKGND_DARKEN',
190
        'BKGND_SCREEN',
191
        'BKGND_COLOR_DODGE',
192
        'BKGND_COLOR_BURN',
193
        'BKGND_ADD',
194
        'BKGND_ADDALPHA',
195
        'BKGND_BURN',
196
        'BKGND_OVERLAY',
197
        'BKGND_ALPHA',
198
        ]
199
200
    def __init__(self):
201
        self.name = 'Line drawing'
202
        self.mk_flag = libtcod.BKGND_SET
203
        self.bk_flag = libtcod.BKGND_SET
204
205
        self.bk = libtcod.console_new(sample_console.width,
206
                                      sample_console.height)
207
        # initialize the colored background
208
        for x in range(sample_console.width):
209
            for y in range(sample_console.height):
210
                col = libtcod.Color(
211
                    x * 255 // (sample_console.width - 1),
212
                    (x + y) * 255 // (sample_console.width - 1 +
213
                                      sample_console.height - 1),
214
                    y * 255 // (sample_console.height-1),
215
                )
216
                self.bk.bg[y, x] = col
217
                self.bk.ch[:] = ord(' ')
218
219
220
    def on_key(self, key):
221
        if key.vk in (libtcod.KEY_ENTER, libtcod.KEY_KPENTER):
222
            self.bk_flag += 1
223
            if (self.bk_flag & 0xff) > libtcod.BKGND_ALPH:
224
                self.bk_flag = libtcod.BKGND_NONE
225
226
    def on_enter(self):
227
        libtcod.sys_set_fps(0)
228
        libtcod.console_set_default_foreground(sample_console, libtcod.white)
229
230
    def on_draw(self, delta_time):
231
        alpha = 0.0
232
        if (self.bk_flag & 0xff) == libtcod.BKGND_ALPH:
233
            # for the alpha mode, update alpha every frame
234
            alpha = (1.0 + math.cos(libtcod.sys_elapsed_seconds() * 2)) / 2.0
235
            self.bk_flag = libtcod.BKGND_ALPHA(alpha)
236
        elif (self.bk_flag & 0xff) == libtcod.BKGND_ADDA:
237
            # for the add alpha mode, update alpha every frame
238
            alpha = (1.0 + math.cos(libtcod.sys_elapsed_seconds() * 2)) / 2.0
239
            self.bk_flag = libtcod.BKGND_ADDALPHA(alpha)
240
241
        self.bk.blit(0, 0, sample_console.width,
242
                     sample_console.height, sample_console, 0, 0)
243
        recty = int((sample_console.height - 2)
244
                    * ((1.0 + math.cos(libtcod.sys_elapsed_seconds())) / 2.0))
245
        for x in range(sample_console.width):
246
            col = libtcod.Color(x * 255 // sample_console.width,
247
                                x * 255 // sample_console.width,
248
                                x * 255 // sample_console.width)
249
            libtcod.console_set_char_background(
250
                sample_console, x, recty, col, self.bk_flag)
251
            libtcod.console_set_char_background(
252
                sample_console, x, recty + 1, col, self.bk_flag)
253
            libtcod.console_set_char_background(
254
                sample_console, x, recty + 2, col, self.bk_flag)
255
        angle = libtcod.sys_elapsed_seconds() * 2.0
256
        cos_angle = math.cos(angle)
257
        sin_angle = math.sin(angle)
258
        xo = int(sample_console.width // 2 * (1 + cos_angle))
259
        yo = int(sample_console.height // 2
260
                 + sin_angle * sample_console.width // 2)
261
        xd = int(sample_console.width // 2 * (1 - cos_angle))
262
        yd = int(sample_console.height // 2
263
                 - sin_angle * sample_console.width // 2)
264
        # draw the line
265
        # in python the easiest way is to use the line iterator
266
        for x, y in libtcod.line_iter(xo, yo, xd, yd):
267
            if 0 <= x < sample_console.width and \
268
               0 <= y < sample_console.height:
269
                libtcod.console_set_char_background(
270
                    sample_console, x, y, libtcod.light_blue, self.bk_flag)
271
        sample_console.print_(
272
            2, 2,
273
            '%s (ENTER to change)' % self.FLAG_NAMES[self.bk_flag & 0xff]
274
            )
275
276
#############################################
277
# noise sample
278
#############################################
279
280
NOISE_OPTIONS = [ # [name, algorithm, implementation],
281
    ['perlin noise', libtcod.NOISE_PERLIN, libtcod.noise.SIMPLE],
282
    ['simplex noise', libtcod.NOISE_SIMPLEX, libtcod.noise.SIMPLE],
283
    ['wavelet noise', libtcod.NOISE_WAVELET, libtcod.noise.SIMPLE],
284
    ['perlin fbm', libtcod.NOISE_PERLIN, libtcod.noise.FBM],
285
    ['perlin turbulence', libtcod.NOISE_PERLIN, libtcod.noise.TURBULENCE],
286
    ['simplex fbm', libtcod.NOISE_SIMPLEX, libtcod.noise.FBM],
287
    ['simplex turbulence',
288
     libtcod.NOISE_SIMPLEX, libtcod.noise.TURBULENCE],
289
    ['wavelet fbm', libtcod.NOISE_WAVELET, libtcod.noise.FBM],
290
    ['wavelet turbulence',
291
     libtcod.NOISE_WAVELET, libtcod.noise.TURBULENCE],
292
    ]
293
294
class NoiseSample(Sample):
295
296
    def __init__(self):
297
        self.name = 'Noise'
298
        self.func = 0
299
        self.dx = 0.0
300
        self.dy = 0.0
301
        self.octaves = 4.0
302
        self.zoom = 3.0
303
        self.hurst = libtcod.NOISE_DEFAULT_HURST
304
        self.lacunarity = libtcod.NOISE_DEFAULT_LACUNARITY
305
        self.noise = self.get_noise()
306
        self.img = libtcod.image_new(SAMPLE_SCREEN_WIDTH * 2,
307
                                     SAMPLE_SCREEN_HEIGHT * 2)
308
309
    @property
310
    def algorithm(self):
311
        return NOISE_OPTIONS[self.func][1]
312
313
    @property
314
    def implementation(self):
315
        return NOISE_OPTIONS[self.func][2]
316
317
    def get_noise(self):
318
        return libtcod.noise.Noise(
319
            2,
320
            self.algorithm,
321
            self.implementation,
322
            self.hurst,
323
            self.lacunarity,
324
            self.octaves,
325
            seed=None,
326
            )
327
328
    def on_enter(self):
329
        libtcod.sys_set_fps(0)
330
331
    def on_draw(self, delta_time):
332
        sample_console.clear()
333
        self.dx += delta_time * 0.25
334
        self.dy += delta_time * 0.25
335
        for y in range(2 * sample_console.height):
336
            for x in range(2 * sample_console.width):
337
                f = [self.zoom * x / (2 * sample_console.width) + self.dx,
338
                     self.zoom * y / (2 * sample_console.height) + self.dy]
339
                value = self.noise.get_point(*f)
340
                c = int((value + 1.0) / 2.0 * 255)
341
                c = max(0, min(c, 255))
342
                self.img.put_pixel(x, y, (c // 2, c // 2, c))
343
        sample_console.default_bg = libtcod.grey
344
        rectw = 24
345
        recth = 13
346
        if self.implementation == libtcod.noise.SIMPLE:
347
            recth = 10
348
        self.img.blit_2x(sample_console, 0, 0)
349
        sample_console.default_bg = libtcod.grey
350
        sample_console.rect(2, 2, rectw, recth, False, libtcod.BKGND_MULTIPLY)
351
        sample_console.fg[2:2+recth, 2:2+rectw] = \
352
            (sample_console.fg[2:2+recth, 2:2+rectw] *
353
             sample_console.default_bg / 255)
354
355
        for curfunc in range(len(NOISE_OPTIONS)):
356
            text = '%i : %s' % (curfunc + 1, NOISE_OPTIONS[curfunc][0])
357
            if curfunc == self.func:
358
                sample_console.default_fg = libtcod.white
359
                sample_console.default_bg = libtcod.light_blue
360
                sample_console.print_(2, 2 + curfunc, text,
361
                                      libtcod.BKGND_SET, libtcod.LEFT)
362
            else:
363
                sample_console.default_fg = libtcod.grey
364
                sample_console.print_(2, 2 + curfunc, text)
365
        sample_console.default_fg = libtcod.white
366
        sample_console.print_(2, 11, 'Y/H : zoom (%2.1f)' % self.zoom)
367
        if self.implementation != libtcod.noise.SIMPLE:
368
            sample_console.print_(2, 12, 'E/D : hurst (%2.1f)' % self.hurst)
369
            sample_console.print_(2, 13,
370
                                  'R/F : lacunarity (%2.1f)' %
371
                                  self.lacunarity)
372
            sample_console.print_(2, 14,
373
                                  'T/G : octaves (%2.1f)' % self.octaves)
374
375
    def on_key(self, key):
376
        if key.vk == libtcod.KEY_NONE:
377
            return
378
        if ord('9') >= key.c >= ord('1'):
379
            self.func = key.c - ord('1')
380
            self.noise = self.get_noise()
381
        elif key.c in (ord('E'), ord('e')):
382
            self.hurst += 0.1
383
            self.noise = self.get_noise()
384
        elif key.c in (ord('D'), ord('d')):
385
            self.hurst -= 0.1
386
            self.noise = self.get_noise()
387
        elif key.c in (ord('R'), ord('r')):
388
            self.lacunarity += 0.5
389
            self.noise = self.get_noise()
390
        elif key.c in (ord('F'), ord('f')):
391
            self.lacunarity -= 0.5
392
            self.noise = self.get_noise()
393
        elif key.c in (ord('T'), ord('t')):
394
            self.octaves += 0.5
395
            self.noise.octaves = self.octaves
396
        elif key.c in (ord('G'), ord('g')):
397
            self.octaves -= 0.5
398
            self.noise.octaves = self.octaves
399
        elif key.c in (ord('Y'), ord('y')):
400
            self.zoom += 0.2
401
        elif key.c in (ord('H'), ord('h')):
402
            self.zoom -= 0.2
403
404
#############################################
405
# field of view sample
406
#############################################
407
DARK_WALL = libtcod.Color(0, 0, 100)
408
LIGHT_WALL = libtcod.Color(130, 110, 50)
409
DARK_GROUND = libtcod.Color(50, 50, 150)
410
LIGHT_GROUND = libtcod.Color(200, 180, 50)
411
412
SAMPLE_MAP = [
413
    '##############################################',
414
    '#######################      #################',
415
    '#####################    #     ###############',
416
    '######################  ###        ###########',
417
    '##################      #####             ####',
418
    '################       ########    ###### ####',
419
    '###############      #################### ####',
420
    '################    ######                  ##',
421
    '########   #######  ######   #     #     #  ##',
422
    '########   ######      ###                  ##',
423
    '########                                    ##',
424
    '####       ######      ###   #     #     #  ##',
425
    '#### ###   ########## ####                  ##',
426
    '#### ###   ##########   ###########=##########',
427
    '#### ##################   #####          #####',
428
    '#### ###             #### #####          #####',
429
    '####           #     ####                #####',
430
    '########       #     #### #####          #####',
431
    '########       #####      ####################',
432
    '##############################################',
433
    ]
434
435
SAMPLE_MAP = np.array([list(line) for line in SAMPLE_MAP])
436
437
FOV_ALGO_NAMES = [
438
    'BASIC      ',
439
    'DIAMOND    ',
440
    'SHADOW     ',
441
    'PERMISSIVE0',
442
    'PERMISSIVE1',
443
    'PERMISSIVE2',
444
    'PERMISSIVE3',
445
    'PERMISSIVE4',
446
    'PERMISSIVE5',
447
    'PERMISSIVE6',
448
    'PERMISSIVE7',
449
    'PERMISSIVE8',
450
    'RESTRICTIVE',
451
    ]
452
453
TORCH_RADIUS = 10
454
SQUARED_TORCH_RADIUS = TORCH_RADIUS * TORCH_RADIUS
455
456
class FOVSample(Sample):
457
458
    def __init__(self):
459
        self.name = 'Field of view'
460
461
        self.px = 20
462
        self.py = 10
463
        self.recompute = True
464
        self.torch = False
465
        self.map = None
466
        self.noise = None
467
        self.torchx = 0.0
468
        self.light_walls = True
469
        self.algo_num = 0
470
        # 1d noise for the torch flickering
471
        self.noise = libtcod.noise_new(1, 1.0, 1.0)
472
473
        self.map = libtcod.map_new(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT)
474
        self.map.walkable[:] = SAMPLE_MAP[:] == ' '
475
        self.map.transparent[:] = self.map.walkable[:] | (SAMPLE_MAP == '=')
476
477
        self.light_map_bg = np.full(SAMPLE_MAP.shape + (3,), LIGHT_GROUND,
478
                                    dtype=np.uint8)
479
        self.light_map_bg[SAMPLE_MAP[:] == '#'] = LIGHT_WALL
480
        self.dark_map_bg = np.full(SAMPLE_MAP.shape + (3,), DARK_GROUND,
481
                                   dtype=np.uint8)
482
        self.dark_map_bg[SAMPLE_MAP[:] == '#'] = DARK_WALL
483
484
    def draw_ui(self):
485
        libtcod.console_set_default_foreground(sample_console, libtcod.white)
486
        libtcod.console_print(
487
            sample_console, 1, 1,
488
            "IJKL : move around\n"
489
            "T : torch fx %s\n"
490
            "W : light walls %s\n"
491
            "+-: algo %s" % ('on ' if self.torch else 'off',
492
                             'on ' if self.light_walls else 'off',
493
                             FOV_ALGO_NAMES[self.algo_num],
494
                            ),
495
            )
496
        libtcod.console_set_default_foreground(sample_console, libtcod.black)
497
498
    def on_enter(self):
499
        libtcod.sys_set_fps(60)
500
        # we draw the foreground only the first time.
501
        #  during the player movement, only the @ is redrawn.
502
        #  the rest impacts only the background color
503
        # draw the help text & player @
504
        libtcod.console_clear(sample_console)
505
        self.draw_ui()
506
        libtcod.console_put_char(sample_console, self.px, self.py, '@',
507
                                 libtcod.BKGND_NONE)
508
        # draw windows
509
        sample_console.ch[np.where(SAMPLE_MAP == '=')] = libtcod.CHAR_DHLINE
510
        sample_console.fg[np.where(SAMPLE_MAP == '=')] = libtcod.black
511
512
    def on_draw(self, delta_time):
513
        dx = 0.0
514
        dy = 0.0
515
        di = 0.0
516
        if self.recompute:
517
            self.recompute = False
518
            self.map.compute_fov(
519
                self.px,
520
                self.py,
521
                TORCH_RADIUS if self.torch else 0,
522
                self.light_walls,
523
                self.algo_num
524
                )
525
        sample_console.bg[:] = self.dark_map_bg[:]
526
        if self.torch:
527
            # slightly change the perlin noise parameter
528
            self.torchx += 0.1
529
            # randomize the light position between -1.5 and 1.5
530
            tdx = [self.torchx + 20.0]
531
            dx = libtcod.noise_get(self.noise, tdx,
532
                                   libtcod.NOISE_SIMPLEX) * 1.5
533
            tdx[0] += 30.0
534
            dy = libtcod.noise_get(self.noise, tdx,
535
                                   libtcod.NOISE_SIMPLEX) * 1.5
536
            di = 0.2 * libtcod.noise_get(self.noise, [self.torchx],
537
                                         libtcod.NOISE_SIMPLEX)
538
            #where_fov = np.where(self.map.fov[:])
539
            mgrid = np.mgrid[:SAMPLE_SCREEN_HEIGHT, :SAMPLE_SCREEN_WIDTH]
540
            # get squared distance
541
            light = ((mgrid[0] - self.py + dy) ** 2 +
542
                     (mgrid[1] - self.px + dx) ** 2)
543
            light = light.astype(np.float16)
544
            where_visible = np.where((light < SQUARED_TORCH_RADIUS) &
545
                                     self.map.fov[:])
546
            light[where_visible] = SQUARED_TORCH_RADIUS - light[where_visible]
547
            light[where_visible] /= SQUARED_TORCH_RADIUS
548
            light[where_visible] += di
549
            light[where_visible] = light[where_visible].clip(0, 1)
550
551
            for yx in zip(*where_visible):
552
                sample_console.bg[yx] = libtcod.color_lerp(
553
                    tuple(self.dark_map_bg[yx]),
554
                    tuple(self.light_map_bg[yx]),
555
                    light[yx],
556
                    )
557
        else:
558
            where_fov = np.where(self.map.fov[:])
559
            sample_console.bg[where_fov] = self.light_map_bg[where_fov]
560
561
562
    def on_key(self, key):
563
        MOVE_KEYS = {
564
            ord('i'): (0, -1),
565
            ord('j'): (-1, 0),
566
            ord('k'): (0, 1),
567
            ord('l'): (1, 0),
568
        }
569
        FOV_SELECT_KEYS = {ord('-'): -1, ord('='): 1}
570
        if key.c in MOVE_KEYS:
571
            x, y = MOVE_KEYS[key.c]
572
            if SAMPLE_MAP[self.py + y][self.px + x] == ' ':
573
                libtcod.console_put_char(sample_console, self.px, self.py, ' ',
574
                                         libtcod.BKGND_NONE)
575
                self.px += x
576
                self.py += y
577
                libtcod.console_put_char(sample_console, self.px, self.py, '@',
578
                                         libtcod.BKGND_NONE)
579
                self.recompute = True
580
        elif key.c == ord('t'):
581
            self.torch = not self.torch
582
            self.draw_ui()
583
            self.recompute = True
584
        elif key.c == ord('w'):
585
            self.light_walls = not self.light_walls
586
            self.draw_ui()
587
            self.recompute = True
588
        elif key.c in FOV_SELECT_KEYS:
589
            self.algo_num += FOV_SELECT_KEYS[key.c]
590
            self.algo_num %= libtcod.NB_FOV_ALGORITHMS
591
            self.draw_ui()
592
            self.recompute = True
593
594
#############################################
595
# pathfinding sample
596
#############################################
597
598
class PathfindingSample(Sample):
599
    def __init__(self):
600
        self.name = 'Path finding'
601
602
        self.px = 20
603
        self.py = 10
604
        self.dx = 24
605
        self.dy = 1
606
        self.map = None
607
        self.path = None
608
        self.dijk_dist = 0.0
609
        self.using_astar = True
610
        self.dijk = None
611
        self.recalculate = False
612
        self.busy = 0.0
613
        self.oldchar = ' '
614
615
        self.map = libtcod.map_new(SAMPLE_SCREEN_WIDTH, SAMPLE_SCREEN_HEIGHT)
616
        for y in range(SAMPLE_SCREEN_HEIGHT):
617
            for x in range(SAMPLE_SCREEN_WIDTH):
618
                if SAMPLE_MAP[y][x] == ' ':
619
                    # ground
620
                    libtcod.map_set_properties(self.map, x, y, True, True)
621
                elif SAMPLE_MAP[y][x] == '=':
622
                    # window
623
                    libtcod.map_set_properties(self.map, x, y, True, False)
624
        self.path = libtcod.path_new_using_map(self.map)
625
        self.dijk = libtcod.dijkstra_new(self.map)
626
627
    def on_enter(self):
628
        libtcod.sys_set_fps(60)
629
        # we draw the foreground only the first time.
630
        #  during the player movement, only the @ is redrawn.
631
        #  the rest impacts only the background color
632
        # draw the help text & player @
633
        libtcod.console_clear(sample_console)
634
        libtcod.console_set_default_foreground(sample_console, libtcod.white)
635
        libtcod.console_put_char(sample_console, self.dx, self.dy, '+',
636
                                 libtcod.BKGND_NONE)
637
        libtcod.console_put_char(sample_console, self.px, self.py, '@',
638
                                 libtcod.BKGND_NONE)
639
        libtcod.console_print(
640
            sample_console, 1, 1,
641
            "IJKL / mouse :\nmove destination\nTAB : A*/dijkstra")
642
        libtcod.console_print(sample_console, 1, 4, "Using : A*")
643
        # draw windows
644
        for y in range(SAMPLE_SCREEN_HEIGHT):
645
            for x in range(SAMPLE_SCREEN_WIDTH):
646
                if SAMPLE_MAP[y][x] == '=':
647
                    libtcod.console_put_char(sample_console, x, y,
648
                                             libtcod.CHAR_DHLINE,
649
                                             libtcod.BKGND_NONE)
650
        self.recalculate = True
651
652
    def on_draw(self, delta_time):
653
        if self.recalculate:
654
            if self.using_astar:
655
                libtcod.path_compute(self.path, self.px, self.py,
656
                                     self.dx, self.dy)
657
            else:
658
                self.dijk_dist = 0.0
659
                # compute dijkstra grid (distance from px,py)
660
                libtcod.dijkstra_compute(self.dijk, self.px, self.py)
661
                # get the maximum distance (needed for rendering)
662
                for y in range(SAMPLE_SCREEN_HEIGHT):
663
                    for x in range(SAMPLE_SCREEN_WIDTH):
664
                        d = libtcod.dijkstra_get_distance(self.dijk, x, y)
665
                        if d > self.dijk_dist:
666
                            self.dijk_dist = d
667
                # compute path from px,py to dx,dy
668
                libtcod.dijkstra_path_set(self.dijk, self.dx, self.dy)
669
            self.recalculate = False
670 View Code Duplication
            self.busy = 0.2
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
671
        # draw the dungeon
672
        for y in range(SAMPLE_SCREEN_HEIGHT):
673
            for x in range(SAMPLE_SCREEN_WIDTH):
674
                if SAMPLE_MAP[y][x] == '#':
675
                    libtcod.console_set_char_background(
676
                        sample_console, x, y, DARK_WALL, libtcod.BKGND_SET)
677
                else:
678
                    libtcod.console_set_char_background(
679
                        sample_console, x, y, DARK_GROUND, libtcod.BKGND_SET)
680
        # draw the path
681
        if self.using_astar:
682
            for i in range(libtcod.path_size(self.path)):
683
                x, y = libtcod.path_get(self.path, i)
684
                libtcod.console_set_char_background(
685
                    sample_console, x, y, LIGHT_GROUND, libtcod.BKGND_SET)
686
        else:
687
            for y in range(SAMPLE_SCREEN_HEIGHT):
688
                for x in range(SAMPLE_SCREEN_WIDTH):
689
                    if SAMPLE_MAP[y][x] != '#':
690
                        libtcod.console_set_char_background(
691
                            sample_console, x, y,
692
                            libtcod.color_lerp(
693
                                LIGHT_GROUND, DARK_GROUND,
694
                                0.9* libtcod.dijkstra_get_distance(self.dijk,
695
                                                                   x, y)
696
                                / self.dijk_dist),
697
                            libtcod.BKGND_SET,
698
                            )
699
            for i in range(libtcod.dijkstra_size(self.dijk)):
700
                x, y = libtcod.dijkstra_get(self.dijk, i)
701
                libtcod.console_set_char_background(
702
                    sample_console, x, y, LIGHT_GROUND, libtcod.BKGND_SET)
703
704
        # move the creature
705
        self.busy -= libtcod.sys_get_last_frame_length()
706
        if self.busy <= 0.0:
707
            self.busy = 0.2
708
            if self.using_astar:
709
                if not libtcod.path_is_empty(self.path):
710
                    libtcod.console_put_char(sample_console, self.px, self.py,
711
                                             ' ', libtcod.BKGND_NONE)
712
                    self.px, self.py = libtcod.path_walk(self.path, True)
713
                    libtcod.console_put_char(sample_console, self.px, self.py,
714
                                             '@', libtcod.BKGND_NONE)
715
            else:
716
                if not libtcod.dijkstra_is_empty(self.dijk):
717
                    libtcod.console_put_char(sample_console, self.px, self.py,
718
                                             ' ', libtcod.BKGND_NONE)
719
                    self.px, self.py = libtcod.dijkstra_path_walk(self.dijk)
720
                    libtcod.console_put_char(sample_console, self.px, self.py,
721
                                             '@', libtcod.BKGND_NONE)
722
                    self.recalculate = True
723
724
    def on_key(self, key):
725
        if key.c in (ord('I'), ord('i')) and self.dy > 0:
726
            # destination move north
727
            libtcod.console_put_char(sample_console, self.dx, self.dy,
728
                                     self.oldchar, libtcod.BKGND_NONE)
729
            self.dy -= 1
730
            self.oldchar = libtcod.console_get_char(sample_console, self.dx,
731
                                                    self.dy)
732
            libtcod.console_put_char(sample_console, self.dx, self.dy, '+',
733
                                     libtcod.BKGND_NONE)
734
            if SAMPLE_MAP[self.dy][self.dx] == ' ':
735
                self.recalculate = True
736
        elif (key.c in (ord('K'), ord('k'))
737
              and self.dy < SAMPLE_SCREEN_HEIGHT - 1):
738
            # destination move south
739
            libtcod.console_put_char(sample_console, self.dx, self.dy,
740
                                     self.oldchar, libtcod.BKGND_NONE)
741
            self.dy += 1
742
            self.oldchar = libtcod.console_get_char(sample_console, self.dx,
743
                                                    self.dy)
744
            libtcod.console_put_char(sample_console, self.dx, self.dy, '+',
745
                                     libtcod.BKGND_NONE)
746
            if SAMPLE_MAP[self.dy][self.dx] == ' ':
747
                self.recalculate = True
748
        elif key.c in (ord('J'), ord('j')) and self.dx > 0:
749
            # destination move west
750
            libtcod.console_put_char(sample_console, self.dx, self.dy,
751
                                     self.oldchar, libtcod.BKGND_NONE)
752
            self.dx -= 1
753
            self.oldchar = libtcod.console_get_char(sample_console, self.dx,
754
                                                    self.dy)
755
            libtcod.console_put_char(sample_console, self.dx, self.dy, '+',
756
                                     libtcod.BKGND_NONE)
757
            if SAMPLE_MAP[self.dy][self.dx] == ' ':
758
                self.recalculate = True
759
        elif (key.c in (ord('L'), ord('l')) and
760
              self.dx < SAMPLE_SCREEN_WIDTH - 1):
761
            # destination move east
762
            libtcod.console_put_char(sample_console, self.dx, self.dy,
763
                                     self.oldchar, libtcod.BKGND_NONE)
764
            self.dx += 1
765
            self.oldchar = libtcod.console_get_char(sample_console, self.dx,
766
                                                    self.dy)
767
            libtcod.console_put_char(sample_console, self.dx, self.dy, '+',
768
                                     libtcod.BKGND_NONE)
769
            if SAMPLE_MAP[self.dy][self.dx] == ' ':
770
                self.recalculate = True
771
        elif key.vk == libtcod.KEY_TAB:
772
            self.using_astar = not self.using_astar
773
            if self.using_astar:
774
                libtcod.console_print(sample_console, 1, 4, "Using : A*      ")
775
            else:
776
                libtcod.console_print(sample_console, 1, 4, "Using : Dijkstra")
777
            self.recalculate = True
778
779
    def on_mouse(self, mouse):
780
        mx = mouse.cx - SAMPLE_SCREEN_X
781
        my = mouse.cy - SAMPLE_SCREEN_Y
782
        if (0 <= mx < SAMPLE_SCREEN_WIDTH and 0 <= my < SAMPLE_SCREEN_HEIGHT
783
                and (self.dx != mx or self.dy != my)):
784
            libtcod.console_put_char(sample_console, self.dx, self.dy,
785
                                     self.oldchar, libtcod.BKGND_NONE)
786
            self.dx = mx
787
            self.dy = my
788
            self.oldchar = libtcod.console_get_char(sample_console, self.dx,
789
                                                    self.dy)
790
            libtcod.console_put_char(sample_console, self.dx, self.dy, '+',
791
                                     libtcod.BKGND_NONE)
792
            if SAMPLE_MAP[self.dy][self.dx] == ' ':
793
                self.recalculate = True
794
795
#############################################
796
# bsp sample
797
#############################################
798
bsp_depth = 8
799
bsp_min_room_size = 4
800
# a room fills a random part of the node or the maximum available space ?
801
bsp_random_room = False
802
# if true, there is always a wall on north & west side of a room
803
bsp_room_walls = True
804
bsp_map = None
805
# draw a vertical line
806
def vline(m, x, y1, y2):
807
    if y1 > y2:
808
        y1, y2 = y2, y1
809
    for y in range(y1, y2 + 1):
810
        m[x][y] = True
811
812
# draw a vertical line up until we reach an empty space
813
def vline_up(m, x, y):
814
    while y >= 0 and not m[x][y]:
815
        m[x][y] = True
816
        y -= 1
817
818
# draw a vertical line down until we reach an empty space
819
def vline_down(m, x, y):
820
    while y < SAMPLE_SCREEN_HEIGHT and not m[x][y]:
821
        m[x][y] = True
822
        y += 1
823
824
# draw a horizontal line
825 View Code Duplication
def hline(m, x1, y, x2):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
826
    if x1 > x2:
827
        x1, x2 = x2, x1
828
    for x in range(x1, x2 + 1):
829
        m[x][y] = True
830
831
# draw a horizontal line left until we reach an empty space
832
def hline_left(m, x, y):
833
    while x >= 0 and not m[x][y]:
834
        m[x][y] = True
835
        x -= 1
836
837
# draw a horizontal line right until we reach an empty space
838
def hline_right(m, x, y):
839
    while x < SAMPLE_SCREEN_WIDTH and not m[x][y]:
840
        m[x][y] = True
841
        x += 1
842
843
# the class building the dungeon from the bsp nodes
844
def traverse_node(node, *dat):
845
    global bsp_map
846
    if libtcod.bsp_is_leaf(node):
847
        # calculate the room size
848
        minx = node.x + 1
849
        maxx = node.x + node.w - 1
850
        miny = node.y + 1
851
        maxy = node.y + node.h - 1
852
        if not bsp_room_walls:
853
            if minx > 1:
854
                minx -= 1
855
            if miny > 1:
856
                miny -= 1
857
        if maxx == SAMPLE_SCREEN_WIDTH - 1:
858
            maxx -= 1
859
        if maxy == SAMPLE_SCREEN_HEIGHT - 1:
860
            maxy -= 1
861
        if bsp_random_room:
862
            minx = libtcod.random_get_int(None,
863
                                          minx, maxx - bsp_min_room_size + 1)
864
            miny = libtcod.random_get_int(None,
865
                                          miny, maxy - bsp_min_room_size + 1)
866
            maxx = libtcod.random_get_int(None,
867
                                          minx + bsp_min_room_size - 1, maxx)
868
            maxy = libtcod.random_get_int(None,
869
                                          miny + bsp_min_room_size - 1, maxy)
870
        # resize the node to fit the room
871
        node.x = minx
872
        node.y = miny
873
        node.w = maxx - minx + 1
874
        node.h = maxy - miny + 1
875
        # dig the room
876
        for x in range(minx, maxx + 1):
877
            for y in range(miny, maxy + 1):
878
                bsp_map[x][y] = True
879
    else:
880
        # resize the node to fit its sons
881
        left = libtcod.bsp_left(node)
882
        right = libtcod.bsp_right(node)
883
        node.x = min(left.x, right.x)
884
        node.y = min(left.y, right.y)
885
        node.w = max(left.x + left.w, right.x + right.w) - node.x
886
        node.h = max(left.y + left.h, right.y + right.h) - node.y
887
        # create a corridor between the two lower nodes
888
        if node.horizontal:
889
            # vertical corridor
890
            if left.x + left.w - 1 < right.x or right.x + right.w - 1 < left.x:
891
                # no overlapping zone. we need a Z shaped corridor
892
                x1 = libtcod.random_get_int(None, left.x, left.x + left.w - 1)
893
                x2 = libtcod.random_get_int(None,
894
                                            right.x, right.x + right.w - 1)
895
                y = libtcod.random_get_int(None, left.y + left.h, right.y)
896
                vline_up(bsp_map, x1, y - 1)
897
                hline(bsp_map, x1, y, x2)
898
                vline_down(bsp_map, x2, y + 1)
899
            else:
900
                # straight vertical corridor
901
                minx = max(left.x, right.x)
902
                maxx = min(left.x + left.w - 1, right.x + right.w - 1)
903
                x = libtcod.random_get_int(None, minx, maxx)
904
                vline_down(bsp_map, x, right.y)
905
                vline_up(bsp_map, x, right.y - 1)
906
        else:
907
            # horizontal corridor
908
            if left.y + left.h - 1 < right.y or right.y + right.h - 1 < left.y:
909
                # no overlapping zone. we need a Z shaped corridor
910
                y1 = libtcod.random_get_int(None, left.y, left.y + left.h - 1)
911 View Code Duplication
                y2 = libtcod.random_get_int(None,
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
912
                                            right.y, right.y + right.h - 1)
913
                x = libtcod.random_get_int(None, left.x + left.w, right.x)
914
                hline_left(bsp_map, x - 1, y1)
915
                vline(bsp_map, x, y1, y2)
916
                hline_right(bsp_map, x + 1, y2)
917
            else:
918
                # straight horizontal corridor
919
                miny = max(left.y, right.y)
920
                maxy = min(left.y + left.h - 1, right.y + right.h - 1)
921
                y = libtcod.random_get_int(None, miny, maxy)
922
                hline_left(bsp_map, right.x - 1, y)
923
                hline_right(bsp_map, right.x, y)
924
    return True
925
926
bsp = None
927
bsp_generate = True
928
bsp_refresh = False
929
class BSPSample(Sample):
930
    def __init__(self):
931
        self.name = 'Bsp toolkit'
932
933
    def on_draw(self, delta_time):
934
        global bsp, bsp_generate, bsp_refresh, bsp_map
935
        global bsp_random_room, bsp_room_walls, bsp_depth, bsp_min_room_size
936
        if bsp_generate or bsp_refresh:
937
            # dungeon generation
938
            if bsp is None:
939
                # create the bsp
940
                bsp = libtcod.bsp_new_with_size(0, 0, SAMPLE_SCREEN_WIDTH,
941
                                                SAMPLE_SCREEN_HEIGHT)
942
            else:
943
                # restore the nodes size
944
                libtcod.bsp_resize(bsp, 0, 0, SAMPLE_SCREEN_WIDTH,
945
                                   SAMPLE_SCREEN_HEIGHT)
946
            bsp_map = list()
947
            for x in range(SAMPLE_SCREEN_WIDTH):
948
                bsp_map.append([False] * SAMPLE_SCREEN_HEIGHT)
949
            if bsp_generate:
950
                # build a new random bsp tree
951
                libtcod.bsp_remove_sons(bsp)
952
                if bsp_room_walls:
953
                    libtcod.bsp_split_recursive(bsp, 0, bsp_depth,
954
                                                bsp_min_room_size + 1,
955
                                                bsp_min_room_size + 1, 1.5, 1.5)
956
                else:
957
                    libtcod.bsp_split_recursive(bsp, 0, bsp_depth,
958
                                                bsp_min_room_size,
959
                                                bsp_min_room_size, 1.5, 1.5)
960
            # create the dungeon from the bsp
961
            libtcod.bsp_traverse_inverted_level_order(bsp, traverse_node)
962
            bsp_generate = False
963
            bsp_refresh = False
964
        libtcod.console_clear(sample_console)
965
        libtcod.console_set_default_foreground(sample_console, libtcod.white)
966
        rooms = 'OFF'
967
        if bsp_random_room:
968
            rooms = 'ON'
969
        libtcod.console_print(sample_console, 1, 1,
970 View Code Duplication
                              "ENTER : rebuild bsp\n"
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
971
                              "SPACE : rebuild dungeon\n"
972
                              "+-: bsp depth %d\n"
973
                              "*/: room size %d\n"
974
                              "1 : random room size %s"
975
                              % (bsp_depth, bsp_min_room_size, rooms))
976
        if bsp_random_room:
977
            walls = 'OFF'
978
            if bsp_room_walls:
979
                walls = 'ON'
980
            libtcod.console_print(sample_console, 1, 6,
981
                                  '2 : room walls %s' % walls)
982
        # render the level
983
        for y in range(SAMPLE_SCREEN_HEIGHT):
984
            for x in range(SAMPLE_SCREEN_WIDTH):
985
                if not bsp_map[x][y]:
986
                    libtcod.console_set_char_background(
987
                        sample_console, x, y, DARK_WALL, libtcod.BKGND_SET)
988
                else:
989
                    libtcod.console_set_char_background(
990
                        sample_console, x, y, DARK_GROUND, libtcod.BKGND_SET)
991
992
    def on_key(self, key):
993
        global bsp, bsp_generate, bsp_refresh, bsp_map
994
        global bsp_random_room, bsp_room_walls, bsp_depth, bsp_min_room_size
995
        if key.vk in (libtcod.KEY_ENTER, libtcod.KEY_KPENTER):
996
            bsp_generate = True
997
        elif key.c == ord(' '):
998
            bsp_refresh = True
999
        elif key.c == ord('='):
1000
            bsp_depth += 1
1001
            bsp_generate = True
1002
        elif key.c == ord('-') and bsp_depth > 1:
1003
            bsp_depth -= 1
1004
            bsp_generate = True
1005
        elif key.c == ord('*'):
1006
            bsp_min_room_size += 1
1007
            bsp_generate = True
1008
        elif key.c == ord('/') and bsp_min_room_size > 2:
1009
            bsp_min_room_size -= 1
1010
            bsp_generate = True
1011
        elif key.c == ord('1') or key.vk in (libtcod.KEY_1, libtcod.KEY_KP1):
1012
            bsp_random_room = not bsp_random_room
1013
            if not bsp_random_room:
1014
                bsp_room_walls = True
1015
            bsp_refresh = True
1016
        elif key.c == ord('2') or key.vk in (libtcod.KEY_2, libtcod.KEY_KP2):
1017
            bsp_room_walls = not bsp_room_walls
1018
            bsp_refresh = True
1019
1020
#############################################
1021
# image sample
1022
#############################################
1023
img_blue = libtcod.Color(0, 0, 255)
1024
img_green = libtcod.Color(0, 255, 0)
1025
class ImageSample(Sample):
1026
    def __init__(self):
1027
        self.name = 'Image toolkit'
1028
1029
        self.img = libtcod.image_load('data/img/skull.png')
1030
        self.img.set_key_color(libtcod.black)
1031
        self.circle = libtcod.image_load('data/img/circle.png')
1032
1033
    def on_enter(self):
1034
        libtcod.sys_set_fps(0)
1035
1036
    def on_draw(self, delta_time):
1037
        sample_console.default_bg = libtcod.black
1038
        sample_console.clear()
1039
        x = (sample_console.width / 2
1040
             + math.cos(libtcod.sys_elapsed_seconds()) * 10.0)
1041
        y = float(sample_console.height / 2)
1042
        scalex = (0.2 + 1.8
1043
                  * (1.0 + math.cos(libtcod.sys_elapsed_seconds() / 2)) / 2.0)
1044
        scaley = scalex
1045
        angle = libtcod.sys_elapsed_seconds()
1046
        elapsed = libtcod.sys_elapsed_milli() // 2000
1047
        if elapsed & 1 != 0:
1048
            # split the color channels of circle.png
1049
            # the red channel
1050
            sample_console.default_bg = libtcod.red
1051
1052
            sample_console.rect(0, 3, 15, 15, False, libtcod.BKGND_SET)
1053
            self.circle.blit_rect(sample_console, 0, 3, -1, -1,
1054
                                  libtcod.BKGND_MULTIPLY)
1055
            # the green channel
1056
            sample_console.default_bg = img_green
1057
            sample_console.rect(15, 3, 15, 15, False, libtcod.BKGND_SET)
1058
            self.circle.blit_rect(sample_console,
1059
                                  15, 3, -1, -1, libtcod.BKGND_MULTIPLY)
1060
            # the blue channel
1061
            sample_console.default_bg = img_blue
1062
            sample_console.rect(30, 3, 15, 15, False, libtcod.BKGND_SET)
1063
            self.circle.blit_rect(sample_console,
1064
                                  30, 3, -1, -1, libtcod.BKGND_MULTIPLY)
1065
        else:
1066
            # render circle.png with normal blitting
1067
            self.circle.blit_rect(sample_console,
1068
                                  0, 3, -1, -1, libtcod.BKGND_SET)
1069
            self.circle.blit_rect(sample_console,
1070
                                  15, 3, -1, -1, libtcod.BKGND_SET)
1071
            self.circle.blit_rect(sample_console,
1072
                                  30, 3, -1, -1, libtcod.BKGND_SET)
1073
        self.img.blit(sample_console, x, y,
1074
                      libtcod.BKGND_SET, scalex, scaley, angle)
1075
1076
#############################################
1077
# mouse sample
1078
#############################################
1079
butstatus = ('OFF', 'ON')
1080
1081
class MouseSample(Sample):
1082
    def __init__(self):
1083
        self.name = 'Mouse support'
1084
1085
        self.lbut = self.mbut = self.rbut = 0
1086
1087
    def on_enter(self):
1088
        libtcod.console_set_default_background(sample_console, libtcod.grey)
1089
        libtcod.console_set_default_foreground(sample_console,
1090
                                               libtcod.light_yellow)
1091
        libtcod.mouse_move(320, 200)
1092
        libtcod.mouse_show_cursor(True)
1093
        libtcod.sys_set_fps(60)
1094
1095
    def on_mouse(self, mouse):
1096
        libtcod.console_clear(sample_console)
1097
        if mouse.lbutton_pressed:
1098
            self.lbut = not self.lbut
1099
        if mouse.rbutton_pressed:
1100
            self.rbut = not self.rbut
1101
        if mouse.mbutton_pressed:
1102
            self.mbut = not self.mbut
1103
        wheel = ""
1104
        if mouse.wheel_up:
1105
            wheel = "UP"
1106
        elif mouse.wheel_down:
1107
            wheel = "DOWN"
1108
        sample_console.print_(
1109
            1, 1,
1110
            "Mouse position : %4dx%4d\n"
1111
            "Mouse cell     : %4dx%4d\n"
1112
            "Mouse movement : %4dx%4d\n"
1113
            "Left button    : %s (toggle %s)\n"
1114
            "Right button   : %s (toggle %s)\n"
1115
            "Middle button  : %s (toggle %s)\n"
1116
            "Wheel          : %s"
1117
            % (mouse.x, mouse.y,
1118
               mouse.cx, mouse.cy,
1119
               mouse.dx, mouse.dy,
1120
               butstatus[mouse.lbutton], butstatus[self.lbut],
1121
               butstatus[mouse.rbutton], butstatus[self.rbut],
1122
               butstatus[mouse.mbutton], butstatus[self.mbut],
1123
               wheel,
1124
              )
1125
            )
1126
        sample_console.print_(1, 10, "1 : Hide cursor\n2 : Show cursor")
1127
1128
    def on_key(self, key):
1129
        if key.c == ord('1'):
1130
            libtcod.mouse_show_cursor(False)
1131
        elif key.c == ord('2'):
1132
            libtcod.mouse_show_cursor(True)
1133
1134
#############################################
1135
# name generator sample
1136
#############################################
1137
1138
class NameGeneratorSample(Sample):
1139
    def __init__(self):
1140
        self.name = 'Name generator'
1141
1142
        self.curset = 0
1143
        self.nbsets = 0
1144
        self.delay = 0.0
1145
        self.names = []
1146
        self.sets = None
1147
1148
    def on_enter(self):
1149
        libtcod.sys_set_fps(60)
1150
1151
    def on_draw(self, delta_time):
1152
        if self.nbsets == 0:
1153
            # parse all *.cfg files in data/namegen
1154
            for file in os.listdir(b'data/namegen'):
1155
                if file.find(b'.cfg') > 0:
1156
                    libtcod.namegen_parse(
1157
                        os.path.join(b'data', b'namegen', file))
1158
            # get the sets list
1159
            self.sets = libtcod.namegen_get_sets()
1160
            print(self.sets)
1161
            self.nbsets = len(self.sets)
1162
        while len(self.names) > 15:
1163
            self.names.pop(0)
1164
        libtcod.console_clear(sample_console)
1165
        libtcod.console_set_default_foreground(sample_console, libtcod.white)
1166
        libtcod.console_print(sample_console, 1, 1,
1167
                              "%s\n\n+ : next generator\n- : prev generator"
1168
                              % self.sets[self.curset])
1169
        for i in range(len(self.names)):
1170
            libtcod.console_print_ex(
1171
                sample_console, SAMPLE_SCREEN_WIDTH-2, 2+i,
1172
                libtcod.BKGND_NONE, libtcod.RIGHT, self.names[i])
1173
        self.delay += libtcod.sys_get_last_frame_length()
1174
        if self.delay > 0.5:
1175
            self.delay -= 0.5
1176
            self.names.append(libtcod.namegen_generate(self.sets[self.curset]))
1177
1178
    def on_key(self, key):
1179
        if key.c == ord('='):
1180
            self.curset += 1
1181
            if self.curset == self.nbsets:
1182
                self.curset = 0
1183
            self.names.append("======")
1184
        elif key.c == ord('-'):
1185
            self.curset -= 1
1186
            if self.curset < 0:
1187
                self.curset = self.nbsets-1
1188
            self.names.append("======")
1189
1190
#############################################
1191
# python fast render sample
1192
#############################################
1193
numpy_available = True
1194
1195
use_numpy = numpy_available  #default option
1196
SCREEN_W = SAMPLE_SCREEN_WIDTH
1197
SCREEN_H = SAMPLE_SCREEN_HEIGHT
1198
HALF_W = SCREEN_W // 2
1199
HALF_H = SCREEN_H // 2
1200
RES_U = 80  #texture resolution
1201
RES_V = 80
1202
TEX_STRETCH = 5  #texture stretching with tunnel depth
1203
SPEED = 15
1204
LIGHT_BRIGHTNESS = 3.5  #brightness multiplier for all lights (changes their radius)
1205
LIGHTS_CHANCE = 0.07  #chance of a light appearing
1206
MAX_LIGHTS = 6
1207
MIN_LIGHT_STRENGTH = 0.2
1208
LIGHT_UPDATE = 0.05  #how much the ambient light changes to reflect current light sources
1209
AMBIENT_LIGHT = 0.8  #brightness of tunnel texture
1210
1211
#the coordinates of all tiles in the screen, as numpy arrays. example: (4x3 pixels screen)
1212
#xc = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]
1213
#yc = [[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]]
1214
if numpy_available:
1215
    (xc, yc) = np.meshgrid(range(SCREEN_W), range(SCREEN_H))
1216
    #translate coordinates of all pixels to center
1217
    xc = xc - HALF_W
1218
    yc = yc - HALF_H
1219
1220
noise2d = libtcod.noise_new(2, 0.5, 2.0)
1221
if numpy_available:  #the texture starts empty
1222
    texture = np.zeros((RES_U, RES_V))
1223
1224
class Light:
1225
    def __init__(self, x, y, z, r, g, b, strength):
1226
        self.x, self.y, self.z = x, y, z  #pos.
1227
        self.r, self.g, self.b = r, g, b  #color
1228
        self.strength = strength  #between 0 and 1, defines brightness
1229
1230
class FastRenderSample(Sample):
1231
    def __init__(self):
1232
        self.name = 'Python fast render'
1233
1234
    def on_enter(self):
1235
        global frac_t, abs_t, lights, tex_r, tex_g, tex_b
1236
        libtcod.sys_set_fps(0)
1237
        libtcod.console_clear(sample_console)  #render status message
1238
        libtcod.console_set_default_foreground(sample_console, libtcod.white)
1239
        libtcod.console_print(sample_console, 1, SCREEN_H - 3,
1240
                              "Renderer: NumPy")
1241
1242
        frac_t = RES_V - 1  #time is represented in number of pixels of the texture, start later in time to initialize texture
1243
        abs_t = RES_V - 1
1244
        lights = []  #lights list, and current color of the tunnel texture
1245
        tex_r, tex_g, tex_b = 0, 0, 0
1246
1247
    def on_draw(self, delta_time):
1248
        global use_numpy, frac_t, abs_t, lights, tex_r, tex_g, tex_b, xc, yc, texture, texture2, brightness2, R2, G2, B2
1249
1250
        time_delta = libtcod.sys_get_last_frame_length() * SPEED  #advance time
1251
        frac_t += time_delta  #increase fractional (always < 1.0) time
1252
        abs_t += time_delta  #increase absolute elapsed time
1253
        int_t = int(frac_t)  #integer time units that passed this frame (number of texture pixels to advance)
1254
        frac_t -= int_t  #keep this < 1.0
1255
1256
        #change texture color according to presence of lights (basically, sum them
1257
        #to get ambient light and smoothly change the current color into that)
1258
        ambient_r = (AMBIENT_LIGHT
1259
                     * sum(light.r * light.strength for light in lights))
1260
        ambient_g = (AMBIENT_LIGHT
1261
                     * sum(light.g * light.strength for light in lights))
1262
        ambient_b = (AMBIENT_LIGHT
1263
                     * sum(light.b * light.strength for light in lights))
1264
        alpha = LIGHT_UPDATE * time_delta
1265
        tex_r = tex_r * (1 - alpha) + ambient_r * alpha
1266
        tex_g = tex_g * (1 - alpha) + ambient_g * alpha
1267
        tex_b = tex_b * (1 - alpha) + ambient_b * alpha
1268
1269
        if int_t >= 1:  #roll texture (ie, advance in tunnel) according to int_t
1270
            int_t = int_t % RES_V  #can't roll more than the texture's size (can happen when time_delta is large)
1271
            int_abs_t = int(abs_t)  #new pixels are based on absolute elapsed time
1272
1273
            texture = np.roll(texture, -int_t, 1)
1274
            #replace new stretch of texture with new values
1275
            for v in range(RES_V - int_t, RES_V):
1276
                for u in range(0, RES_U):
1277
                    tex_v = (v + int_abs_t) / float(RES_V)
1278
                    texture[u, v] = (
1279
                        libtcod.noise_get_fbm(
1280
                            noise2d, [u/float(RES_U), tex_v], 32.0)
1281
                        + libtcod.noise_get_fbm(
1282
                            noise2d, [1 - u/float(RES_U), tex_v], 32.0)
1283
                        )
1284
1285
        # squared distance from center,
1286
        # clipped to sensible minimum and maximum values
1287
        sqr_dist = xc ** 2 + yc ** 2
1288
        sqr_dist = sqr_dist.clip(1.0 / RES_V, RES_V ** 2)
1289
1290
        #one coordinate into the texture, represents depth in the tunnel
1291
        v = TEX_STRETCH * float(RES_V) / sqr_dist + frac_t
1292
        v = v.clip(0, RES_V - 1)
1293
1294
        #another coordinate, represents rotation around the tunnel
1295
        u = np.mod(RES_U * (np.arctan2(yc, xc) / (2 * np.pi) + 0.5), RES_U)
1296
1297
        #retrieve corresponding pixels from texture
1298
        brightness = texture[u.astype(int), v.astype(int)] / 4.0 + 0.5
1299
1300
        #use the brightness map to compose the final color of the tunnel
1301
        R = brightness * tex_r
1302
        G = brightness * tex_g
1303
        B = brightness * tex_b
1304
1305
        #create new light source
1306
        if (libtcod.random_get_float(0, 0, 1) <= time_delta * LIGHTS_CHANCE and
1307
                len(lights) < MAX_LIGHTS):
1308
            x = libtcod.random_get_float(0, -0.5, 0.5)
1309
            y = libtcod.random_get_float(0, -0.5, 0.5)
1310
            strength = libtcod.random_get_float(0, MIN_LIGHT_STRENGTH, 1.0)
1311
1312
            color = libtcod.Color(0, 0, 0)  #create bright colors with random hue
1313
            hue = libtcod.random_get_float(0, 0, 360)
1314
            libtcod.color_set_hsv(color, hue, 0.5, strength)
1315
            lights.append(Light(x, y, TEX_STRETCH,
1316
                                color.r, color.g, color.b, strength))
1317
1318
        #eliminate lights that are going to be out of view
1319
        lights = [light for light in lights
1320
                  if light.z - time_delta > 1.0 / RES_V]
1321
1322
        for light in lights:  #render lights
1323
            #move light's Z coordinate with time, then project its XYZ coordinates to screen-space
1324
            light.z -= float(time_delta) / TEX_STRETCH
1325
            xl = light.x / light.z * SCREEN_H
1326
            yl = light.y / light.z * SCREEN_H
1327
1328
            #calculate brightness of light according to distance from viewer and strength,
1329
            #then calculate brightness of each pixel with inverse square distance law
1330
            light_brightness = (LIGHT_BRIGHTNESS * light.strength
1331
                                * (1.0 - light.z / TEX_STRETCH))
1332
            brightness = light_brightness / ((xc - xl) ** 2 + (yc - yl) ** 2)
1333
1334
            #make all pixels shine around this light
1335
            R += brightness * light.r
1336
            G += brightness * light.g
1337
            B += brightness * light.b
1338
1339
        #truncate values
1340
        R = R.clip(0, 255)
1341
        G = G.clip(0, 255)
1342
        B = B.clip(0, 255)
1343
1344
        #fill the screen with these background colors
1345
        sample_console.bg.transpose()[:] = [R.T, G.T, B.T]
1346
1347
#############################################
1348
# main loop
1349
#############################################
1350
1351
RENDERER_KEYS = {
1352
    libtcod.KEY_F1: libtcod.RENDERER_GLSL,
1353
    libtcod.KEY_F2: libtcod.RENDERER_OPENGL,
1354
    libtcod.KEY_F3: libtcod.RENDERER_SDL,
1355
    }
1356
1357
RENDERER_NAMES = ('F1 GLSL   ', 'F2 OPENGL ', 'F3 SDL    ')
1358
1359
SAMPLES = (
1360
    TrueColorSample(),
1361
    OffscreenConsoleSample(),
1362
    LineDrawingSample(),
1363
    NoiseSample(),
1364
    FOVSample(),
1365
    PathfindingSample(),
1366
    BSPSample(),
1367
    ImageSample(),
1368
    MouseSample(),
1369
    NameGeneratorSample(),
1370
    FastRenderSample()
1371
    )
1372
1373
cur_sample = 0
1374 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1375
def main():
1376
    global cur_sample
1377
    credits_end = False
1378
    SAMPLES[cur_sample].on_enter()
1379
    draw_samples_menu()
1380
    draw_renderer_menu()
1381
1382
    while not libtcod.console_is_window_closed():
1383
        root_console.default_fg = (255, 255, 255)
1384
        root_console.default_bg = (0, 0, 0)
1385
        # render credits
1386
        if not credits_end:
1387
            root_console.clear()
1388
            draw_samples_menu()
1389
            draw_renderer_menu()
1390
            credits_end = libtcod.console_credits_render(60, 43, 0)
1391
1392
        # render the sample
1393
        SAMPLES[cur_sample].on_draw(libtcod.sys_get_last_frame_length())
1394
        sample_console.blit(0, 0, sample_console.width, sample_console.height,
1395
                            root_console, SAMPLE_SCREEN_X, SAMPLE_SCREEN_Y)
1396
        draw_stats()
1397
        handle_events()
1398
        libtcod.console_flush()
1399
1400
def handle_events():
1401
    global cur_sample
1402
    key = libtcod.Key()
1403
    mouse = libtcod.Mouse()
1404
    EVENT_MASK = libtcod.EVENT_MOUSE | libtcod.EVENT_KEY_PRESS
1405
    while libtcod.sys_check_for_event(EVENT_MASK, key, mouse):
1406
        SAMPLES[cur_sample].on_mouse(mouse)
1407
        SAMPLES[cur_sample].on_key(key)
1408
        # key handler
1409
        if key.vk == libtcod.KEY_DOWN:
1410
            cur_sample = (cur_sample + 1) % len(SAMPLES)
1411
            SAMPLES[cur_sample].on_enter()
1412
            draw_samples_menu()
1413
        elif key.vk == libtcod.KEY_UP:
1414
            cur_sample = (cur_sample - 1) % len(SAMPLES)
1415
            SAMPLES[cur_sample].on_enter()
1416
            draw_samples_menu()
1417
        elif key.vk == libtcod.KEY_ENTER and key.lalt:
1418
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
1419
        elif key.vk == libtcod.KEY_PRINTSCREEN or key.c == 'p':
1420
            print("screenshot")
1421
            if key.lalt:
1422
                libtcod.console_save_apf(None, "samples.apf")
1423
                print("apf")
1424
            else:
1425
                libtcod.sys_save_screenshot()
1426
                print("png")
1427
        elif key.vk == libtcod.KEY_ESCAPE:
1428
            raise SystemExit()
1429
        elif key.vk in RENDERER_KEYS:
1430
            libtcod.sys_set_renderer(RENDERER_KEYS[key.vk])
1431
            draw_renderer_menu()
1432
1433
def draw_samples_menu():
1434
    for i, sample in enumerate(SAMPLES):
1435
        if i == cur_sample:
1436
            root_console.default_fg = libtcod.white
1437
            root_console.default_bg = libtcod.light_blue
1438
        else:
1439
            root_console.default_fg = libtcod.grey
1440
            root_console.default_bg = libtcod.black
1441
        root_console.print_(2, 46 - (len(SAMPLES) - i),
1442
                            '  %s' % sample.name.ljust(19),
1443
                            libtcod.BKGND_SET, libtcod.LEFT)
1444
1445
def draw_stats():
1446
    root_console.default_fg = libtcod.grey
1447
    root_console.print_(
1448
        79, 46,
1449
        ' last frame : %3d ms (%3d fps)' % (
1450
            libtcod.sys_get_last_frame_length() * 1000.0,
1451
            libtcod.sys_get_fps(),
1452
            ),
1453
        libtcod.BKGND_NONE, libtcod.RIGHT
1454
        )
1455
    root_console.print_(
1456
        79, 47,
1457
        'elapsed : %8d ms %4.2fs' % (libtcod.sys_elapsed_milli(),
1458
                                     libtcod.sys_elapsed_seconds()),
1459
        libtcod.BKGND_NONE, libtcod.RIGHT,
1460
        )
1461
1462
def draw_renderer_menu():
1463
    current_renderer = libtcod.sys_get_renderer()
1464
    root_console.default_fg = libtcod.grey
1465
    root_console.default_bg = libtcod.black
1466
    root_console.print_(42, 46 - (libtcod.NB_RENDERERS + 1),
1467
                        "Renderer :", libtcod.BKGND_SET, libtcod.LEFT)
1468
    for i, name in enumerate(RENDERER_NAMES):
1469
        if i == current_renderer:
1470
            root_console.default_fg = libtcod.white
1471
            root_console.default_bg = libtcod.light_blue
1472
        else:
1473
            root_console.default_fg = libtcod.grey
1474
            root_console.default_bg = libtcod.black
1475
        root_console.print_(
1476
            42, 46 - (libtcod.NB_RENDERERS - i),
1477
            name, libtcod.BKGND_SET, libtcod.LEFT
1478
            )
1479
1480
if __name__ == '__main__':
1481
    main()
1482