Difference between revisions of "Complete Roguelike Tutorial, using python3+libtcod, part 2 code"

From RogueBasin
Jump to navigation Jump to search
 
(3 intermediate revisions by the same user not shown)
Line 6: Line 6:


<div style="background-color: #EEEEEE; border-style: dotted"><syntaxhighlight lang="python">
<div style="background-color: #EEEEEE; border-style: dotted"><syntaxhighlight lang="python">
#!/usr/bin/env python
import libtcodpy as libtcod
import os


import libtcodpy as tcod
#actual size of the window
SCREEN_WIDTH = 80
SCREEN_HEIGHT = 50


LIMIT_FPS = 20  #20 frames-per-second maximum


# ######################################################################
# Game Constants
# ######################################################################
# Size of the terminal window in characters
SCREEN_WIDTH = 80  # characters wide
SCREEN_HEIGHT = 50  # characters tall
LIMIT_FPS = 20  # 20 frames-per-second maximum
REALTIME = False  # set True for real-time, False for turn-based


 
class Object:
# ######################################################################
     #this is a generic object: the player, a monster, an item, the stairs...
# Exceptions
     #it's always represented by a character on screen.
# ######################################################################
     def __init__(self, x, y, char, color):
class GameError(Exception):
         self.x = x
    """Base Exception for all game errors"""
        self.y = y
 
         self.char = char
 
class FontError(GameError):
     """Font could not be loaded"""
 
 
# ######################################################################
# Classes
# ######################################################################
class Direction(object):
    """Defines direction of movement
 
    Matrix:
 
      -1, -1 | 0, -1 |  1, -1
      -1, 0  | 0, 0  |  1, 0
      -1, 1  | 0, 1  |  1, 1
 
          |  UP  |
      LEFT | NONE | RIGHT
          | DOWN |
 
        NW | NORTH | NE
      WEST | NONE  | EAST
        SW | SOUTH | SE
 
    """
    NONE = (0, 0)
 
    UP = (0, -1)
    DOWN = (0, 1)
    LEFT = (-1, 0)
    RIGHT = (1, 0)
 
    NORTH = UP
    NE = (1, -1)
    EAST = RIGHT
    SE = (1, 1)
    SOUTH = DOWN
    SW = (-1, 1)
    WEST = LEFT
    NW = (-1, -1)
 
 
class Object(object):
    """Game object.  This is used for any displayable game object such
    as the player, a mob, an item, a staircase, etc.
 
     Args:
        character (str): the character to display
        position (Position): (x, y) the position of the object on the map
        color (tcod.Color, optional): (R, G, B) color to use when drawing [default: tcod.white]
    """
 
     def __init__(self, character, position, color=tcod.white):
         self.character = character
         self.position = position
         self.color = color
         self.color = color
 
   
    def move(self, dx, dy):
        #move by the given amount
        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_default_foreground(con, self.color)
        libtcod.console_put_char(con, self.x, self.y, self.char, libtcod.BKGND_NONE)
   
     def clear(self):
     def clear(self):
         """Erase the character from the console"""
         #erase the character that represents this object
         self.draw(' ')
         libtcod.console_put_char(con, self.x, self.y, ' ', libtcod.BKGND_NONE)
 
    def draw(self, character=None):
        """Controls how the object is displayed on the screen.
 
        Args:
            character (str, optional): the character to display [default: self.character]
        """
        global con


        character = character or self.character
        tcod.console_set_default_foreground(con, self.color)
        x, y = self.position
        tcod.console_put_char(con, x, y, character, tcod.BKGND_NONE)


    def move(self, direction):
        """Moves the object in a specific direction.
        Modifies the object position.
        Args:
            direction (Direction, tuple): UP, DOWN, LEFT, RIGHT
        """
        self.position += direction
