Advertisement
Guest User

Untitled

a guest
Jul 7th, 2013
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 26.06 KB | None | 0 0
  1. import libtcodpy as libtcod
  2. import math
  3. import textwrap
  4. import shelve
  5.  
  6. #screen settings
  7. SCREEN_WIDTH = 80
  8. SCREEN_HEIGHT = 50
  9.  
  10. #map settings
  11. MAP_WIDTH = 80
  12. MAP_HEIGHT = 43
  13.  
  14. #dungeon room settings
  15. ROOM_MAX_SIZE = 10
  16. ROOM_MIN_SIZE = 6
  17. MAX_ROOMS = 30
  18. MAX_ROOM_MONSTERS = 1
  19. MAX_ROOM_ITEMS = 2
  20.  
  21. #panel settings
  22. BAR_WIDTH = 20
  23. PANEL_HEIGHT = 7
  24. PANEL_Y = SCREEN_HEIGHT - PANEL_HEIGHT
  25. MSG_X = BAR_WIDTH + 2
  26. MSG_WIDTH = SCREEN_WIDTH - BAR_WIDTH - 2
  27. MSG_HEIGHT = PANEL_HEIGHT - 1
  28. #inventory window width
  29. INVENTORY_WIDTH = 50
  30.  
  31. #amount healing potion heals with
  32. HEAL_AMOUNT = 4
  33.  
  34. LIGHTNING_DAMAGE = 20
  35. LIGHTNING_RANGE = 5
  36. CONFUSE_RANGE = 8
  37. CONFUSE_NUM_TURNS = 10
  38. FIREBALL_RADIUS = 3
  39. FIREBALL_DAMAGE = 12
  40.  
  41. #field-of-vision
  42. FOV_ALGO = 0
  43. FOV_LIGHT_WALLS = True
  44. TORCH_RADIUS = 10
  45.  
  46. #frames per second
  47. LIMIT_FPS = 20
  48.  
  49. #colors of the dungeon
  50. color_dark_wall = libtcod.Color(30, 30, 30)
  51. color_dark_ground = libtcod.Color(20, 20, 20)
  52. color_light_wall = libtcod.Color(60, 60, 60)
  53. color_light_ground = libtcod.Color(40, 40, 40)
  54.        
  55. #generalization of objects
  56. class Tile:
  57.     #a tile of the map and its properties
  58.     def __init__(self, blocked, block_sight = None):
  59.        
  60.         self.blocked = blocked
  61.         self.explored = False
  62.  
  63.         #by default, if a tile is blocked, it also blacks sight
  64.         if block_sight is None: block_sight = blocked
  65.         self.block_sight = block_sight
  66.  
  67. class Rect:
  68.     #a rectangle on the map. used to characterize a room
  69.     def __init__(self, x, y, w, h):
  70.         self.x1 = x
  71.         self.y1 = y
  72.         self.x2 = x + w
  73.         self.y2 = y + h
  74.  
  75.     def center(self):
  76.         center_x = (self.x1 + self.x2)/ 2
  77.         center_y = (self.y1 + self.y2) / 2
  78.         return (center_x, center_y)
  79.  
  80.     def intersect(self, other):
  81.         #returns true if this rectangle intersects with another one
  82.         return(self.x1 <= other.x2 and self.x2 >= other.x1 and
  83.                self.y1 <= other.y2 and self.y2 >= other.y1)
  84.        
  85. class Object:
  86.     #this is a generic object
  87.     #it's always represented by a character on screen
  88.     def __init__(self, x, y, char, name, color, blocks=False, fighter=None, ai=None, item=None):
  89.         self.x = x
  90.         self.y = y
  91.         self.char = char
  92.         self.name = name
  93.         self.color = color
  94.         self.blocks = blocks
  95.         self.fighter = fighter
  96.         if self.fighter:
  97.             self.fighter.owner = self
  98.  
  99.         self.ai = ai
  100.         if self.ai:
  101.             self.ai.owner = self
  102.  
  103.         self.item = item
  104.         if self.item:
  105.             self.item.owner = self
  106.  
  107.     def move(self, dx, dy):
  108.         if not is_blocked(self.x + dx, self.y + dy):
  109.         #move by the given amount
  110.             self.x += dx
  111.             self.y += dy
  112.  
  113.     def move_towards(self, target_x, target_y):
  114.         dx = target_x - self.x
  115.         dy = target_y - self.y
  116.         distance = math.sqrt(dx **2 + dy ** 2)
  117.  
  118.         dx = int(round(dx / distance))
  119.         dy = int(round(dy / distance))
  120.         self.move(dx, dy)
  121.  
  122.     def distance_to(self, other):
  123.         dx = other.x - self.x
  124.         dy = other.y - self.y
  125.         return math.sqrt(dx ** 2 + dy ** 2)
  126.  
  127.     def distance(self, x, y):
  128.         return math.sqrt((x - self.x) ** 2 + (y - self.y) ** 2)
  129.  
  130.     def send_to_back(self):
  131.         global objects
  132.         objects.remove(self)
  133.         objects.insert(0, self)
  134.  
  135.     def draw(self):
  136.         if libtcod.map_is_in_fov(fov_map, self.x, self.y):
  137.             #set the color and then draw the character that represents this object at its position
  138.             libtcod.console_set_default_foreground(con, self.color)
  139.             libtcod.console_put_char(con,self.x, self.y, self.char, libtcod.BKGND_NONE)
  140.        
  141.            
  142.     def clear(self):
  143.         #erase the character that represents this object
  144.         libtcod.console_put_char(con, self.x, self.y, ' ', libtcod.BKGND_NONE)
  145.  
  146.  
  147. def initialize_fov():
  148.     global fov_recompute, fov_map
  149.     fov_recompute = True
  150.  
  151.     fov_map = libtcod.map_new(MAP_WIDTH, MAP_HEIGHT)
  152.     for y in range(MAP_HEIGHT):
  153.         for x in range(MAP_WIDTH):
  154.             libtcod.map_set_properties(fov_map, x, y, not map[x][y].block_sight, not map[x][y].blocked)
  155.     libtcod.console_clear(con)
  156.  
  157. def play_game():
  158.     global key, mouse
  159.  
  160.     player_action = None
  161.  
  162.     mouse = libtcod.Mouse()
  163.     key = libtcod.Key()
  164.     while not libtcod.console_is_window_closed():
  165.  
  166.         libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS|libtcod.EVENT_MOUSE, key, mouse)
  167.         render_all()
  168.  
  169.         libtcod.console_flush()
  170.  
  171.         for object in objects:
  172.             object.clear()
  173.  
  174.             player_action = handle_keys()
  175.             if player_action == 'exit':
  176.                 save_game()
  177.                 break
  178.  
  179.         if game_state == 'playing' and player_action == 'didnt-take-turn':
  180.             for object in objects:
  181.                 if object.ai:
  182.                     object.ai.take_turn()
  183.  
  184. def main_menu():
  185.     img = libtcod.image_load('menu_background.png')
  186.  
  187.     while not libtcod.console_is_window_closed():
  188.         libtcod.image_blit_2x(img, 0, 0, 0)
  189.  
  190.         libtcod.console_set_default_foreground(0, libtcod.light_yellow)
  191.         libtcod.console_print_ex(0, SCREEN_WIDTH/2, SCREEN_HEIGHT/2-4, libtcod.BKGND_NONE, libtcod.CENTER, 'TOMBS OF THE ANCIENT KINGS')
  192.         libtcod.console_print_ex(0, SCREEN_WIDTH/2, SCREEN_HEIGHT-2, libtcod.BKGND_NONE, libtcod.CENTER, 'By Me')
  193.         choice = menu('', ['Play a new game', 'Continue last game', 'Quit'], 24)
  194.  
  195.         if choice == 0:
  196.             new_game()
  197.             play_game()
  198.         if choice == 1:
  199.             try:
  200.                 load_game()
  201.             except:
  202.                 msgbox('\n No saved game to load.\n', 24)
  203.                 continue
  204.             play_game()
  205.         elif choice == 2:
  206.             break
  207.            
  208. class Fighter:
  209.     #combat-related properties and methods (monster, player, NPC)
  210.     def __init__(self, hp, defense, power, death_function = None):
  211.         self.max_hp = hp
  212.         self.hp = hp
  213.         self. defense = defense
  214.         self.power = power
  215.         self.death_function = death_function
  216.  
  217.     def attack(self, target):
  218.         #a simple formula for attack damage
  219.         damage = self.power - target.fighter.defense
  220.  
  221.         if damage > 0:
  222.             message(self.owner.name.capitalize() + 'attacks ' + target.name + ' for ' + str(damage) + ' hit points.')
  223.             target.fighter.take_damage(damage)
  224.         else:
  225.             message(self.owner.name.capitalize() + ' attacks ' + target.name + ' but it has no effect!')
  226.  
  227.     def take_damage(self, damage):
  228.         if damage > 0:
  229.             self.hp -= damage
  230.  
  231.             if self.hp <= 0:
  232.                 function = self.death_funtion
  233.                 if function is not None:
  234.                     function(self.owner)
  235.  
  236.     def heal(self, amount):
  237.         self.hp += amount
  238.         if self.hp > self.max_hp:
  239.             self.hp = self.max_hp
  240.  
  241. class BasicMonster:
  242.     #AI for a basic monster
  243.     def take_turn(self):
  244.         monster = self.owner
  245.         if libtcod.map_is_in_fov(fov_map, monster.x, monster.y):
  246.             if monster.distance_to(player) >= 2:
  247.                 monster.move_towards(player.x, player.y)
  248.  
  249.             elif player.fighter.hp > 0:
  250.                 monster.fighter.attack(player)
  251.  
  252. class ConfusedMonster:
  253.     def __init__(self, old_ai, num_turns=CONFUSE_NUM_TURNS):
  254.         self.old_ai = old_ai
  255.         self.num_turns = num_turns
  256.  
  257.     def take_turn(self):
  258.         if self.num_turns > 0:
  259.             self.owner.move(libtcod.random_get_int(0, -1, 1), libtcod.random_get_int(0, -1, 1))
  260.             self.num_turns -= 1
  261.         else:
  262.             self.owner.ai = self.old_ai
  263.             message('The ' + self.owner.name + ' is no longer confused!', libtcod.red)  
  264.  
  265. class Item:
  266.     def __init__(self, use_function=None):
  267.         self.use_function = use_function
  268.  
  269.     def pick_up(self):
  270.         if len(inventory) >= 26:
  271.             message('Your inventory is full, cannot pick up ' + self.owner.name + '.', libtcod.red)
  272.         else:
  273.             inventory.append(self.owner)
  274.             objects.remove(self.owner)
  275.             message('You picked up a ' + self.owner.name + '!', libtcod.green)
  276.  
  277.     def drop(self):
  278.         objects.append(self.owner)
  279.         inventory.remove(self.owner)
  280.         self.owner.x = player.x
  281.         self. owner.y = player.y
  282.         message('You dropped a ' + self.owner.name + '.', libtcod.yellow)
  283.  
  284.     def use(self):
  285.         if self.use_function is None:
  286.             message('The ' + self.owner.name + ' cannot be used.')
  287.         else:
  288.             if self.use_function() != 'cancelled':
  289.                 inventory.remove(self.owner)
  290.  
  291. def is_blocked(x, y):
  292.     #first test the map tile
  293.     if map[x][y].blocked:
  294.         return True
  295.  
  296.     for object in objects:
  297.         if object.blocks and object.x == x and object.y == y:
  298.             return True
  299.  
  300.     return False
  301.  
  302. def create_room(room):
  303.     global map
  304.     #go through the tiles in the rectangle and make them passable
  305.     for x in range(room.x1 + 1, room.x2):
  306.         for y in range(room.y1 + 1, room.y2):
  307.             map[x][y].blocked = False
  308.             map[x][y].block_sight = False
  309.  
  310. def create_h_tunnel(x1, x2, y):
  311.     global map
  312.     #horizontal tunnel. min() and max() are used in case x1>x2
  313.     for x in range(min(x1, x2), max(x1, x2) + 1):
  314.         map[x][y].blocked = False
  315.         map[x][y].block_sight = False
  316.  
  317. def create_v_tunnel(y1, y2, x):
  318.     global map
  319.     #vertical tunnel
  320.     for y in range(min(y1, y2), max(y1, y2) + 1):
  321.         map[x][y].blocked = False
  322.         map[x][y].block_sight = False
  323.  
  324.                
  325. def make_map():
  326.     global map, objects
  327.  
  328.     objects = [player]
  329.  
  330.     #fill map with blocked tiles
  331.     map = [[ Tile(True)
  332.         for y in range(MAP_HEIGHT)]
  333.            for x in range(MAP_WIDTH)]
  334.    
  335.     rooms = []
  336.     num_rooms = 0
  337.  
  338.     for r in range (MAX_ROOMS):
  339.         #random width and height
  340.         w = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
  341.         h = libtcod.random_get_int(0, ROOM_MIN_SIZE, ROOM_MAX_SIZE)
  342.         #random position without going out of the boundaries of the map
  343.         x = libtcod.random_get_int(0, 0, MAP_WIDTH - w - 1)
  344.         y = libtcod.random_get_int(0, 0, MAP_HEIGHT - h - 1)
  345.  
  346.         #"Rect" class makes rectangles easier to work with
  347.         new_room = Rect(x, y, w, h)
  348.  
  349.         #run through the other rooms and see if they intersect with this one
  350.         failed = False
  351.         for other_room in rooms:
  352.             if new_room.intersect(other_room):
  353.                 failed = True
  354.                 break
  355.  
  356.         if not failed:
  357.             #this means there are no intersections, so this room is valid
  358.  
  359.             #"paint" it to the map's tiles
  360.             create_room(new_room)
  361.             place_objects(new_room)
  362.  
  363.             #center coordinates of new room, will be useful later
  364.             (new_x, new_y) = new_room.center()
  365.          
  366.  
  367.             if num_rooms == 0:
  368.                 #this is the first room, where the player start at
  369.                 player.x = new_x
  370.                 player.y = new_y
  371.             else:
  372.                 #all rooms after the first:
  373.                 #connect it to the previous room with a tunnel
  374.  
  375.                 #center coordinates of previous room
  376.                 (prev_x, prev_y) = rooms[num_rooms-1].center()
  377.  
  378.                 #draw a coin(random number that is either 0 or 1)
  379.                 if libtcod.random_get_int(0,0,1) == 1:
  380.                     #first move horizontally, then vertically
  381.                     create_h_tunnel(prev_x, new_x, prev_y)
  382.                     create_v_tunnel(prev_y, new_y, new_x)
  383.                 else:
  384.                     #first move vertically, then horizontally
  385.                     create_v_tunnel(prev_y, new_y, prev_x)
  386.                     create_h_tunnel(prev_x, new_x, new_y)
  387.  
  388.             #finally, append the new room to the list
  389.             rooms.append(new_room)
  390.             num_rooms += 1
  391.  
  392. def place_objects(room):
  393.  
  394.     num_monsters = libtcod.random_get_int(0, 0, MAX_ROOM_MONSTERS)
  395.  
  396.     for i in range(num_monsters):
  397.         x = libtcod.random_get_int(0, room.x1, room.x2-1)
  398.         y = libtcod.random_get_int(0, room.y1, room.y2-1)
  399.  
  400.         if not is_blocked(x, y):
  401.             if libtcod.random_get_int(0, 0, 100) < 80:
  402.                 fighter_component = Fighter(hp = 10, defense = 0, power = 3, death_function = monster_death)
  403.                 ai_component = BasicMonster()
  404.                
  405.                 monster = Object(x, y, 'G', ' Guard', libtcod.white, blocks = True, fighter = fighter_component, ai = ai_component)
  406.  
  407.             else:
  408.                 fighter_component = Fighter(hp=16, defense = 1, power = 4, death_function = monster_death)
  409.                 ai_component = BasicMonster()
  410.  
  411.                 monster = Object(x, y, 'E', ' Elemental ', libtcod.green, blocks = True, fighter = fighter_component, ai = ai_component)
  412.  
  413.             objects.append(monster)
  414.  
  415.     num_items = libtcod.random_get_int(0, 0, MAX_ROOM_ITEMS)
  416.  
  417.     for i in range(num_items):
  418.         x = libtcod.random_get_int(0, room.x1+1, room.x2-1)
  419.         y = libtcod.random_get_int(0, room.y1+1, room.y2-1)
  420.  
  421.         if not is_blocked(x, y):
  422.             dice = libtcod.random_get_int(0, 0, 100)
  423.             if dice < 70:
  424.                 item_component = Item(use_function=cast_heal)
  425.  
  426.                 item = Object(x, y, '!', 'healing potion', libtcod.violet, item=item_component)
  427.             elif dice < 70+10:
  428.                 item_component = Item(use_function=cast_lightning)
  429.                 item = Object(x, y, '#', 'scroll of lighting bolt', libtcod.light_yellow, item=item_component)
  430.             elif dice < 70+10+10:
  431.                 item_component = Item(use_function=cast_confuse)
  432.                 item = Object(x, y, '#', 'scroll of confusion', libtcod.light_yellow, item=item_component)
  433.             else:
  434.                 item_component = Item(use_function=cast_confuse)
  435.                 item = Object(x, y, '#', 'scroll of confusion', libtcod.light_yellow, item=item_component)
  436.             objects.append(item)
  437.             item.send_to_back()
  438.  
  439. def render_bar(x, y, total_width, name, value, maximum, bar_color, back_color):
  440.     bar_width = int(float(value) / maximum * (total_width))
  441.  
  442.     libtcod.console_set_default_background(panel, back_color)
  443.     libtcod.console_rect(panel, x, y, total_width, 1, False, libtcod.BKGND_SCREEN)
  444.  
  445.     libtcod.console_set_default_background(panel, bar_color)
  446.     if bar_width > 0:
  447.         libtcod.console_rect(panel, x, y, bar_width, 1, False, libtcod.BKGND_SCREEN)
  448.     libtcod.console_set_default_foreground(panel, libtcod.white)
  449.     libtcod.console_print_ex(panel, x + total_width / 2, y, libtcod.BKGND_NONE, libtcod.CENTER, name + ': ' + str(value) + '/' + str(maximum))
  450.  
  451. def get_names_under_mouse():
  452.     global mouse
  453.  
  454.     (x, y)= (mouse.cx, mouse.cy)
  455.  
  456.     names = [obj.name for obj in objects
  457.         if obj.x == x and obj.y == y and libtcod.map_is_in_fov(fov_map, obj.x, obj.y)]
  458.  
  459.     names = ', '. join(names)
  460.     return names.capitalize()
  461.  
  462. def render_all():
  463.     #colors of wall and ground
  464.     global fov_map, color_dark_wall, color_light_wall
  465.     global color_dark_ground, color_light_ground
  466.     global fov_recompute
  467.  
  468.     if fov_recompute:
  469.         fov_recompute = True
  470.         libtcod.map_compute_fov(fov_map, player.x, player.y, TORCH_RADIUS, FOV_LIGHT_WALLS, FOV_ALGO)
  471.  
  472.         #go through all tiles, and set their background color
  473.         for y in range(MAP_HEIGHT):
  474.             for x in range(MAP_WIDTH):
  475.                 visible = libtcod.map_is_in_fov(fov_map, x, y)
  476.                 wall = map[x][y].block_sight
  477.                 if not visible:
  478.                     if map[x][y].explored:
  479.                         if wall:
  480.                             libtcod.console_set_char_background(con, x, y, color_dark_wall, libtcod.BKGND_SET)
  481.                         else:
  482.                             libtcod.console_set_char_background(con, x, y, color_dark_ground, libtcod.BKGND_SET)
  483.                 else:
  484.                     if wall:
  485.                         libtcod.console_set_char_background(con, x, y, color_light_wall, libtcod.BKGND_SET)
  486.                     else:
  487.                         libtcod.console_set_char_background(con, x, y, color_light_ground, libtcod.BKGND_SET)
  488.                     map[x][y].explored = True
  489.  
  490.     for object in objects:
  491.         if object != player:
  492.             object.draw()
  493.  
  494.     player.draw()
  495.    
  496.     libtcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
  497.  
  498.     libtcod.console_set_default_background(panel, libtcod.black)
  499.     libtcod.console_clear(panel)
  500.  
  501.     y = 1
  502.     for(line, color) in game_msgs:
  503.         libtcod.console_set_default_foreground(panel, color)
  504.         libtcod.console_print_ex(panel, MSG_X, y, libtcod.BKGND_NONE, libtcod.LEFT,line)
  505.         y += 1
  506.  
  507.     render_bar(1, 1, BAR_WIDTH, 'HP', player.fighter.hp, player.fighter.max_hp, libtcod.light_red, libtcod.darker_red)
  508.  
  509.     libtcod.console_set_default_foreground(panel, libtcod.light_gray)
  510.     libtcod.console_print_ex(panel, 1, 0, libtcod.BKGND_NONE, libtcod.LEFT, get_names_under_mouse())
  511.  
  512.     libtcod.console_blit(panel, 0, 0, SCREEN_WIDTH, PANEL_HEIGHT, 0, 0, PANEL_Y)
  513.        
  514. def player_move_or_attack(dx, dy):
  515.     global fov_recompute
  516.  
  517.     x = player.x + dx
  518.     y = player.y + dy
  519.  
  520.     target = None
  521.     for object in objects:
  522.         if object.fighter and object.x == x and object.y == y:
  523.             target = object
  524.             break
  525.  
  526.     if target is not None:
  527.         player.fighter.attack(target)
  528.     else:
  529.         player.move(dx, dy)
  530.         fov_recompute = True
  531.  
  532. def menu(header, options, width):
  533.     if len(options) > 26: raise ValueError('Cannot have a menu with more than 26 options.')
  534.  
  535.     header_height = libtcod.console_get_height_rect(con, 0, 0, width, SCREEN_HEIGHT, header)
  536.     if header == '':
  537.         header_height = 0
  538.     height = len(options) + header_height
  539.  
  540.     window = libtcod.console_new(width, height)
  541.  
  542.     libtcod.console_set_default_foreground(window, libtcod.white)
  543.     libtcod.console_print_rect_ex(window, 0, 0, width, height, libtcod.BKGND_NONE, libtcod.LEFT, header)
  544.  
  545.     y = header_height
  546.     letter_index = ord('a')
  547.     for option_text in options:
  548.         text = '(' + chr(letter_index) + ')' + option_text
  549.         libtcod.console_print_ex(window, 0, y, libtcod.BKGND_NONE, libtcod.LEFT, text)
  550.         y += 1
  551.         letter_index += 1
  552.  
  553.     x = SCREEN_WIDTH/2 - width/2
  554.     y = SCREEN_HEIGHT/2 - height/2
  555.     libtcod.console_blit(window, 0, 0, width, height, 0, x, y, 1.0, 0.7)
  556.  
  557.     libtcod.console_flush()
  558.     key = libtcod.console_wait_for_keypress(True)
  559.  
  560.     if key.vk == libtcod.KEY_ENTER and key.lalt:
  561.         libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
  562.  
  563.     index = key.c - ord('a')
  564.     if index >= 0 and index < len(options): return index
  565.     return None
  566.  
  567. def inventory_menu(header):
  568.     if len(inventory) == 0:
  569.         options = ['Inventory is empty.']
  570.     else:
  571.         options = [item.name for item in inventory]
  572.  
  573.     index = menu(header, options, INVENTORY_WIDTH)
  574.  
  575.     if index is None or len(inventory) == 0: return None
  576.     return inventory[index].item
  577.  
  578. def msgbox(text, width=50):
  579.     menu(text, [], width)
  580.  
  581. #function for key input
  582. def handle_keys():
  583.     #key = libtcod.console_check_for_keypress() #real-time
  584.     key = libtcod.console_wait_for_keypress(True) #turn-based
  585.    
  586.     if key.vk == libtcod.KEY_ENTER and key.lalt:
  587.         #Alt+Enter: toggle fullscreen
  588.         libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
  589.  
  590.     elif key.vk == libtcod.KEY_ESCAPE:
  591.         #exit game
  592.         return 'exit'
  593.  
  594.     if game_state == 'playing':
  595.         #movement keys
  596.         if libtcod.console_is_key_pressed(libtcod.KEY_UP):
  597.             player.move(0, -1)
  598.  
  599.         elif libtcod.console_is_key_pressed(libtcod.KEY_DOWN):
  600.             player.move(0, 1)
  601.  
  602.         elif libtcod.console_is_key_pressed(libtcod.KEY_LEFT):
  603.             player.move(-1, 0)
  604.  
  605.         elif libtcod.console_is_key_pressed(libtcod.KEY_RIGHT):
  606.             player.move(1, 0)
  607.         else:
  608.             key_char = chr(key.c)
  609.  
  610.             if key_char == 'g':
  611.                 for object in objects:
  612.                     if object.x == player.x and object.y == player.y and object.item:
  613.                         object.item.pick_up()
  614.                         break
  615.             if key_char == 'i':
  616.                 chosen_item = inventory_menu('Press the key next to an item to se it, or any other to cancel.\n')
  617.                 if chosen_item is not None:
  618.                     chosen_item.use()
  619.  
  620.             if key_char == 'd':
  621.                 chosen_item = invetory_menu('Press the key next to an item to drop it, or any other to cancel.\n')
  622.                 if chosen_item is not None:
  623.                     chosen_item.drop()
  624.                
  625.             return 'didnt-take-turn'
  626.  
  627. def player_death(player):
  628.     global game_state
  629.     print 'You died!'
  630.     game_state = 'dead'
  631.  
  632.     player.char = '%'
  633.     player.color = libtcod.dark_red
  634.  
  635. def monster_death(monster):
  636.     message(monster.name.capitalize() + ' is dead!', libtcod.orange)
  637.     monster.char = '%'
  638.     monster.color = libtcod.dark_red
  639.     monster.blocks = False
  640.     monster.fight = None
  641.     monster.ai = None
  642.     monster.name = 'remains of ' + monster.name
  643.     monster.send_to_back()
  644.  
  645. def closest_monster(max_range):
  646.     closest_enemy = None
  647.     closest_dist = max_range + 1
  648.  
  649.     for object in objects:
  650.         if object.fighter and not object == player and libtcod.map_is_in_fov(fov_map, object.x, object.y):
  651.             dist = player.distance_to(object)
  652.             if dist < closest_dist:
  653.                 closest_enemy = object
  654.                 closest_dist = dist
  655.     return closest_enemy
  656.            
  657.  
  658. def cast_heal():
  659.     if player.fighter.hp == player.fighter.max_hp:
  660.         message('You are already at full health.', libtcod.red)
  661.         return 'cancelled'
  662.     message('Your wounds start to feel better!', libtcod.light_violet)
  663.     player.fighter.heal(HEAL_AMOUNT)
  664.  
  665. def cast_lightning():
  666.     monster = closest_monster(LIGHTNING_RANGE)
  667.     if monster is None:
  668.         message('No enemy is close enough to strike.', libtcod.red)
  669.         return 'cancelled'
  670.  
  671.     message('A lighting bolt strikes the ' + monster.name + 'with a loud thunder! The damage is ' + str(LIGHTNING_DAMAGE) + ' hit points.', libtcod.light_blue)
  672.     monster.fighter.take_damage(LIGHTNING_DAMAGE)
  673.  
  674. def cast_fireball():
  675.     message('Left-click a target tile for the fireball, or right-click to cance.', libtcod.light_cyan)
  676.     (x, y) = target_tile()
  677.     if x is None: return 'cancelled'
  678.     message('The fireball explodes, burning everything within ' + str(FIREBALL_RADIUS) + ' tiles!', libtcod.orange)
  679.  
  680.     for obj in objects:
  681.         if obj.distance(x, y) <= FIREBALL_RADIUS and obj.fighter:
  682.             message('The ' + obj.name + ' gets burned for ' + str(FIREBALL_DAMAGE) + ' hit points.', libtcod.orange)
  683.             obj.fighter.take_damage(FIREBALL_DAMAGE)
  684.  
  685. def cast_confuse():
  686.     message('Left-click an enemy to confuse it, or right-click to cancel.', libtcod.light_cyan)
  687.     monster = target_monster(CONFUSE_RANGE)
  688.     if monster is None: return 'cancelled'
  689.  
  690.     old_ai = monster.ai
  691.     monster.ai = ConfusedMonster(old_ai)
  692.     monster.ai.owner = monster
  693.     message('The eyes of the ' + monster.name + ' look vacant, as he starts to stumble around!', libtcod.light_green)
  694.  
  695. def message(new_msg, color = libtcod.white):
  696.     new_msg_lines = textwrap.wrap(new_msg, MSG_WIDTH)
  697.  
  698.     for line in new_msg_lines:
  699.         if len(game_msgs) == MSG_HEIGHT:
  700.             del game_msgs[0]
  701.  
  702.         game_msgs.append((line, color))
  703.  
  704. def target_tile(max_range=None):
  705.     global key, mouse
  706.     while True:
  707.         libtcod.console_flush()
  708.         libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS|libtcod.EVENT_MOUSE, key, mouse)
  709.         render_all()
  710.         (x, y) = (mouse.cx, mouse.cy)
  711.  
  712.         if mouse.rbutton_pressed or key.vk == libtcod.KEY_ESCAPE:
  713.             return (None, None)
  714.  
  715.         if (mouse.lbutton_pressed and libtcod.map_is_in_fov(fov_map, x, y) and
  716.             (max_range is None or player.distance(x, y) <= max_range)):
  717.             return(x, y)
  718.  
  719. def target_monster(max_range=None):
  720.     while True:
  721.         (x, y) = target_tile(max_range)
  722.         if x is None:
  723.             return None
  724.  
  725. def new_game():
  726.     global player, inventory, game_msgs, game_state
  727.  
  728.     fighter_component = Fighter(hp=30, defense=2, power=5, death_function=player_death)
  729.     player = Object(0, 0, '@', 'player', libtcod.white, blocks=True, fighter=fighter_component)
  730.  
  731.     make_map()
  732.     initialize_fov()
  733.  
  734.     game_state = 'playing'
  735.     inventory = []
  736.     game_msgs = []
  737.  
  738.     message('Welcome stranger! Prepare to perish in the Tombs of the Ancient King.', libtcod.red)
  739.  
  740.  
  741. def save_game():
  742.     file = shelve.open('savegame', 'n')
  743.     file['map'] = map
  744.     file['objects'] = objects
  745.     file['player_index'] = objects.index(player)
  746.     file['inventory'] = inventory
  747.     file['game_msgs'] = game_msgs
  748.     file['game_state'] = game_state
  749.     file.close()
  750.  
  751. def load_game():
  752.     global map, objects, player, inventory, game_msgs, game_state
  753.     file = shelve.open('savegame', 'r')
  754.     map = file['map']
  755.     objects = file['objects']
  756.     player = objects[file['player_index']]
  757.     inventory = file['inventory']
  758.     game_msgs = file['game_msgs']
  759.     game_state = file['game_state']
  760.     file.close()
  761.  
  762.     initialize_fov()
  763.  
  764.  
  765.  
  766. ##############################
  767. # Initialization & Main Loop #
  768. ##############################
  769.  
  770. #customize font
  771. libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)
  772. #screen initialization
  773. libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'Roguelike', False)
  774. #maximum frames per second, only important for real-time applications
  775. libtcod.sys_set_fps(LIMIT_FPS)
  776. #second screen, where the characters will be printed on
  777. con = libtcod.console_new(MAP_WIDTH, SCREEN_HEIGHT)
  778. #panel at the bottom of the screen
  779. panel = libtcod.console_new(SCREEN_WIDTH, PANEL_HEIGHT)
  780.  
  781. main_menu()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement