Complete Roguelike Tutorial, using python+libtcod, part 2 code
This is part of the code for a series of tutorials; the main page can be found here. |
The Map
import libtcodpy as libtcod #actual size of the window SCREEN_WIDTH = 80 SCREEN_HEIGHT = 50 #size of the map MAP_WIDTH = 80 MAP_HEIGHT = 45 LIMIT_FPS = 20 #20 frames-per-second maximum color_dark_wall = libtcod.Color(0, 0, 100) color_dark_ground = libtcod.Color(50, 50, 150) class Tile: #a tile of the map and its properties def __init__(self, blocked, block_sight = None): self.blocked = blocked #by default, if a tile is blocked, it also blocks sight if block_sight is None: block_sight = blocked self.block_sight = block_sight class Object: #this is a generic object: the player, a monster, an item, the stairs... #it's always represented by a character on screen. def __init__(self, x, y, char, color): self.x = x self.y = y self.char = char self.color = color def move(self, dx, dy): #move by the given amount, if the destination is not blocked if not map[self.x + dx][self.y + dy].blocked: self.x += dx self.y += dy def draw(self): #set the color and then draw the character that represents this object at its position libtcod.console_set_foreground_color(0, self.color) libtcod.console_put_char(0, self.x, self.y, self.char, libtcod.BKGND_NONE) def clear(self): #erase the character that represents this object libtcod.console_put_char(0, self.x, self.y, ' ', libtcod.BKGND_NONE) def make_map(): global map #fill map with "unblocked" tiles map = [[ Tile(False) for y in range(MAP_HEIGHT) ] for x in range(MAP_WIDTH) ] #place two pillars to test the map map[30][22].blocked = True map[30][22].block_sight = True map[50][22].blocked = True map[50][22].block_sight = True def render_all(): global color_light_wall global color_light_ground #draw all objects in the list for object in objects: object.draw() #go through all tiles, and set their background color for y in range(MAP_HEIGHT): for x in range(MAP_WIDTH): wall = map[x][y].block_sight if wall: libtcod.console_set_back(0, x, y, color_dark_wall, libtcod.BKGND_SET ) else: libtcod.console_set_back(0, x, y, color_dark_ground, libtcod.BKGND_SET ) def handle_keys(): key = libtcod.console_check_for_keypress() #real-time #key = libtcod.console_wait_for_keypress(True) #turn-based if key.vk == libtcod.KEY_ENTER and key.lalt: #Alt+Enter: toggle fullscreen libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) elif key.vk == libtcod.KEY_ESCAPE: return True #exit game #movement keys if libtcod.console_is_key_pressed(libtcod.KEY_UP): player.move(0, -1) elif libtcod.console_is_key_pressed(libtcod.KEY_DOWN): player.move(0, 1) elif libtcod.console_is_key_pressed(libtcod.KEY_LEFT): player.move(-1, 0) elif libtcod.console_is_key_pressed(libtcod.KEY_RIGHT): player.move(1, 0) ############################################# # Initialization & Main Loop ############################################# libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'python/libtcod tutorial', False) libtcod.sys_set_fps(LIMIT_FPS) #create object representing the player player = Object(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, '@', libtcod.white) #create an NPC npc = Object(SCREEN_WIDTH/2 - 5, SCREEN_HEIGHT/2, '@', libtcod.yellow) #the list of objects with those two objects = [npc, player] #generate map (at this point it's not drawn to the screen) make_map() #first_time = True #for turn-based games while not libtcod.console_is_window_closed(): #erase all objects at their old locations, before they move for object in objects: object.clear() #handle keys and exit game if needed #if not first_time: #for turn-based games exit = handle_keys() if exit: break #first_time = False #for turn-based games #render the screen render_all() libtcod.console_flush()
Field of View (FOV)
import libtcodpy as libtcod
- actual size of the window
SCREEN_WIDTH = 80 SCREEN_HEIGHT = 50
- size of the map
MAP_WIDTH = 80 MAP_HEIGHT = 45
FOV_ALGO = 0 #default FOV algorithm
FOV_LIGHT_WALLS = True #light walls or not
TORCH_RADIUS = 10
LIMIT_FPS = 20 #20 frames-per-second maximum
color_dark_wall = libtcod.Color(0, 0, 100)
color_light_wall = libtcod.Color(130, 110, 50)
color_dark_ground = libtcod.Color(50, 50, 150)
color_light_ground = libtcod.Color(200, 180, 50)
class Tile:
#a tile of the map and its properties def __init__(self, blocked, block_sight = None): self.blocked = blocked #by default, if a tile is blocked, it also blocks sight if block_sight is None: block_sight = blocked self.block_sight = block_sight
class Object:
#this is a generic object: the player, a monster, an item, the stairs... #it's always represented by a character on screen. def __init__(self, x, y, char, color): self.x = x self.y = y self.char = char self.color = color def move(self, dx, dy): #move by the given amount, if the destination is not blocked if not map[self.x + dx][self.y + dy].blocked: self.x += dx self.y += dy def draw(self): #only show if it's visible to the player if libtcod.map_is_in_fov(fov_map, self.x, self.y): #set the color and then draw the character that represents this object at its position libtcod.console_set_foreground_color(0, self.color) libtcod.console_put_char(0, self.x, self.y, self.char, libtcod.BKGND_NONE) def clear(self): #erase the character that represents this object libtcod.console_put_char(0, self.x, self.y, ' ', libtcod.BKGND_NONE)
def make_map():
global map #fill map with "unblocked" tiles map = [[ Tile(False) for y in range(MAP_HEIGHT) ] for x in range(MAP_WIDTH) ] #place two pillars to test the map map[30][22].blocked = True map[30][22].block_sight = True map[50][22].blocked = True map[50][22].block_sight = True
def render_all():
global fov_map, color_dark_wall, color_light_wall global color_dark_ground, color_light_ground global fov_recompute
#draw all objects in the list for object in objects: object.draw() if fov_recompute: #recompute FOV if needed (the player moved or something) fov_recompute = False libtcod.map_compute_fov(fov_map, player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO)
#go through all tiles, and set their background color according to the FOV for y in range(MAP_HEIGHT): for x in range(MAP_WIDTH): visible = libtcod.map_is_in_fov(fov_map, x, y) wall = map[x][y].block_sight if not visible: #it's out of the player's FOV if wall: libtcod.console_set_back(0, x, y, color_dark_wall, libtcod.BKGND_SET) else: libtcod.console_set_back(0, x, y, color_dark_ground, libtcod.BKGND_SET) else: #it's visible if wall: libtcod.console_set_back(0, x, y, color_light_wall, libtcod.BKGND_SET ) else: libtcod.console_set_back(0, x, y, color_light_ground, libtcod.BKGND_SET )
def handle_keys():
global fov_recompute key = libtcod.console_check_for_keypress() #real-time #key = libtcod.console_wait_for_keypress(True) #turn-based if key.vk == libtcod.KEY_ENTER and key.lalt: #Alt+Enter: toggle fullscreen libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen()) elif key.vk == libtcod.KEY_ESCAPE: return True #exit game #movement keys elif libtcod.console_is_key_pressed(libtcod.KEY_UP): player.move(0, -1) fov_recompute = True elif libtcod.console_is_key_pressed(libtcod.KEY_DOWN): player.move(0, 1) fov_recompute = True elif libtcod.console_is_key_pressed(libtcod.KEY_LEFT): player.move(-1, 0) fov_recompute = True elif libtcod.console_is_key_pressed(libtcod.KEY_RIGHT): player.move(1, 0) fov_recompute = True
- Initialization & Main Loop
libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'python/libtcod tutorial', False) libtcod.sys_set_fps(LIMIT_FPS)
- create object representing the player
player = Object(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, '@', libtcod.white)
- create an NPC
npc = Object(SCREEN_WIDTH/2 - 5, SCREEN_HEIGHT/2, '@', libtcod.yellow)
- the list of objects with those two
objects = [npc, player]
- generate map (at this point it's not drawn to the screen)
make_map()
- create the FOV map, according to the generated map
fov_map = libtcod.map_new(MAP_WIDTH, MAP_HEIGHT) for y in range(MAP_HEIGHT):
for x in range(MAP_WIDTH): libtcod.map_set_properties(fov_map, x, y, not map[x][y].blocked, not map[x][y].block_sight)
fov_recompute = True
- first_time = True #for turn-based games
while not libtcod.console_is_window_closed():
#erase all objects at their old locations, before they move for object in objects: object.clear() #handle keys and exit game if needed #if not first_time: #for turn-based games exit = handle_keys() if exit: break #first_time = False #for turn-based games #render the screen render_all() libtcod.console_flush()