class Position(object):
    """A class to help with position-related math in 2d space
    Args:
        x (int): the width coordinate value
        y (int): the height coordinate value
    """
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __eq__(self, other):
        equal = False
        if isinstance(other, Position):
            if self.x == other.x and self.y == other.y:
                equal = True
        elif isinstance(other, (tuple, list)):
            if len(self) == len(other):
                if self.x == other[0] and self.y == other[1]:
                    equal = True
        return equal
    def __hash__(self):
        return hash((self.x, self.y))
    def __iter__(self):
        yield self.x
        yield self.y
    def __len__(self):
        return len((self.x, self.y))
    def __add__(self, other):
        if isinstance(other, Direction):
            dx, dy = other
            self.x += dx
            self.y += dy
        elif isinstance(other, (list, tuple)):
            dx, dy = other
            if len(other) == len(self):
                self.x += dx
                self.y += dy
        return self
    def __repr__(self):
        return f'<Position ({self.x}, {self.y})>'
    def __str__(self):
        return f'({self.x}, {self.y})'
# ######################################################################
# User Interface Control
# ######################################################################
def handle_keys():
def handle_keys():
     """Handles keyboard input
     #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)


    Updates:
        player_x: x coordinate of player position
        player_y: y coordinate of player position


    Returns:
#############################################
        bool: True if exit the game is requested else False
# Initialization & Main Loop
    """
#############################################
    global player


    exit_game = False
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)
con = libtcod.console_new(SCREEN_WIDTH, SCREEN_HEIGHT)


    # Run with REALTIME or turn-based
#create object representing the player
    if REALTIME:
player = Object(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2, '@', libtcod.white)
        key = tcod.console_check_for_keypress()
    else:
        key = tcod.console_wait_for_keypress(True)


    if key.vk == tcod.KEY_ENTER and key.lalt:
#create an NPC
        # Alt+Enter: toggle fullscreen
npc = Object(SCREEN_WIDTH // 2 - 5, SCREEN_HEIGHT // 2, '@', libtcod.yellow)
        tcod.console_set_fullscreen(not tcod.console_is_fullscreen())
    elif key.vk == tcod.KEY_ESCAPE:
        exit_game = True  # exit game


    # movement keys
#the list of objects with those two
    if tcod.console_is_key_pressed(tcod.KEY_UP):
objects = [npc, player]
        player.move(Direction.UP)
    elif tcod.console_is_key_pressed(tcod.KEY_DOWN):
        player.move(Direction.DOWN)
    elif tcod.console_is_key_pressed(tcod.KEY_LEFT):
        player.move(Direction.LEFT)
    elif tcod.console_is_key_pressed(tcod.KEY_RIGHT):
        player.move(Direction.RIGHT)


    return exit_game


while not libtcod.console_is_window_closed():
   
    #draw all objects in the list
    for object in objects:
        object.draw()
   
    #blit the contents of "con" to the root console and present it
    libtcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
    libtcod.console_flush()


# ######################################################################
     #erase all objects at their old locations, before they move
# Game
     for object in objects:
# ######################################################################
         object.clear()
def initialize_game(font_filepath=None, window_title=None, fullscreen=False):
   
    """Sets up libtcod and creates a window
    #handle keys and exit game if needed
 
    exit = handle_keys()
    Updates:
    if exit:
        player_x: x coordinate of player position
        break
        player_y: y coordinate of player position
 
    Args:
        font_filepath (str): the path to the font file [default: terminal.png]
        window_title (str): the title to display for the game [default: Python3 Tutorial]
    """
    global player, con, npc
 
    # Setup displayed font
    font_filepath = os.path.abspath(font_filepath or 'terminal.png')
    font_flags = tcod.FONT_TYPE_GREYSCALE | tcod.FONT_LAYOUT_ASCII_INCOL
    if not os.path.exists(font_filepath):
        raise FontError("Could not open font file: {}".format(font_filepath))
    tcod.console_set_custom_font(font_filepath, font_flags)
 
    # Setup window
    window_title = window_title or 'Python3 Tutorial'
    fullscreen = fullscreen or False
    tcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, window_title, fullscreen)
 
    # Limit frames per second
    tcod.sys_set_fps(LIMIT_FPS)
 
    # Create the console
    con = tcod.console_new(SCREEN_WIDTH, SCREEN_HEIGHT)
 
     # Setup player's initial position
    player_starting_position = Position(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
    player = Object('@', player_starting_position)
 
    npc_starting_position = Position(SCREEN_WIDTH // 2 - 5, SCREEN_HEIGHT // 2)
    npc = Object('@', npc_starting_position, color=tcod.yellow)
 
 
def main():
    global player, npc
 
    initialize_game()
    objects = [npc, player]
 
    # Game loop
    exit_game = False
     while not tcod.console_is_window_closed() and exit_game is not True:
        tcod.console_set_default_foreground(0, tcod.white)
 
        for game_object in objects:
            game_object.draw()
 
        tcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
        tcod.console_flush()
 
         for game_object in objects:
            game_object.clear()
 
        # handle keys
        exit_game = handle_keys()
 
 
if __name__ == '__main__':
    main()
 
</syntaxhighlight></div>
</syntaxhighlight></div>


Line 337: Line 162:
      
      
     #fill map with "unblocked" tiles
     #fill map with "unblocked" tiles
     map = [[ Tile(False)
     map = [
        for y in range(MAP_HEIGHT) ]
        [Tile(False) for y in range(MAP_HEIGHT)]
            for x in range(MAP_WIDTH) ]
        for x in range(MAP_WIDTH)  
    ]
      
      
     #place two pillars to test the map
     #place two pillars to test the map
Line 357: Line 183:
             wall = map[x][y].block_sight
             wall = map[x][y].block_sight
             if wall:
             if wall:
                 libtcod.console_set_char_background(con, x, y, color_dark_wall, libtcod.BKGND_SET )
                 libtcod.console_set_char_background(con, x, y, color_dark_wall, libtcod.BKGND_SET)
             else:
             else:
                 libtcod.console_set_char_background(con, x, y, color_dark_ground, libtcod.BKGND_SET )
                 libtcod.console_set_char_background(con, x, y, color_dark_ground, libtcod.BKGND_SET)


     #draw all objects in the list
     #draw all objects in the list
Line 403: Line 229:


#create object representing the player
#create object representing the player
player = Object(SCREEN_WIDTH/2, SCREEN_HEIGHT/2, '@', libtcod.white)
player = Object(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2, '@', libtcod.white)


#create an NPC
#create an NPC
npc = Object(SCREEN_WIDTH/2 - 5, SCREEN_HEIGHT/2, '@', libtcod.yellow)
npc = Object(SCREEN_WIDTH // 2 - 5, SCREEN_HEIGHT // 2, '@', libtcod.yellow)


#the list of objects with those two
#the list of objects with those two

Latest revision as of 19:46, 25 September 2017

This is part of a series of tutorials; the main page can be found here.

Generalizing

import libtcodpy as libtcod

#actual size of the window
SCREEN_WIDTH = 80
SCREEN_HEIGHT = 50

LIMIT_FPS = 20  #20 frames-per-second maximum


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
        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_default_foreground(con, self.color)
        libtcod.console_put_char(con, self.x, self.y, self.char, libtcod.BKGND_NONE)
    
    def clear(self):
        #erase the character that represents this object
        libtcod.console_put_char(con, self.x, self.y, ' ', libtcod.BKGND_NONE)


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)
con = libtcod.console_new(SCREEN_WIDTH, SCREEN_HEIGHT)

#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]


while not libtcod.console_is_window_closed():
    
    #draw all objects in the list
    for object in objects:
        object.draw()
    
    #blit the contents of "con" to the root console and present it
    libtcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
    libtcod.console_flush()

    #erase all objects at their old locations, before they move
    for object in objects:
        object.clear()
    
    #handle keys and exit game if needed
    exit = handle_keys()
    if exit:
        break

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_default_foreground(con, self.color)
        libtcod.console_put_char(con, self.x, self.y, self.char, libtcod.BKGND_NONE)
    
    def clear(self):
        #erase the character that represents this object
        libtcod.console_put_char(con, 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
    
    #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_char_background(con, x, y, color_dark_wall, libtcod.BKGND_SET)
            else:
                libtcod.console_set_char_background(con, x, y, color_dark_ground, libtcod.BKGND_SET)

    #draw all objects in the list
    for object in objects:
        object.draw()
    
    #blit the contents of "con" to the root console
    libtcod.console_blit(con, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0)
    
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)
con = libtcod.console_new(SCREEN_WIDTH, SCREEN_HEIGHT)

#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()


while not libtcod.console_is_window_closed():
    
    #render the screen
    render_all()
    
    libtcod.console_flush()

    #erase all objects at their old locations, before they move
    for object in objects:
        object.clear()
    
    #handle keys and exit game if needed
    exit = handle_keys()
    if exit:
        break