@@ -537,31 +537,26 @@ class FOVSample(Sample):
537537 def __init__ (self ):
538538 self .name = "Field of view"
539539
540- self .px = 20
541- self .py = 10
542- self .recompute = True
540+ self .player_x = 20
541+ self .player_y = 10
543542 self .torch = False
544- self .map = None
545- self .noise = None
546- self .torchx = 0.0
547543 self .light_walls = True
548544 self .algo_num = 0
549- # 1d noise for the torch flickering
550- self .noise = tcod .noise_new (1 , 1.0 , 1.0 )
545+ self .noise = tcod .noise .Noise (1 ) # 1D noise for the torch flickering.
551546
552- self .map = tcod .map .Map (
553- SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT , order = "F"
554- )
555- self .map .walkable [:] = SAMPLE_MAP [:] == " "
556- self .map .transparent [:] = self .map .walkable [:] | (SAMPLE_MAP == "=" )
547+ map_shape = (SAMPLE_SCREEN_WIDTH , SAMPLE_SCREEN_HEIGHT )
557548
558- self .light_map_bg = np .full (
559- SAMPLE_MAP .shape + (3 ,), LIGHT_GROUND , dtype = np .uint8
560- )
549+ self .walkable = np .zeros (map_shape , dtype = bool , order = "F" )
550+ self .walkable [:] = SAMPLE_MAP [:] == " "
551+
552+ self .transparent = np .zeros (map_shape , dtype = bool , order = "F" )
553+ self .transparent [:] = self .walkable [:] | (SAMPLE_MAP == "=" )
554+
555+ # Lit background colors for the map.
556+ self .light_map_bg = np .full (SAMPLE_MAP .shape , LIGHT_GROUND , dtype = "3B" )
561557 self .light_map_bg [SAMPLE_MAP [:] == "#" ] = LIGHT_WALL
562- self .dark_map_bg = np .full (
563- SAMPLE_MAP .shape + (3 ,), DARK_GROUND , dtype = np .uint8
564- )
558+ # Dark background colors for the map.
559+ self .dark_map_bg = np .full (SAMPLE_MAP .shape , DARK_GROUND , dtype = "3B" )
565560 self .dark_map_bg [SAMPLE_MAP [:] == "#" ] = DARK_WALL
566561
567562 def draw_ui (self ):
@@ -583,64 +578,64 @@ def draw_ui(self):
583578
584579 def on_enter (self ):
585580 tcod .sys_set_fps (60 )
586- # we draw the foreground only the first time.
587- # during the player movement, only the @ is redrawn.
588- # the rest impacts only the background color
589- # draw the help text & player @
581+
582+ def on_draw (self ):
590583 sample_console .clear ()
584+ # Draw the help text & player @.
591585 self .draw_ui ()
592- tcod .console_put_char (
593- sample_console , self .px , self .py , "@" , tcod .BKGND_NONE
586+ sample_console .print (self .player_x , self .player_y , "@" )
587+ # Draw windows.
588+ sample_console .tiles_rgb ["ch" ][SAMPLE_MAP == "=" ] = tcod .CHAR_DHLINE
589+ sample_console .tiles_rgb ["fg" ][SAMPLE_MAP == "=" ] = tcod .black
590+
591+ # Get a 2D boolean array of visible cells.
592+ fov = tcod .map .compute_fov (
593+ transparency = self .transparent ,
594+ pov = (self .player_x , self .player_y ),
595+ radius = TORCH_RADIUS if self .torch else 0 ,
596+ light_walls = self .light_walls ,
597+ algorithm = self .algo_num ,
594598 )
595- # draw windows
596- sample_console .ch [np .where (SAMPLE_MAP == "=" )] = tcod .CHAR_DHLINE
597- sample_console .fg [np .where (SAMPLE_MAP == "=" )] = tcod .black
598599
599- def on_draw (self ):
600- dx = 0.0
601- dy = 0.0
602- di = 0.0
603- if self .recompute :
604- self .recompute = False
605- self .map .compute_fov (
606- self .px ,
607- self .py ,
608- TORCH_RADIUS if self .torch else 0 ,
609- self .light_walls ,
610- self .algo_num ,
611- )
612- sample_console .bg [:] = self .dark_map_bg [:]
613600 if self .torch :
614- # slightly change the perlin noise parameter
615- self .torchx += 0.1
616- # randomize the light position between -1.5 and 1.5
617- tdx = [self .torchx + 20.0 ]
618- dx = tcod .noise_get (self .noise , tdx , tcod .NOISE_SIMPLEX ) * 1.5
619- tdx [0 ] += 30.0
620- dy = tcod .noise_get (self .noise , tdx , tcod .NOISE_SIMPLEX ) * 1.5
621- di = 0.2 * tcod .noise_get (
622- self .noise , [self .torchx ], tcod .NOISE_SIMPLEX
601+ # Derive the touch from noise based on the current time.
602+ torch_t = time .perf_counter () * 5
603+ # Randomize the light position between -1.5 and 1.5
604+ torch_x = self .player_x + self .noise .get_point (torch_t ) * 1.5
605+ torch_y = self .player_y + self .noise .get_point (torch_t + 11 ) * 1.5
606+ # Extra light brightness.
607+ brightness = 0.2 * self .noise .get_point (torch_t + 17 )
608+
609+ # Get the squared distance using a mesh grid.
610+ x , y = np .mgrid [:SAMPLE_SCREEN_WIDTH , :SAMPLE_SCREEN_HEIGHT ]
611+ # Center the mesh grid on the torch position.
612+ x = x .astype (np .float32 ) - torch_x
613+ y = y .astype (np .float32 ) - torch_y
614+
615+ distance_squared = x ** 2 + y ** 2 # 2D squared distance array.
616+
617+ # Get the currently visible cells.
618+ visible = (distance_squared < SQUARED_TORCH_RADIUS ) & fov
619+
620+ # Invert the values, so that the center is the 'brightest' point.
621+ light = SQUARED_TORCH_RADIUS - distance_squared
622+ light /= SQUARED_TORCH_RADIUS # Convert into non-squared distance.
623+ light += brightness # Add random brightness.
624+ light .clip (0 , 1 , out = light ) # Clamp values in-place.
625+ light [~ visible ] = 0 # Set non-visible areas to darkness.
626+
627+ # Setup background colors for floating point math.
628+ light_bg = self .light_map_bg .astype (np .float16 )
629+ dark_bg = self .dark_map_bg .astype (np .float16 )
630+
631+ # Linear interpolation between colors.
632+ sample_console .tiles_rgb ["bg" ] = (
633+ dark_bg + (light_bg - dark_bg ) * light [..., np .newaxis ]
623634 )
624- # where_fov = np.where(self.map.fov[:])
625- mgrid = np .mgrid [:SAMPLE_SCREEN_WIDTH , :SAMPLE_SCREEN_HEIGHT ]
626- # get squared distance
627- light = (mgrid [0 ] - self .px + dx ) ** 2 + (
628- mgrid [1 ] - self .py + dy
629- ) ** 2
630- light = light .astype (np .float16 )
631- visible = (light < SQUARED_TORCH_RADIUS ) & self .map .fov [:]
632- light [...] = SQUARED_TORCH_RADIUS - light
633- light [...] /= SQUARED_TORCH_RADIUS
634- light [...] += di
635- light [...] = light .clip (0 , 1 )
636- light [~ visible ] = 0
637-
638- sample_console .bg [...] = (
639- self .light_map_bg .astype (np .float16 ) - self .dark_map_bg
640- ) * light [..., np .newaxis ] + self .dark_map_bg
641635 else :
642- where_fov = np .where (self .map .fov [:])
643- sample_console .bg [where_fov ] = self .light_map_bg [where_fov ]
636+ sample_console .bg [...] = np .where (
637+ fov [:, :, np .newaxis ], self .light_map_bg , self .dark_map_bg
638+ )
644639
645640 def ev_keydown (self , event : tcod .event .KeyDown ):
646641 MOVE_KEYS = {
@@ -649,32 +644,24 @@ def ev_keydown(self, event: tcod.event.KeyDown):
649644 ord ("k" ): (0 , 1 ),
650645 ord ("l" ): (1 , 0 ),
651646 }
652- FOV_SELECT_KEYS = {ord ("-" ): - 1 , ord ("=" ): 1 }
647+ FOV_SELECT_KEYS = {
648+ ord ("-" ): - 1 ,
649+ ord ("=" ): 1 ,
650+ tcod .event .K_KP_MINUS : - 1 ,
651+ tcod .event .K_KP_PLUS : 1 ,
652+ }
653653 if event .sym in MOVE_KEYS :
654654 x , y = MOVE_KEYS [event .sym ]
655- if SAMPLE_MAP [self .px + x , self .py + y ] == " " :
656- tcod .console_put_char (
657- sample_console , self .px , self .py , " " , tcod .BKGND_NONE
658- )
659- self .px += x
660- self .py += y
661- tcod .console_put_char (
662- sample_console , self .px , self .py , "@" , tcod .BKGND_NONE
663- )
664- self .recompute = True
655+ if self .walkable [self .player_x + x , self .player_y + y ]:
656+ self .player_x += x
657+ self .player_y += y
665658 elif event .sym == ord ("t" ):
666659 self .torch = not self .torch
667- self .draw_ui ()
668- self .recompute = True
669660 elif event .sym == ord ("w" ):
670661 self .light_walls = not self .light_walls
671- self .draw_ui ()
672- self .recompute = True
673662 elif event .sym in FOV_SELECT_KEYS :
674663 self .algo_num += FOV_SELECT_KEYS [event .sym ]
675664 self .algo_num %= tcod .NB_FOV_ALGORITHMS
676- self .draw_ui ()
677- self .recompute = True
678665 else :
679666 super ().ev_keydown (event )
680667
0 commit comments