Skip to content

Commit 2acf224

Browse files
committed
Update FOV sample.
1 parent 873111a commit 2acf224

1 file changed

Lines changed: 74 additions & 87 deletions

File tree

examples/samples_tcod.py

Lines changed: 74 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)