Difference between revisions of "Complete Roguelike Tutorial, using python+libtcod, part 2 code"
Jump to navigation
Jump to search
Line 462: | Line 462: | ||
player.x = 25 | player.x = 25 | ||
player.y = 23 | player.y = 23 | ||
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() | |||
</pre> | |||
== Dungeon generator == | |||
<pre> | |||
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 | |||
#parameters for dungeon generator | |||
ROOM_MAX_SIZE = 10 | |||
ROOM_MIN_SIZE = 6 | |||
MAX_ROOMS = 30 | |||
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 Rect: | |||
#a rectangle on the map. used to characterize a room. | |||
def __init__(self, x, y, w, h): | |||
self.x1 = x | |||
self.y1 = y | |||
self.x2 = x + w | |||
self.y2 = y + h | |||
def center(self): | |||
center_x = (self.x1 + self.x2) / 2 | |||
center_y = (self.y1 + self.y2) / 2 | |||
return (center_x, center_y) | |||
def intersect(self, other): | |||
#returns true if this rectangle intersects with another one | |||
return (self.x1 <= other.x2 and self.x2 >= other.x1 and | |||
self.y1 <= other.y2 and self.y2 >= other.y1) | |||
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 create_room(room): | |||
global map | |||
#go through the tiles in the rectangle and make them passable | |||
for x in range(room.x1 + 1, room.x2): | |||
for y in range(room.y1 + 1, room.y2): | |||
map[x][y].blocked = False | |||
map[x][y].block_sight = False | |||
def create_h_tunnel(x1, x2, y): | |||
global map | |||
#horizontal tunnel. min() and max() are used in case x1>x2 | |||
for x in range(min(x1, x2), max(x1, x2) + 1): | |||
map[x][y].blocked = False | |||
map[x][y].block_sight = False | |||
def create_v_tunnel(y1, y2, x): | |||
global map | |||
#vertical tunnel | |||
for y in range(min(y1, y2), max(y1, y2) + 1): | |||
map[x][y].blocked = False | |||
map[x][y].block_sight = False | |||
def make_map(): | |||
global map, player | |||
#fill map with "blocked" tiles | |||
map = [[ Tile(True) | |||
for y in range(MAP_HEIGHT) ] | |||
for x in range(MAP_WIDTH) ] | |||
rooms = [] | |||
num_rooms = 0 | |||
for r in range(MAX_ROOMS): | |||
#random width and height | |||
w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE) | |||
h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE) | |||
#random position without going out of the boundaries of the map | |||
x = libtcod.random_get_int(0, 0, MAP_WIDTH - w - 1) | |||
y = libtcod.random_get_int(0, 0, MAP_HEIGHT - h - 1) | |||
#"Rect" class makes rectangles easier to work with | |||
new_room = Rect(x, y, w, h) | |||
#run through the other rooms and see if they intersect with this one | |||
failed = False | |||
for other_room in rooms: | |||
if new_room.intersect(other_room): | |||
failed = True | |||
break | |||
if not failed: | |||
#this means there are no intersections, so this room is valid | |||
#"paint" it to the map's tiles | |||
create_room(new_room) | |||
#center coordinates of new room, will be useful later | |||
(new_x, new_y) = new_room.center() | |||
if num_rooms == 0: | |||
#this is the first room, where the player starts at | |||
player.x = new_x | |||
player.y = new_y | |||
else: | |||
#all rooms after the first: | |||
#connect it to the previous room with a tunnel | |||
#center coordinates of previous room | |||
(prev_x, prev_y) = rooms[num_rooms-1].center() | |||
#draw a coin (random number that is either 0 or 1) | |||
if libtcod.random_get_int(0, 0, 1) == 1: | |||
#first move horizontally, then vertically | |||
create_h_tunnel(prev_x, new_x, prev_y) | |||
create_v_tunnel(prev_y, new_y, new_x) | |||
else: | |||
#first move vertically, then horizontally | |||
create_v_tunnel(prev_y, new_y, prev_x) | |||
create_h_tunnel(prev_x, new_x, new_y) | |||
#finally, append the new room to the list | |||
rooms.append(new_room) | |||
num_rooms += 1 | |||
Revision as of 05:24, 27 December 2009
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()
Dungeon building blocks
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 Rect: #a rectangle on the map. used to characterize a room. def __init__(self, x, y, w, h): self.x1 = x self.y1 = y self.x2 = x + w self.y2 = y + h 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 create_room(room): global map #go through the tiles in the rectangle and make them passable for x in range(room.x1 + 1, room.x2): for y in range(room.y1 + 1, room.y2): map[x][y].blocked = False map[x][y].block_sight = False def create_h_tunnel(x1, x2, y): global map #horizontal tunnel. min() and max() are used in case x1>x2 for x in range(min(x1, x2), max(x1, x2) + 1): map[x][y].blocked = False map[x][y].block_sight = False def create_v_tunnel(y1, y2, x): global map #vertical tunnel for y in range(min(y1, y2), max(y1, y2) + 1): map[x][y].blocked = False map[x][y].block_sight = False def make_map(): global map #fill map with "blocked" tiles map = [[ Tile(True) for y in range(MAP_HEIGHT) ] for x in range(MAP_WIDTH) ] #create two rooms room1 = Rect(20, 15, 10, 15) room2 = Rect(50, 15, 10, 15) create_room(room1) create_room(room2) #connect them with a tunnel create_h_tunnel(25, 55, 23) #place the player inside the first room player.x = 25 player.y = 23 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()
Dungeon generator
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 #parameters for dungeon generator ROOM_MAX_SIZE = 10 ROOM_MIN_SIZE = 6 MAX_ROOMS = 30 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 Rect: #a rectangle on the map. used to characterize a room. def __init__(self, x, y, w, h): self.x1 = x self.y1 = y self.x2 = x + w self.y2 = y + h def center(self): center_x = (self.x1 + self.x2) / 2 center_y = (self.y1 + self.y2) / 2 return (center_x, center_y) def intersect(self, other): #returns true if this rectangle intersects with another one return (self.x1 <= other.x2 and self.x2 >= other.x1 and self.y1 <= other.y2 and self.y2 >= other.y1) 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 create_room(room): global map #go through the tiles in the rectangle and make them passable for x in range(room.x1 + 1, room.x2): for y in range(room.y1 + 1, room.y2): map[x][y].blocked = False map[x][y].block_sight = False def create_h_tunnel(x1, x2, y): global map #horizontal tunnel. min() and max() are used in case x1>x2 for x in range(min(x1, x2), max(x1, x2) + 1): map[x][y].blocked = False map[x][y].block_sight = False def create_v_tunnel(y1, y2, x): global map #vertical tunnel for y in range(min(y1, y2), max(y1, y2) + 1): map[x][y].blocked = False map[x][y].block_sight = False def make_map(): global map, player #fill map with "blocked" tiles map = [[ Tile(True) for y in range(MAP_HEIGHT) ] for x in range(MAP_WIDTH) ] rooms = [] num_rooms = 0 for r in range(MAX_ROOMS): #random width and height w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE) h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE) #random position without going out of the boundaries of the map x = libtcod.random_get_int(0, 0, MAP_WIDTH - w - 1) y = libtcod.random_get_int(0, 0, MAP_HEIGHT - h - 1) #"Rect" class makes rectangles easier to work with new_room = Rect(x, y, w, h) #run through the other rooms and see if they intersect with this one failed = False for other_room in rooms: if new_room.intersect(other_room): failed = True break if not failed: #this means there are no intersections, so this room is valid #"paint" it to the map's tiles create_room(new_room) #center coordinates of new room, will be useful later (new_x, new_y) = new_room.center() if num_rooms == 0: #this is the first room, where the player starts at player.x = new_x player.y = new_y else: #all rooms after the first: #connect it to the previous room with a tunnel #center coordinates of previous room (prev_x, prev_y) = rooms[num_rooms-1].center() #draw a coin (random number that is either 0 or 1) if libtcod.random_get_int(0, 0, 1) == 1: #first move horizontally, then vertically create_h_tunnel(prev_x, new_x, prev_y) create_v_tunnel(prev_y, new_y, new_x) else: #first move vertically, then horizontally create_v_tunnel(prev_y, new_y, prev_x) create_h_tunnel(prev_x, new_x, new_y) #finally, append the new room to the list rooms.append(new_room) num_rooms += 1 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()