def render_panel(root_console: tcod.console.Console, panel_console: tcod.console.Console, entities: list, player: Entity, game_map: GameMap, message_log: MessageLog, bar_width: int, panel_width: int, panel_height: int, panel_x: int, panel_y: int, mouse: tuple, fps: float): panel_console.default_bg = tcod.black panel_console.clear() # print message log y = 1 for message in message_log.messages: panel_console.default_fg = message.colour panel_console.print_(message_log.x, y, message.text, tcod.BKGND_NONE, tcod.LEFT) panel_console.print(message_log.x, y, message.text, fg=message.colour, bg=message_log.bg, bg_blend=tcod.BKGND_NONE, alignment=tcod.LEFT) y += 1 render_bar(panel_console, 1, 1, bar_width, 'HP', player.fighter.hp, player.fighter.max_hp, tcod.red, tcod.darkest_red) panel_console.default_fg = tcod.light_grey panel_console.print_(1, 0, get_names_under_mouse(mouse, entities, game_map), tcod.BKGND_NONE, tcod.LEFT) # panel_console.print(1, 2, 'FPS: {0}'.format(fps), bg_blend=tcod.BKGND_NONE) panel_console.blit(root_console, panel_x, panel_y, 0, 0, panel_width, panel_height) return
def blit_and_flush( from_console: tcod.console.Console, to_console: tcod.console.Console ) -> None: from_console.blit( to_console, width=from_console.width, height=from_console.height ) tcod.console_flush()
def render_map(root_console: tcod.console.Console, map_console: tcod.console.Console, game_map: GameMap, entities: list, start_x: int, start_y: int, colours: dict, fov_recompute: bool): # render map tiles only when a change has occurred if fov_recompute: for y in range(game_map.height): for x in range(game_map.width): visible = game_map.fov(x, y) wall = not game_map.walkable(x, y) # if tile can be seen now, light it up if visible: if wall: tcod.console_set_char_background( map_console, x, y, colours.get('light_wall'), tcod.BKGND_SET) else: tcod.console_set_char_background( map_console, x, y, colours.get('light_ground'), tcod.BKGND_SET) # if we've previously seen this tile elif game_map.explored(x, y): if wall: tcod.console_set_char_background( map_console, x, y, colours.get('dark_wall'), tcod.BKGND_SET) else: tcod.console_set_char_background( map_console, x, y, colours.get('dark_ground'), tcod.BKGND_SET) # render entities onto map entities_in_render_order = sorted(entities, key=lambda e: e.render_order.value) for entity in entities_in_render_order: draw_entity(map_console, entity, game_map) # copy to the actual screen map_console.blit(root_console, start_x, start_y, 0, 0, game_map.width, game_map.height) return
def render_all(root_con: tcod.console.Console, con: tcod.console.Console, panel: tcod.console.Console, entities, player, game_map, fov_map, fov_recompute: bool, message_log, bar_width, panel_y: int, mouse_pos, colors, game_state: GameStates): """Render characters on the console screen""" render_main_map(con, entities, player, game_map, fov_map, fov_recompute, colors) con.blit(root_con) render_panel(panel, message_log, bar_width, player, mouse_pos, entities, fov_map, game_map.dungeon_level) panel.blit(root_con, 0, panel_y) if game_state in (GameStates.SHOW_INVENTORY, GameStates.DROP_INVENTORY): if game_state == GameStates.SHOW_INVENTORY: inventory_title = 'Press the key next to an item to use it, or Esc to cancel.\n' else: inventory_title = 'Press the key next to an item to drop it, or Esc to cancel.\n' inventory_menu(root_con, inventory_title, player, 50) elif game_state == GameStates.LEVEL_UP: level_up_menu(root_con, 'Level up! Choose a stat to raise:', player, 40) elif game_state == GameStates.CHARACTER_SCREEN: character_screen(root_con, player, 30, 10)
def render_viewport(root_console: tcod.console.Console, viewport_console: tcod.console.Console, viewport_x: int, viewport_y: int, viewport_width: int, viewport_height: int, game_map: GameMap, entities: list, colours: dict, player_x: float, player_y: float, player_rot: float, fov_radius: int, rerender: bool, wall_texture): """ Raycasting algorithm based on javidx9's excellent YouTube video, 'Code-It-Yourself! First Person Shooter (Quick and Simple C++)' """ if rerender: texture_width = 32 # pi/4 rad = 45 deg (pi/3=60, pi/2=90, pi=180) fov = math.pi / 3.0 cam_x = player_x + 0.5 cam_y = player_y + 0.5 ground_colour = colours.get('light_ground') wall_colour = colours.get('light_wall') viewport_console.clear() for x in range(viewport_width): # render a column based on raycasting ray_angle = (player_rot + fov / 2.0) - (x / float(viewport_width)) * fov eye_x = math.sin(ray_angle) eye_y = math.cos(ray_angle) distance_to_wall = 0.0 hit_wall = False distance_to_entity = 0.0 hit_entity = False entity: Entity = None wall_x = 0.0 entity_x = 0.0 ray_step = 0.2 prev_test_x = 0.0 prev_test_y = 0.0 while not hit_wall and distance_to_wall < fov_radius: distance_to_wall += ray_step if not hit_entity: distance_to_entity += ray_step test_x = cam_x + eye_x * distance_to_wall test_y = cam_y + eye_y * distance_to_wall i_test_x = int(test_x) i_test_y = int(test_y) # if outside of game map if i_test_x < 0 or i_test_x >= game_map.width or i_test_y < 0 or i_test_y >= game_map.height: hit_wall = True distance_to_wall = fov_radius # else else: # have we hit a wall yet if not game_map.walkable(i_test_x, i_test_y): hit_wall = True # where on the wall have we hit? if abs(prev_test_x - test_x) > abs(prev_test_y - test_y): wall_x = test_y - i_test_y else: wall_x = test_x - i_test_x # if we haven't seen an entity yet if not hit_entity and abs(distance_to_entity) > 0.6: # can we see an entity entities_in_render_order = sorted( entities, key=lambda e: 4 - e.render_order.value) e = get_first_entity_at_location( entities_in_render_order, i_test_x, i_test_y) if e is not None: hit_entity = True entity = e # vector maths adapted from https://stackoverflow.com/a/58542694 # find the point where the ray meets the entity's centre line P = (cam_x, cam_y) # player point Q = (i_test_x + 0.5, i_test_y + 0.5 ) # entity centre point R = (eye_x, eye_y) # dir of player vision S = (eye_y, -eye_x ) # entity plane is perpendicular PQ = numpy.subtract(Q, P) # vector between two points PQ_mag = math.sqrt(PQ[0]**2 + PQ[1]**2) # t = PQ_mag * 0.707 t = float( numpy.vdot(PQ, R) / numpy.vdot(R, (S[1], -S[0]))) X = numpy.add( P, (t * R[0], t * R[1]) ) # point where we hit a line through the centre of the entity's tile a = ( Q[0] - (0.5 * S[0]), Q[1] - (0.5 * S[1]) ) # approximation of the start of the entity's line b = ( Q[0] + (0.5 * S[0]), Q[1] + (0.5 * S[1]) ) # approximation of the end of the entity's line c = (old_lerp(a[0], b[0], X[0]), old_lerp(a[1], b[1], X[1])) entity_x = math.sqrt((X[0] - int(X[0]))**2 + (X[1] - int(X[1]))**2) # entity_x = math.sqrt(c[0]**2 + c[1]**2) distance_to_entity = math.sqrt(PQ[0]**2 + PQ[1]**2) prev_test_x = test_x prev_test_y = test_y # end_while # draw this column of the room ceiling = float(viewport_height / 2.0) - viewport_height / float(distance_to_wall) floor = viewport_height - ceiling wall_brightness = 1.0 - slerp_float(0, fov_radius, distance_to_wall / fov_radius) if wall_brightness < 0.0: wall_brightness = 1.0 elif wall_brightness > 1.0: wall_brightness = 1.0 out_of_view = distance_to_wall >= fov_radius x_cell = x # viewport_width - x - 1 half_viewport_height = viewport_height / 2 for y in range(viewport_height): # ceiling if y < ceiling: brightness = 1.0 - slerp_float(0, half_viewport_height, y / half_viewport_height) tcod.console_set_char_background(viewport_console, x_cell, y, wall_colour * brightness, tcod.BKGND_SET) # wall elif y < floor: if out_of_view: tcod.console_set_char_background( viewport_console, x_cell, y, tcod.black, tcod.BKGND_SET) elif hit_wall: u = int(wall_x * texture_width) if u >= 32: u = 31 v = int(((y - ceiling) / (floor - ceiling)) * texture_width) pixel = wall_texture.getpixel((u, v)) tex_col = (int(pixel[0] * wall_brightness), int(pixel[1] * wall_brightness), int(pixel[2] * wall_brightness)) tcod.console_set_char_background( viewport_console, x_cell, y, tex_col, tcod.BKGND_SET) # floor else: brightness = slerp_float( half_viewport_height, viewport_height, (y - half_viewport_height) / half_viewport_height) tcod.console_set_char_background( viewport_console, x_cell, y, ground_colour * brightness, tcod.BKGND_SET) # draw this column of entity (if we found one) if hit_entity and entity.texture: height = (viewport_height / min(max(float(distance_to_entity), 0.0), 1.0)) start = int((viewport_height - height) / 2.0) entity_brightness = 1.0 - slerp_float( 0, fov_radius, distance_to_entity / fov_radius) for y in range(int(height)): u = int(entity_x * texture_width) if u >= 32: u = 31 v = int((y / height) * texture_width) pixel = entity.texture.getpixel((u, v)) # ignore transparent texels if pixel[3] != 0: tex_col = (int(pixel[0] * entity_brightness), int(pixel[1] * entity_brightness), int(pixel[2] * entity_brightness)) tcod.console_set_char_background( viewport_console, x_cell, start + y, tex_col, tcod.BKGND_SET) # end_for x viewport_console.blit(root_console, viewport_x, viewport_y, 0, 0, viewport_width, viewport_height) return
def render_all(root_console: tcod.console.Console, offscreen_console: tcod.console.Console, viewport_console: tcod.console.Console, status_console: tcod.console.Console, log_console: tcod.console.Console, entity_console: tcod.console.Console, player: Entity, game_map: GameMap, mouse_tx: int, mouse_ty: int, fov_recompute: bool, game_messages: MessageLog, box_text: str, game_state: GameState, camera: "Camera") -> None: screen_height = const.SCREEN_HEIGHT screen_width = const.SCREEN_WIDTH bar_width = const.BAR_WIDTH status_console.clear() log_console.clear() entity_console.clear() if fov_recompute: # Show nothing by default viewport_console.ch[:] = 0 viewport_console.fg[:] = (0, 0, 0) viewport_console.bg[:] = (0, 0, 0) # Move camera to follow the player camera.move_camera(player.x, player.y, game_map.width, game_map.height) cam_x, cam_y = camera.x, camera.y cam_x2, cam_y2 = camera.x2, camera.y2 # Translate map coordinates to camera coordinates cam_fov = game_map.fov_map.fov[cam_x:cam_x2 + 1, cam_y:cam_y2 + 1] cam_explored = game_map.explored[cam_x:cam_x2 + 1, cam_y:cam_y2 + 1] cam_glyph = game_map.tile_map.glyph[cam_x:cam_x2 + 1, cam_y:cam_y2 + 1] cam_fg = game_map.tile_map.fg[cam_x:cam_x2 + 1, cam_y:cam_y2 + 1] cam_bg = game_map.tile_map.bg[cam_x:cam_x2 + 1, cam_y:cam_y2 + 1] # If a tile is explored but not visible, render it in dark colors. viewport_console.fg[cam_explored == True] = np.multiply( cam_fg[cam_explored == True], 0.50).astype(np.int) viewport_console.bg[cam_explored == True] = np.multiply( cam_bg[cam_explored == True], 0.50).astype(np.int) viewport_console.ch[cam_explored == True] = cam_glyph[cam_explored == True] # If a tile is visible then render it in light colors. viewport_console.fg[cam_fov == True] = cam_fg[cam_fov == True] viewport_console.bg[cam_fov == True] = cam_bg[cam_fov == True] viewport_console.ch[cam_fov == True] = cam_glyph[cam_fov == True] # viewport_console.ch[cam_transparent == False] = 178 # If a tile is visible, then it is now explored. game_map.explored[game_map.fov_map.fov == True] = True # Draw all entities in the list entities_in_render_order = sorted(game_map.entities, key=lambda x: x.entity_type.value) for entity in entities_in_render_order: draw_entity(viewport_console, entity, game_map, camera) render_bar(status_console, 1, 1, bar_width, 'HP', player.fighter.hp, player.fighter.max_hp, tcod.light_red, tcod.darker_red) status_console.print(1, 3, f"Dungeon Level: {game_map.dungeon_level}") status_console.print(1, 0, get_names_under_mouse(mouse_tx, mouse_ty, game_map.entities, game_map), fg=(128, 128, 128)) y = 0 for message in game_messages.messages: log_console.print(game_messages.x, y, message.text, fg=message.color) y += 1 entity_console.print(5, 0, "Visible:", (128, 128, 128)) visible_entities = [ entity for entity in entities_in_render_order if tcod.map_is_in_fov(game_map.fov_map, entity.x, entity.y) ] for index, entity in enumerate(visible_entities, start=1): if entity.entity_type not in [EntityType.PLAYER, EntityType.CORPSE]: entity_str = f"{chr(entity.glyph)}: {entity.name.capitalize()}" entity_console.print(1, index, entity_str, entity.fg) draw_frames(offscreen_console) # offscreen_console.print(0, screen_height - 1, f"{mouse_tx}, {mouse_ty}") viewport_console.blit(offscreen_console, 1, 1) status_console.blit(offscreen_console, const.VIEWPORT_WIDTH + 2, 1) log_console.blit(offscreen_console, 1, const.VIEWPORT_HEIGHT + 2) entity_console.blit(offscreen_console, const.VIEWPORT_WIDTH + 2, const.STATUS_HEIGHT + 2) offscreen_console.blit(root_console) if game_state in [GameState.SHOW_INVENTORY, GameState.DROP_INVENTORY]: if game_state == GameState.SHOW_INVENTORY: inventory_title = "Press the key next to an item to use it, ESC to cancel.\n" else: inventory_title = "Press the key next to an item to drop it, ESC to cancel.\n" inventory_menu(root_console, inventory_title, player, 50, screen_width, screen_height) elif game_state == GameState.LEVEL_UP: level_up_menu(root_console, "Level up! Choose a stat to raise:", player, 40, screen_width, screen_height) elif game_state == GameState.CHARACTER_SCREEN: character_screen(root_console, player, 30, 10, screen_width, screen_height) elif game_state == GameState.MESSAGE_BOX: message_box(root_console, box_text, len(box_text), const.VIEWPORT_WIDTH, const.VIEWPORT_HEIGHT) if SHOW_STATS: fps = tcod.sys_get_fps() if fps > 0: fps_str = f"FPS: {fps} ({1000 / fps:.2f} ms/frame)" root_console.print(0, const.SCREEN_HEIGHT - 1, fps_str, fg=(255, 255, 255)) tcod.console_flush()
def render_all( root_console: tcod.console.Console, con: tcod.console.Console, panel: tcod.console.Console, entities: List['Entity'], player: 'Entity', game_map: 'GameMap', fov_map: tcod.map.Map, fov_recompute: bool, message_log: MessageLog, screen_width: int, screen_height: int, bar_width: int, panel_height: int, panel_y: int, mouse: tcod.event.Point, colors: Dict[str, tcod.Color], game_state: GameStates, target_radius: int = 0, ) -> None: # Draw all the tiles in the game map for y in range(game_map.height): for x in range(game_map.width): visible = fov_map.fov[x, y] wall = game_map.tiles[x][y].block_sight if visible: if wall: color = colors['light_wall'] else: if (game_state == GameStates.TARGETING and math.hypot(x - mouse.x, y - mouse.y) <= target_radius): color = colors['target_ground'] else: color = colors['light_ground'] game_map.tiles[x][y].explored = True elif game_map.tiles[x][y].explored: if wall: color = colors['dark_wall'] else: color = colors['dark_ground'] else: continue con.bg[x, y] = cast_to_color(color) # Draw all entities in the list for entity in sorted(entities, key=attrgetter('render_order.value')): draw_entity(con, entity, fov_map, game_map) con.blit(root_console, 0, 0, 0, 0, screen_width, screen_height) panel.clear(bg=cast_to_color(tcod.black)) # Print the game messages, one line at a time for y, message in enumerate(message_log.messages, 1): panel.print(message_log.x, y, message.text, fg=cast_to_color(message.color), bg_blend=tcod.BKGND_NONE, alignment=tcod.LEFT) panel.print(1, 0, get_names_under_mouse(mouse, entities, fov_map), fg=cast_to_color(tcod.light_gray), bg_blend=tcod.BKGND_NONE, alignment=tcod.LEFT) render_bar(panel, 1, 1, bar_width, 'HP', player.fighter.hp, player.fighter.max_hp, tcod.light_red, tcod.darker_red) render_bar(panel, 1, 2, bar_width, 'XP', player.level.current_xp, player.level.experience_to_next_level, tcod.light_blue, tcod.darker_blue) panel.print(1, 3, f'Player level: {player.level.current_level}', fg=cast_to_color(tcod.white), bg_blend=tcod.BKGND_NONE, alignment=tcod.LEFT) panel.print(1, 4, f'Dungeon level: {game_map.dungeon_level}', fg=cast_to_color(tcod.white), bg_blend=tcod.BKGND_NONE, alignment=tcod.LEFT) panel.blit(root_console, 0, panel_y, 0, 0, screen_width, panel_height) if game_state == GameStates.SHOW_INVENTORY: inventory_menu( root_console, "Press the key next to an item to use it, or Esc to cancel.\n", player, 50, screen_width, screen_height, ) elif game_state == GameStates.DROP_INVENTORY: inventory_menu( root_console, "Press the key next to an item to drop it, or Esc to cancel.\n", player, 50, screen_width, screen_height, ) elif game_state == GameStates.LEVEL_UP: level_up_menu(root_console, 'Level up! Choose a stat to raise:', player, 40, screen_width, screen_height) elif game_state == GameStates.CHARACTER_SCREEN: character_screen(root_console, player, 30, 10, screen_width, screen_height)