Advertisement
Guest User

libtcod/python2 tutorial

a guest
Oct 23rd, 2017
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.11 KB | None | 0 0
  1. import libtcodpy as libtcod
  2. import math
  3.  
  4. #actual size of the window
  5. SCREEN_WIDTH = 80
  6. SCREEN_HEIGHT = 50
  7.  
  8. #map size
  9. MAP_WIDTH = 80
  10. MAP_HEIGHT = 45
  11.  
  12. LIMIT_FPS = 20 #20 frames-per-second maximum
  13.  
  14. #dungeon generator params
  15. ROOM_MAX_SIZE = 10
  16. ROOM_MIN_SIZE = 6
  17. MAX_ROOMS = 30
  18. MAX_ROOM_MONSTERS = 3
  19.  
  20. FOV_ALGO = 0 #default FOV algorithm
  21. FOV_LIGHT_WALLS = True
  22. TORCH_RADIUS = 5
  23.  
  24. color_dark_wall = libtcod.Color(0, 0, 100)
  25. color_light_wall = libtcod.Color(130, 110, 50)
  26. color_dark_ground = libtcod.Color(50, 50, 150)
  27. color_light_ground = libtcod.Color(200, 180, 50)
  28.  
  29. class Tile:
  30. #tile of the map & its properties
  31. def __init__(self, blocked, block_sight = None):
  32. self.blocked = blocked
  33. #all tiles start unexplored
  34. self.explored = False
  35. #by default, if a tile is blocked, it blocks sight too
  36. if block_sight is None: block_sight = blocked
  37. self.block_sight = block_sight
  38.  
  39. class Rect:
  40. #used to characterize a room
  41. def __init__(self, x, y, w, h):
  42. self.x1 = x
  43. self.y1 = y
  44. self.x2 = x + w
  45. self.y2 = y + h
  46.  
  47. def center(self):
  48. center_x = (self.x1 + self.x2) / 2
  49. center_y = (self.y1 + self.y2) / 2
  50. return (center_x, center_y)
  51.  
  52. def intersect(self, other):
  53. #is true if this rect intersects with another
  54. return (self.x1 <= other.x2 and self.x2 >= other.x1 and
  55. self.y1 <= other.y2 and self.y2 >= other.y1)
  56.  
  57. class Object:
  58. #generic object: player, nme, item, stairs, etc. always an on-screen char
  59. def __init__(self, x, y, char, name, color, blocks=False, fighter=None, ai=None):
  60. self.x = x
  61. self.y = y
  62. self.char = char
  63. self.name = name
  64. self.color = color
  65. self.blocks = blocks
  66. self.fighter = fighter
  67. if self.fighter: #let the fighter component know who owns it
  68. self.fighter.owner = self
  69.  
  70. self.ai = ai
  71. if self.ai:
  72. self.ai.owner = self
  73.  
  74. def move(self, dx, dy):
  75. #move by the given amount
  76. if not is_blocked(self.x + dx, self.y + dy):
  77. self.x += dx
  78. self.y += dy
  79.  
  80. def move_towards(self, target_x, target_y):
  81. #vector from this obj to target, and distance
  82. dx = target_x - self.x
  83. dy = target_y - self.y
  84. distance = math.sqrt(dx ** 2 + dy ** 2)
  85.  
  86. #normalize it to length 1 to preserve direction, then round and convert to interger
  87. dx = int(round(dx / distance))
  88. dy = int(round(dy / distance))
  89. self.move(dx, dy)
  90.  
  91. def distance_to(self, other):
  92. #return the distance to another object
  93. dx = other.x - self.x
  94. dy = other.y = self.y
  95. return math.sqrt(dx ** 2 + dy ** 2)
  96.  
  97. def draw(self):
  98. if libtcod.map_is_in_fov(fov_map, self.x, self.y):
  99. #set color, draw char at its position
  100. libtcod.console_set_default_foreground(con, self.color)
  101. libtcod.console_put_char(con, self.x, self.y, self.char, libtcod.BKGND_NONE)
  102.  
  103. def clear(self):
  104. #erase char that represents this object
  105. libtcod.console_put_char(con, self.x, self.y, ' ', libtcod.BKGND_NONE)
  106.  
  107. class Fighter:
  108. #combat-related properties/methods (monster, player, npc)
  109. def __init__(self, hp, defense, power):
  110. self.max_hp = hp
  111. self.hp = hp
  112. self.defense = defense
  113. self.power = power
  114.  
  115. class BasicMonster:
  116. #AI for basic nme
  117. def take_turn(self):
  118. #BasicMonster takes turn. if you see it, it sees you
  119. monster = self.owner
  120. if libtcod.map_is_in_fov(fov_map, monster.x, monster.y):
  121. #move twd player if far away
  122. if monster.distance_to(player) >= 2:
  123. monster.move_towards(player.x, player.y)
  124. #close enough, attack if player still alive
  125. elif player.fighter.hp > 0:
  126. print 'The ' + monster.name + '`s attack barely misses you.'
  127.  
  128.  
  129. def is_blocked(x, y):
  130. #first test the map tile
  131. if map[x][y].blocked:
  132. return True
  133.  
  134. #now check for any blocking objects
  135. for object in objects:
  136. if object.blocks and object.x == x and object.y == y:
  137. return True
  138.  
  139. return False
  140.  
  141. def create_room(room):
  142. global map
  143. #go through tiles in rect and make them passable
  144. for x in range(room.x1 + 1, room.x2):
  145. for y in range(room.y1 + 1, room.y2):
  146. map[x][y].blocked = False
  147. map[x][y].block_sight = False
  148.  
  149. def create_h_tunnel(x1, x2, y):
  150. global map
  151. for x in range(min(x1, x2), max(x1, x2) + 1):
  152. map[x][y].blocked = False
  153. map[x][y].block_sight = False
  154.  
  155. def create_v_tunnel(y1, y2, x):
  156. global map
  157. for y in range(min(y1, y2), max(y1, y2) + 1):
  158. map[x][y].blocked = False
  159. map[x][y].block_sight = False
  160.  
  161. def make_map():
  162. global map, player
  163.  
  164. #fill map with blocked tiles
  165. map = [[ Tile(True)
  166. for y in range(MAP_HEIGHT) ]
  167. for x in range(MAP_WIDTH) ]
  168.  
  169. rooms = []
  170. num_rooms = 0
  171.  
  172. for r in range(MAX_ROOMS):
  173. #rando width and height
  174. w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
  175. h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
  176. #rando position inside map boundaries
  177. x = libtcod.random_get_int(0, 0, MAP_WIDTH - w - 1)
  178. y = libtcod.random_get_int(0, 0, MAP_HEIGHT - h - 1)
  179.  
  180. new_room = Rect(x, y, w, h)
  181.  
  182. #run through other rooms, see if they intersect
  183. failed = False
  184. for other_room in rooms:
  185. if new_room.intersect(other_room):
  186. failed = True
  187. break
  188.  
  189. if not failed:
  190. #meaning no intersections, so room is valid
  191. #"paint" it to map tiles
  192. create_room(new_room)
  193. #center coords of new room
  194. (new_x, new_y) = new_room.center()
  195.  
  196. if num_rooms == 0:
  197. #player start
  198. player.x = new_x
  199. player.y = new_y
  200. else:
  201. #all rooms after first: connect to prev room with tunnel
  202. (prev_x, prev_y) = rooms[num_rooms-1].center()
  203.  
  204. if libtcod.random_get_int(0, 0, 1) == 1:
  205. create_h_tunnel(prev_x, new_x, prev_y)
  206. create_v_tunnel(prev_y, new_y, new_x)
  207. else:
  208. create_v_tunnel(prev_y, new_y, prev_x)
  209. create_h_tunnel(prev_x, new_x, new_y)
  210.  
  211. #add content to this room
  212. place_objects(new_room)
  213. #append new room to list
  214. rooms.append(new_room)
  215. num_rooms += 1
  216.  
  217. def place_objects(room):
  218. #choose random number of monsters
  219. num_monsters = libtcod.random_get_int(0, 0, MAX_ROOM_MONSTERS)
  220.  
  221. for i in range(num_monsters):
  222. #choose random spot for this monster
  223. x = libtcod.random_get_int(0, room.x1, room.x2)
  224. y = libtcod.random_get_int(0, room.y1, room.y2)
  225.  
  226. #only place it if the tile is not blocked
  227. if not is_blocked(x, y):
  228. #80% chance to spawn orc
  229. if libtcod.random_get_int(0, 0, 100) < 80:
  230. #create an eel
  231. fighter_component = Fighter(hp=10, defense=0, power=3)
  232. ai_component = BasicMonster()
  233.  
  234. monster = Object(x, y, 'e', 'spiny eel', libtcod.light_green,
  235. blocks=True, fighter=fighter_component, ai=ai_component)
  236. else:
  237. #create a troll (siren)
  238. fighter_component = Fighter(hp=16, defense=1, power=4)
  239. ai_component = BasicMonster()
  240.  
  241. monster = Object(x, y, 's', 'siren', libtcod.lighter_red,
  242. blocks=True, fighter=fighter_component, ai=ai_component)
  243.  
  244. objects.append(monster)
  245.  
  246. def render_all():
  247. global fov_map, color_dark_wall, color_light_wall
  248. global color_dark_ground, color_light_ground
  249. global fov_recompute
  250.  
  251. if fov_recompute:
  252. #recomp fov if needed (player moved, etc)
  253. fov_recompute = False
  254. libtcod.map_compute_fov(fov_map, player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO)
  255.  
  256. #go through all tiles and set bg color according to FOV
  257. for y in range(MAP_HEIGHT):
  258. for x in range(MAP_WIDTH):
  259. visible = libtcod.map_is_in_fov(fov_map, x, y)
  260. wall = map[x][y].block_sight
  261. if not visible:
  262. #how it looks after it's been explored
  263. if map[x][y].explored:
  264. if wall:
  265. libtcod.console_put_char_ex(con, x, y, '#', libtcod.dark_han, libtcod.darkest_han)
  266. else:
  267. libtcod.console_put_char_ex(con, x, y, '.', libtcod.darker_blue, libtcod.darkest_blue)
  268. else:
  269. #it's visible right now
  270. if wall:
  271. libtcod.console_put_char_ex(con, x, y, '#', libtcod.light_azure, libtcod.dark_azure)
  272. else:
  273. libtcod.console_put_char_ex(con, x, y, '.', libtcod.dark_azure, libtcod.darker_azure)
  274. #since is visible, explore it
  275. map[x][y].explored = True
  276.  
  277. #draw all objs in list
  278. for object in objects:
  279. object.draw()
  280.  
  281. libtcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
  282.  
  283. def player_move_or_attack(dx, dy):
  284. global fov_recompute
  285.  
  286. #the coordinates the player is moving to/attacking
  287. x = player.x + dx
  288. y = player.y + dy
  289.  
  290. #try to find an attackable object there
  291. target = None
  292. for object in objects:
  293. if object.x == x and object.y == y:
  294. target = object
  295. break
  296.  
  297. #attack if target found, move otherwise
  298. if target is not None:
  299. print 'The ' + target.name + ' swims effortlessly around your blow.'
  300. else:
  301. player.move(dx, dy)
  302. fov_recompute = True
  303.  
  304. def handle_keys():
  305. key = libtcod.console_wait_for_keypress(True)
  306.  
  307. if key.vk == libtcod.KEY_ENTER and key.lalt:
  308. #Alt+Enter: toggle fullscreen
  309. libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
  310.  
  311. elif key.vk == libtcod.KEY_ESCAPE:
  312. return 'exit' #exit game
  313.  
  314. if game_state == 'playing':
  315. #movement keys
  316. if libtcod.console_is_key_pressed(libtcod.KEY_KP8):
  317. player_move_or_attack(0, -1)
  318. fov_recompute = True
  319.  
  320. elif libtcod.console_is_key_pressed(libtcod.KEY_KP2):
  321. player.move(0, 1)
  322. fov_recompute = True
  323.  
  324. elif libtcod.console_is_key_pressed(libtcod.KEY_KP4):
  325. player_move_or_attack(-1, 0)
  326. fov_recompute = True
  327.  
  328. elif libtcod.console_is_key_pressed(libtcod.KEY_KP6):
  329. player_move_or_attack(1, 0)
  330. fov_recompute = True
  331.  
  332. elif libtcod.console_is_key_pressed(libtcod.KEY_KP9):
  333. player_move_or_attack(1,-1)
  334. fov_recompute = True
  335.  
  336. elif libtcod.console_is_key_pressed(libtcod.KEY_KP1):
  337. player_move_or_attack(-1, 1)
  338. fov_recompute = True
  339.  
  340. elif libtcod.console_is_key_pressed(libtcod.KEY_KP3):
  341. player_move_or_attack(1, 1)
  342. fov_recompute = True
  343.  
  344. elif libtcod.console_is_key_pressed(libtcod.KEY_KP7):
  345. player_move_or_attack(-1, -1)
  346. fov_recompute = True
  347.  
  348. else:
  349. return 'didnt-take-turn'
  350.  
  351.  
  352.  
  353. #############################################
  354. # Initialization & Main Loop
  355. #############################################
  356.  
  357. libtcod.console_set_custom_font('sin8x8.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_ASCII_INROW)
  358. libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'python/libtcod tutorial', False)
  359. con = libtcod.console_new(SCREEN_WIDTH, SCREEN_HEIGHT)
  360. libtcod.sys_set_fps(LIMIT_FPS)
  361.  
  362. #create obj representing player
  363. fighter_component = Fighter(hp=30, defense=2, power=5)
  364. player = Object(0, 0, '@', 'player', libtcod.lightest_cyan, blocks=True, fighter=fighter_component)
  365.  
  366. #list of objs with just player
  367. objects = [player]
  368. #gen map but not drawn to screen yet
  369. make_map()
  370.  
  371. #create the FOV map, according to the generated map
  372. fov_map = libtcod.map_new(MAP_WIDTH, MAP_HEIGHT)
  373. for y in range(MAP_HEIGHT):
  374. for x in range(MAP_WIDTH):
  375. libtcod.map_set_properties(fov_map, x, y, not map[x][y].block_sight, not map[x][y].blocked)
  376.  
  377.  
  378. fov_recompute = True
  379. game_state = 'playing'
  380. player_action = None
  381.  
  382. while not libtcod.console_is_window_closed():
  383.  
  384. render_all()
  385.  
  386. libtcod.console_flush()
  387. #erase objs at old location, before they move
  388. for object in objects:
  389. object.clear()
  390.  
  391. #handle keys and exit game if needed
  392. player_action = handle_keys()
  393. if player_action == 'exit':
  394. break
  395.  
  396. #let nmes take their turn
  397. if game_state == 'playing' and player_action != 'didnt-take-turn':
  398. for object in objects:
  399. if object.ai:
  400. object.ai.take_turn()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement