def main(): screen_width = 90 screen_height = 60 tcod.console_set_custom_font( 'terminal8x12_gs_tc.png', tcod.FONT_TYPE_GRAYSCALE | tcod.FONT_LAYOUT_TCOD) tcod.console_init_root(screen_width, screen_height, 'Spaceship', False, renderer=tcod.RENDERER_SDL2, vsync=True) tcod.console_map_ascii_code_to_font(7, 12, 2) tcod.console_map_ascii_code_to_font(9, 13, 2) object_view_width = 40 object_view_height = 40 main_console = Console(screen_width, screen_height, order='F') object_console = Console(screen_width, screen_height, order='F') parse(all_objects, object_list) frame = all_objects['base'][1] spaceship_x, spaceship_y = 0, 0 file_path = getcwd() + '/spaceship.dat' if not path.exists(file_path) or path.getsize(file_path) == 0: pickle.dump(Object('Spaceship', 50, 50), open(file_path, 'wb+')) try: spaceship = pickle.load(open(getcwd() + '/spaceship.dat', 'rb')) except pickle.UnpicklingError: pickle.dump(Object('Spaceship', 50, 50), open(file_path, 'wb+')) spaceship = pickle.load(open(file_path, 'rb')) examine_x = screen_width - 50 examine_y = 1 examine_width = 50 examine_height = screen_height - 2 examine_menu = None build_menu_object_key_list = ['default'] for x in range(spaceship.width): for y in range(spaceship.height): for part in spaceship.tiles[x][y]: for object in object_list: if not 'json_id' in part.__dict__ and object.name == part.name: part.json_id = object.json_id print('Assigned {} to {}'.format( part.name, object.json_id)) if 'json_id' in part.__dict__ and object.json_id == part.json_id: part.fg = object.fg part.bg = object.bg part.char = object.char part.sorting_index = object.sorting_index part.mass = object.mass break else: print( 'Could not find corresponding JSON part for existing part {}' .format(part.name)) spaceship.add_part(0, 0, frame, override=True) spaceship.calculate_rooms() viewing_mode = 0 num_modes = len(view_dict) + 2 selection_index = 0 max_select_index = 0 build_menu_open = False selection_box = (0, 0), (0, 0) screen_offset_x, screen_offset_y = 0, 0 selection_console = Console(screen_width, screen_height, order='F') selection_console.clear(bg=tcod.lightest_blue) while True: response = None prev_select_index = selection_index prev_selection_coord = selection_box[0] for event in tcod.event.get(): if event.type == 'KEYDOWN': response = {} input_resp = handle_input(event) if input_resp: response.update(input_resp) if build_menu_open and ord('z') >= event.sym >= ord('a'): response['select_item'] = event.sym - ord('a') elif event.type == 'MOUSEBUTTONDOWN' or event.type == 'MOUSEBUTTONUP' or event.type == 'MOUSEMOTION': response = handle_mouse(event) elif event.type == 'QUIT': pickle.dump(spaceship, open(file_path, 'wb+')) return if response: if response.get('move'): move = response.get('move') if examine_menu: examine_menu.selected_index += move[1] if move[0] != 0: if examine_menu.try_change_option(move[0]): spaceship.update_all() else: screen_offset_x = max(0, screen_offset_x + move[0]) screen_offset_y = max(0, screen_offset_y + move[1]) elif response.get('viewing_mode'): viewing_mode = (viewing_mode + response.get('viewing_mode')) % num_modes elif response.get('navigate'): selection_index += response.get('navigate') if selection_index < 0: selection_index += max_select_index elif selection_index >= max_select_index: selection_index -= max_select_index if response.get('build'): if not build_menu_open: build_menu_open = True build_menu_object_key_list = [] selection_index = 0 if response.get('delete') and not build_menu_open: if spaceship.is_valid_coord( selection_box[0][0] - spaceship_x, selection_box[0][1] - spaceship_y) and selection_index < len( spaceship.tiles[selection_box[0][0] - spaceship_x][ selection_box[0][1] - spaceship_y]): part_name = spaceship.tiles[ selection_box[0][0] - spaceship_x][selection_box[0][1] - spaceship_y][selection_index].name for x in range(selection_box[0][0], selection_box[1][0]): for y in range(selection_box[0][1], selection_box[1][1]): for part in spaceship.tiles[x][y]: if part.name == part_name: spaceship.remove_part( x - spaceship_x, y - spaceship_y, part) elif (response.get('select') or response.get('mouse_move_lheld') or response.get('lclick_down')) and build_menu_open: if type(get_object(build_menu_object_key_list)) is list: if response.get('mouse_move_lheld'): selection_box = response.get('mouse_move_lheld'), ( response.get('mouse_move_lheld')[0] + 1, response.get('mouse_move_lheld')[1] + 1) for x in range(selection_box[0][0], selection_box[1][0]): for y in range(selection_box[0][1], selection_box[1][1]): spaceship.add_part( x - spaceship_x, y - spaceship_y, get_object(build_menu_object_key_list) [selection_index]) else: build_menu_object_key_list.append( list(get_object(build_menu_object_key_list).keys()) [selection_index]) selection_index = 0 elif response.get('run'): for x in range(spaceship.width): for y in range(spaceship.height): for part in [ part for part in spaceship.tiles[x][y] if part.runnable ]: part.runnable.run(spaceship, part, x, y) spaceship.update_all() if response.get('rclick_down'): selection_box = response.get('rclick_down'), response.get( 'rclick_down') elif response.get('lclick_down'): selection_box = response.get('lclick_down'), response.get( 'lclick_down') elif response.get('mouse_move_rheld'): selection_box = selection_box[0], ( response.get('mouse_move_rheld')[0] + 1, response.get('mouse_move_rheld')[1] + 1) elif response.get( 'mouse_move' ) and selection_box[1][0] - selection_box[0][ 0] <= 1 and selection_box[1][1] - selection_box[0][1] <= 1: selection_box = response.get('mouse_move'), ( response.get('mouse_move')[0] + 1, response.get('mouse_move')[1] + 1) elif response.get('select_item') is not None: index = response.get('select_item') if index < max_select_index: selection_index = index if type(get_object(build_menu_object_key_list)) is dict: build_menu_object_key_list.append( list( get_object(build_menu_object_key_list).keys()) [selection_index]) selection_index = 0 if response.get('escape'): if build_menu_open: if len(build_menu_object_key_list) == 0: build_menu_open = False selection_index = 0 else: build_menu_object_key_list.pop(-1) elif examine_menu: examine_menu = None if prev_select_index != selection_index or prev_selection_coord != selection_box[ 0]: if spaceship.is_valid_coord( selection_box[0][0] - spaceship_x, selection_box[0][1] - spaceship_y) and selection_index < len( spaceship.tiles[selection_box[0][0] - spaceship_x][ selection_box[0][1] - spaceship_y]): examine_menu = PartMenu( spaceship, spaceship.tiles[selection_box[0][0] - spaceship_x][selection_box[0][1] - spaceship_y][selection_index], examine_width, examine_height, selection_box[0][0], selection_box[0][1]) else: examine_menu = None tcod.console_flush() main_console.clear(bg=tcod.black) selection_pos = selection_box[0] object_console.clear() render_object(object_console, spaceship, spaceship_x, spaceship_y, viewing_mode) object_console.blit(main_console, 0, 0, screen_offset_x, screen_offset_y, object_view_width, object_view_height) # main_console.put_char(selection_box[0][0], selection_box[0][1], ord('X')) # main_console.fg[selection_box[0][0], selection_box[0][1]] = tcod.cyan if build_menu_open: iterable = get_object(build_menu_object_key_list) if type(iterable) is list: iterable = [item.name for item in iterable] else: iterable = [item.capitalize() for item in iterable] for i in range(len(iterable)): main_console.print(screen_width - 50, i, chr(ord('a') + i) + ' - ' + iterable[i], bg=(tcod.desaturated_blue if selection_index == i else tcod.black)) main_console.fg[screen_width - 50, i] = tcod.light_green max_select_index = len(iterable) else: main_console.print(0, screen_height - 1, str(viewing_mode)) if spaceship_x <= selection_pos[ 0] < spaceship_x + spaceship.width and spaceship_y <= selection_pos[ 1] < spaceship_y + spaceship.height: room = spaceship.find_room(selection_pos[0] - spaceship_x, selection_pos[1] - spaceship_y) parts = [ part for part in spaceship.tiles[selection_pos[0] - spaceship_x][selection_pos[1] - spaceship_y] ] if room: gas_y = len(parts) + 2 for gas in room.gas_content: main_console.print( 20, gas_y, '{1}: {0:.2f} kPa'.format( components.calc_pressure( room.gas_content[gas], len(room.tiles)), gas.capitalize())) gas_y += 1 for i in range(len(parts)): part = parts[i] string = part.name if part.gas_grid: string += ' {}'.format(part.gas_grid.index) main_console.print( 20, i + 1, string, bg=(tcod.desaturated_blue if selection_index == i else tcod.black)) max_select_index = len(parts) if examine_menu: examine_menu.draw() examine_menu.console.blit(main_console, examine_x, examine_y, 0, 0, examine_width, examine_height) dsel_x = selection_box[1][0] - selection_box[0][0] dsel_y = selection_box[1][1] - selection_box[0][1] if dsel_x > 0 and dsel_y > 0: selection_console.blit(main_console, selection_box[0][0], selection_box[0][1], 0, 0, dsel_x, dsel_y, bg_alpha=0.25) main_console.blit(tcod.console._root_console, width=screen_width, height=screen_height, bg_alpha=1)
def render(self, console: Console) -> None: """ Renders the map. If a tile is in the "visible" array, then draw it with the "light" colors. If it isn't, but it's in the "explored" array, then draw it with the "dark" colors. Otherwise, the default is "SHROUD". """ # swap color modes depending on mode # this is an animation trick. each time we render, move down a row. if self.vision_mode and self.vision_row <= self.height: self.vision_row += 4 elif self.vision_row > 0: self.vision_row -= 4 # clamp it self.vision_row = min(self.vision_row, self.height) self.vision_row = max(self.vision_row, 0) # then we do rendering in two passes. 0 to current row, then current_row # to rest. so in vision mode, it's 0:0 and we dont' render with vision # colors. if self.vision_row > 0: tiles = np.select( condlist=[self.visible, self.explored], choicelist=[self.tiles["light_vision"], self.tiles["dark_vision"]], default=tile_types.SHROUD, ) console.tiles_rgb[0 : self.width, 0 : self.vision_row] = tiles[0:self.width,0:self.vision_row] tiles = np.select( condlist=[self.visible, self.explored], choicelist=[self.tiles["light"], self.tiles["dark"]], default=tile_types.SHROUD, ) # print(f'tiles.shape={tiles.shape} tiles.shape[][]={tiles[:][self.vision_row:self.height].shape} console.shape={console.tiles_rgb[0 : self.width, self.vision_row : self.height].shape} from {(self.width, self.height)} + row {self.vision_row}') console.tiles_rgb[0 : self.width, self.vision_row : self.height] = tiles[0:self.width,self.vision_row:self.height] entities_sorted_for_rendering = sorted( self.entities, key=lambda x: x.render_order.value ) for entity in entities_sorted_for_rendering: # for now, always render all enemies. this will make my life easier. # if self.visible[entity.x, entity.y]: console.print( x=entity.x, y=entity.y, string=entity.char, fg=entity.color ) if self.vision_mode: # in case I want to bring back facing # console.print(x=entity.x+fx, y=entity.y+fy, string='*', fg=entity.color) # now print their facing # not sure where to do this but need to map facing to dx/dy. LASER_SIGHT_DISTANCE = 4 # if we're target locked, don't draw facing, draw direct connection # if not entity.is_player and entity.target_lock == None: # (fx, fy) = Facing.get_pos(entity.facing) # # # get delta to destination # (dx, dy) = (LASER_SIGHT_DISTANCE*fx, LASER_SIGHT_DISTANCE*fy) # # cells = tcod.los.bresenham((entity.x, entity.y), # (entity.x + dx, entity.y + dy)) # # cells = np.delete(cells, 0, 0) # # discount = 0.5 # for cell in cells: # tile = self.tiles[cell[0]][cell[1]] # # if tile['walkable'] or tile['transparent']: # console.print(cell[0], cell[1], string=' ', bg=(int(255*discount), 0, 0)) # discount -= 0.5/LASER_SIGHT_DISTANCE if not entity.is_player and entity.target_lock != None: cells = tcod.los.bresenham((entity.x, entity.y), (entity.target_lock.x, entity.target_lock.y)) cells = np.delete(cells, 0, 0) for cell in cells: tile = self.tiles[cell[0]][cell[1]] if tile['walkable'] or tile['transparent']: console.print(cell[0], cell[1], string=' ', bg=(255, 0, 0)) elif not entity.is_player and entity.target_lock == None: # if they don't have a lock, paint their entire vision # only compute this for tiles the player can see cells = entity.get_visibility(self.tiles["transparent"]) # this is an ndarray with T/F in it. we need to AND this # with a matching size array that has just a white with # alpha channel set. then overlay the whole thing. vision = np.full((self.width, self.height, 3), (255, 0, 0)) # I'm not honestly sure why I have to invert this. Need to see if fix is in get_visibility. vision[np.invert(cells)] = [0, 0, 0] vision[np.invert(self.visible[:])] = [0,0,0] vision_console = Console(self.width, self.height, order="F") vision_console.bg_alpha=0.3 vision_console.bg[:] = vision # no params since we're fully overlaying the whole screen vision_console.blit(console, bg_alpha=0.2)
class Renderer(): def __init__(self, root_console: Console, ui_manager: UIManager, debug=False): self.debug = debug self.root_console = root_console self.flash_console = None self.flash_alpha = 1 self.width = self.root_console.width self.height = self.root_console.height self.map_dest_coords = (0, 1) self.ui_manager = ui_manager self.vision_console = Console(60, 60, 'F') def render_level(self, player: Player, entities: list, level_map: BaseMap, colors: bool = True, cursor=None, line: bool = False): if self.ui_manager.console: self.ui_manager.print_ui() self.ui_manager.console.blit(self.root_console, 0, 0, 0, 0, 0, 0, key_color=tcod.fuchsia) if level_map.terrain_console: level_map.terrain_console.blit(self.root_console, self.map_dest_coords[0], self.map_dest_coords[1], 0, 0, 0, 0, key_color=tcod.fuchsia) if level_map.decor_console: level_map.decor_console.blit(self.root_console, self.map_dest_coords[0], self.map_dest_coords[1], 0, 0, 0, 0, key_color=tcod.fuchsia) if level_map.living_console: level_map.living_console.blit(self.root_console, self.map_dest_coords[0], self.map_dest_coords[1], 0, 0, 0, 0, key_color=tcod.fuchsia) #Set FOV if not self.debug: self.vision_console.bg[:] = tcod.black self.vision_console.bg[level_map.fov] = tcod.fuchsia self.vision_console.blit(self.root_console, self.map_dest_coords[0], self.map_dest_coords[1], 0, 0, 0, 0, key_color=tcod.fuchsia) if not colors: self.root_console.fg[:] = tcod.white self.root_console.bg[:] = tcod.black if self.flash_console: self.flash_console.blit(self.root_console, 0, 0, 0, 0, self.flash_console.width, self.flash_console.height, self.flash_alpha, self.flash_alpha) if self.ui_manager.popup: self.show_popup(*self.ui_manager.popup) tcod.console_flush() def show_popup(self, title, text, exit_text, centered): title_width = self.get_text_width(title) text_width = self.get_text_width(text) exit_width = self.get_text_width(exit_text) min_width = max(title_width, text_width, exit_width) min_height = self.get_text_height(text) popup_rect = self.calculate_popup_rect(min_width, min_height) if centered: alignment = tcod.CENTER else: alignment = tcod.LEFT self.root_console.draw_frame(popup_rect[0] - 2, popup_rect[1] - 2, popup_rect[2] + 4, popup_rect[3] + 4, title, fg=tcod.white, bg=tcod.black) self.root_console.print_box(popup_rect[0], popup_rect[1], popup_rect[2], popup_rect[3], text, fg=tcod.white, bg=tcod.black, alignment=alignment) self.root_console.print_box(popup_rect[0] + popup_rect[2] - exit_width, popup_rect[1] + popup_rect[3] + 1, exit_width, 1, exit_text, fg=tcod.white, bg=tcod.black, alignment=tcod.RIGHT) def get_text_width(self, text): lines = text.split('\n') width = 0 for line in lines: line_width = len(line) if line_width > width: width = line_width return width def get_text_height(self, text): lines = text.split('\n') return len(lines) def calculate_popup_rect(self, width, height): hmid = self.width // 2 vmid = self.height // 2 popup_hmid = width // 2 popup_vmid = height // 2 return (hmid - popup_hmid, vmid - popup_vmid, width, height) def flash(self, color, player, entities, level_map, colors=True, cursor=None, line=False, delay=2): self.flash_console = Console(self.root_console.width, self.root_console.height, 'F') delta = timedelta(seconds=delay) start = datetime.now() diff = timedelta(seconds=0) while diff < delta: self.flash_alpha = 1 - (( (diff.seconds * 1000000) + diff.microseconds) / (delay * 1000000)) self.flash_console.draw_rect(0, 0, self.flash_console.width, self.flash_console.height, ord(' '), color, color) diff = datetime.now() - start self.render_level(player, entities, level_map, colors=colors, cursor=cursor, line=line) self.flash_console = None self.flash_alpha = 1