Пример #1
0
 def draw(self, camera):
     x, y = camera.to_camera_coordinates(self.x, self.y)
     if self.projectile:
         terminal.put_ext(x * 4, y * 2, self.projectile.x_offset,
                          self.projectile.y_offset, self.icon)
     else:
         terminal.put(x * 4, y * 2, self.icon)
Пример #2
0
 def draw(self):
     x = self.top
     frame_height = self.height
     scrollbar_height = self.scrollbar_height
     frame_offset = self.offset
     messages_len = self.contents.total_height
     cell_height = blt.state(blt.TK_CELL_HEIGHT)
     blt.color(self.color)
     self.scrollbar_column = self.left + self.width
     self.scrollbar_offset = int(
         (x + (frame_height - scrollbar_height) * (1 - frame_offset / (messages_len - frame_height + 1)))
         * cell_height)
     # top line
     for i in range(self.left - 1, self.left + self.width + 2):
         blt.put(i, self.top - 1, '+')
     # bottom line
     for i in range(self.left - 1, self.left + self.width + 2):
         blt.put(i, self.top + self.height, '+')
     # left line
     for i in range(self.top - 1, self.top + self.height + 1):
         blt.put(self.left - 1, i, '+')
     # right line
     for i in range(self.top - 1, self.top + self.height + 1):
         blt.put(self.left + self.width + 1, i, '+')
     # scrollbar
     for i in range(self.scrollbar_height):
         blt.put_ext(self.scrollbar_column, i, 0, self.scrollbar_offset, 0x2588)
Пример #3
0
    def pprint(self, x: int, y: int, string: str):
        """
        Pretty prints a string starting at (x,y).
        :param x: x coordinate

        :param y: y coordinate

        :param string: String to print to screen
        """
        bearlib.layer(1)
        bearlib.composition(bearlib.TK_ON)
        pos = x
        cell_size, _ = self.window.cell_size.split('x')
        cell_width = int(cell_size)
        for c in string:
            if pos >= self.window.width - x - 1:
                pos = pos + 1
                offset = (pos - x) * (cell_width / 2)
                bearlib.put_ext(x, y, int(offset), 0, c)
            else:
                pos = pos + 1
                offset = 0 - pos * (cell_width / 2)
                bearlib.put_ext(pos, y, int(offset), 0, c)
        bearlib.layer(0)
        bearlib.composition(bearlib.TK_OFF)
Пример #4
0
    def pprint_center(self, text: List[str]):
        """
        Prints a string or list of strings in the center of the window

        :param text: List of strings to be printed
        """
        height = self.window.height
        width = self.window.width
        cellsize, _ = self.window.cell_size.split('x')
        cell_width = int(cellsize)
        center = int(width / 2)

        bearlib.clear()
        bearlib.layer(1)
        bearlib.composition("TK_ON")
        y = int(height / 2 - len(text) / 2)
        for i, s in enumerate(text):
            middle_char = int(len(s) / 2)
            x = int(center - middle_char)
            pos = 0
            for c in s:
                offset = (center - x) * (cell_width / 2)
                bearlib.put_ext(x, y + i, int(offset), 0, c)
                x = x + 1
                pos = pos + 1
        bearlib.composition("TK_OFF")
        bearlib.layer(0)
        bearlib.refresh()
Пример #5
0
def clear_entity(ent, camera):
    # Clear an entity display on the terminal.
    prev_layer = terminal.state(terminal.TK_LAYER)
    terminal.layer(RenderLayer.ENTITIES.value)

    (term_x, term_y) = camera.to_camera_coordinates(ent.x, ent.y)
    terminal.put_ext(term_x, term_y, 0, 0, ' ', None)

    terminal.layer(prev_layer)
Пример #6
0
    def draw(self):
        is_visible = libtcod.map_is_in_fov(FOV_MAP, self.x, self.y)

        if is_visible:
            tile_x, tile_y = draw_iso(self.x, self.y)
            # draw our entity's ASCII symbol at an offset
            # blt.put_ext(tile_x, tile_y, 0, blt.state(blt.TK_CELL_HEIGHT), self.char)

            # draw the tile at different offset because size of a tile is much different than the size of an ASCII letter
            blt.put_ext(tile_x, tile_y, 0, 2, self.char)
Пример #7
0
    def render(self, x, y, symbol, layer, bkcolor=None):
        """Render the given symbol.  May modify the state of terminal.color or terminal.bkcolor."""
        # Set terminal
        blt.layer(layer)
        if bkcolor is not None:
            blt.bkcolor(bkcolor)
        blt.color(symbol.color)

        draw_x, draw_y = x + self.x_offset, y + self.y_offset
        blt.put_ext(draw_x, draw_y, symbol.dx, symbol.dy, symbol.char, None)
Пример #8
0
def draw_char_ex(dest, x, y, dx, dy, char, color=None, flag=None, alpha=255):
    # LIBTCOD
    # libtcod.console_put_char_ex(dest, x, y, char, color, flag)

    # BEARLIB
    terminal.layer(dest)
    # TODO: CONVERT COLORS ON THEME IMPORT, INSTEAD OF INLINE (all render func)
    if color is not None:
        #color = Utils.convert_color(color, alpha)
        terminal.color(color)
    terminal.put_ext(x, y, dx, dy, char, None)
Пример #9
0
def draw_char_ex(dest, x, y, dx, dy, char, color=None, flag=None, alpha=255):
    # LIBTCOD
    # libtcod.console_put_char_ex(dest, x, y, char, color, flag)

    # BEARLIB
    terminal.layer(dest)
    # TODO: CONVERT COLORS ON THEME IMPORT, INSTEAD OF INLINE (all render func)
    if color is not None:
        #color = Utils.convert_color(color, alpha)
        terminal.color(color)
    terminal.put_ext(x, y, dx, dy, char, None)
Пример #10
0
    def draw(self):
        # Frame background
        blt.layer(0)
        blt.bkcolor("darkest gray")
        blt.clear_area(self.left, self.top, self.width, self.height)

        # Scroll bar
        blt.bkcolor("darker gray")
        blt.clear_area(self.left + self.width, self.top, 1, self.height)
        blt.bkcolor("none")
        blt.color("dark orange")
        self.scrollbar_column = self.left + self.width
        self.scrollbar_offset = int(
            (self.top + (self.height - self.scrollbar_height) * (self.offset / (self.contents.total_height - self.height))) * 
            blt.state(blt.TK_CELL_HEIGHT))
        for i in range(self.scrollbar_height):
            blt.put_ext(self.scrollbar_column, i, 0, self.scrollbar_offset, 0x2588)
Пример #11
0
    def render(self):
        if not self.messages:
            return

        self.update_geometry()

        terminal.layer = 0
        terminal.clear_area(self.padding_left, self.padding_top, self.frame_width, self.frame_height)

        index: int = 0
        first_line: int = 0

        while index < self.total_messages_height:
            message = self.messages[index]
            _, message_height = terminal.measuref(message)

            if first_line + message_height >= self.frame_offset:
                break

            first_line += message_height + 1

            index += 1

        delta: int = int(first_line - self.frame_offset)

        terminal.layer = 1

        while index < len(self.messages) and delta <= self.frame_height:
            message = self.messages[index]

            terminal.puts(self.padding_left, self.padding_top + delta, message, self.frame_width, 0,
                          terminal.state(terminal.TK_ALIGN_DEFAULT))

            _, message_height = terminal.measuref(message)
            delta += message_height
            index += 1

        terminal.layer = 0
        terminal.clear_area(self.padding_left + self.frame_width, self.padding_top, 1, self.frame_height)

        scrollbar_column: int = self.padding_left + self.frame_width
        scrollbar_offset = (self.padding_top + (self.frame_height - self.scrollbar_height) * (self.frame_offset / self.total_messages_height)) * terminal.state(terminal.TK_CELL_HEIGHT)

        for i in range(self.scrollbar_height):
            terminal.put_ext(scrollbar_column, i, 0, int(scrollbar_offset), 0x2588)
Пример #12
0
    def draw(self, entity, x, y):
        if entity.hidden:
            return
        player = self.owner.player
        game_map = self.owner.levels.current_map
        # Draw the entity to the screen
        blt.layer(entity.layer)
        blt.color(entity.color)
        c = blt.color_from_name(entity.color)
        if not entity.cursor:
            light_map = player.player.lightmap
            argb = argb_from_color(c)
            a = argb[0]
            r = min(int(argb[1] * light_map[entity.y][entity.x]), 255)
            g = min(int(argb[2] * light_map[entity.y][entity.x]), 255)
            b = min(int(argb[3] * light_map[entity.y][entity.x]), 255)

            blt.color(blt.color_from_argb(a, r, g, b))

        if not (player.light_source.fov_map.fov[entity.y, entity.x]
                and game_map.tiles[entity.x][entity.y].explored):
            blt.color("dark gray")

        # Cursor needs some offset in ascii
        if self.owner.options.gfx == "ascii" and entity.name == "cursor":
            blt.put_ext(x * self.owner.options.tile_offset_x,
                        y * self.owner.options.tile_offset_y,
                        -3 * self.owner.options.tile_offset_x,
                        -5 * self.owner.options.tile_offset_y, entity.char)
        else:
            if entity.boss and not entity.fighter:
                blt.put((x - 1) * self.owner.options.tile_offset_x,
                        (y - 1) * self.owner.options.tile_offset_y,
                        entity.char)
            else:
                blt.put(x * self.owner.options.tile_offset_x,
                        y * self.owner.options.tile_offset_y, entity.char)
Пример #13
0
    def draw_side_panel_content(self):
        game_map = self.owner.levels.current_map
        player = self.owner.player
        side_panel = self.owner.ui.side_panel
        # Draw side panel content
        blt.layer(1)
        blt.color(None)
        x_margin = self.owner.ui.side_panel.x_margin
        map_title = fill(game_map.title, 21)

        blt.clear_area(side_panel.offset_x + x_margin,
                       side_panel.offset_y + 20,
                       side_panel.offset_w - x_margin,
                       side_panel.offset_h - 40)

        blt.puts(side_panel.offset_x + x_margin, side_panel.offset_y + 21,
                 "Location: " + map_title, 0, 0, blt.TK_ALIGN_LEFT)
        blt.puts(side_panel.offset_x + x_margin,
                 side_panel.offset_y + 24 + map_title.count('\n'),
                 "World tendency: " + str(self.owner.levels.world_tendency), 0,
                 0, blt.TK_ALIGN_LEFT)

        # Fetch player skills and draw icons
        weapon = []
        attack = []
        utility = []
        for skill in player.abilities.items:
            if skill.skill_type == "weapon":
                weapon.append(skill)
            elif skill.skill_type == "attack":
                attack.append(skill)
            elif skill.skill_type == "utility":
                utility.append(skill)

        blt.color(None)

        # Weapon skills
        y_margin = 0
        first_heading_y = 30
        fill_chars = 32
        for i, wpn in enumerate(weapon):
            if wpn.name == player.player.sel_weapon.name:
                # heading
                title = "Weapon (Switch: 'W')"
                blt.color("lighter yellow")
                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + first_heading_y,
                         fill(title, fill_chars), 0, 0, blt.TK_ALIGN_LEFT)

                # name of the selected skill
                blt.color(None)
                skill_str = "{0}, {1}+{2} dmg".format(wpn.name.capitalize(),
                                                      wpn.damage[wpn.rank],
                                                      player.fighter.str_bonus)
                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + first_heading_y + 2,
                         fill(skill_str, fill_chars), 0, 0, blt.TK_ALIGN_LEFT)

                # highlight icon of the selected skill
                blt.color("dark amber")
                y_margin += skill_str.count('\n')

            if i > 4:
                y_margin += 3
                i = 0

            blt.put(side_panel.offset_x + x_margin + 1 + i * 6,
                    side_panel.offset_y + first_heading_y + 4, wpn.icon)
            blt.color(None)

        second_heading_y = first_heading_y + 9

        # Attack skills
        for i, atk in enumerate(attack):

            if player.player.sel_attack and atk.name == player.player.sel_attack.name:
                # heading
                title = "Skills (Switch: 'A', Use: 'TAB')"
                blt.color("lighter green")
                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + second_heading_y + y_margin,
                         fill(title, fill_chars), 0, 0, blt.TK_ALIGN_LEFT)

                blt.color(None)
                skill_str = "{0}: ".format(atk.name)
                chance_str, atk_str, effect_str, duration_str = "", "", "", ""
                if atk.chance:
                    # chance_str = str(int(1 / atk.chance[atk.rank])) + "% chance of "
                    chance_str = "100% chance of "
                if atk.effect:
                    effect_str = ", ".join(atk.effect) + ", "
                if atk.duration:
                    duration_str = atk.duration[atk.rank] + " turns"
                if atk.damage:
                    atk_str = ", " + atk.damage[atk.rank] + "+" + str(player.fighter.str_bonus) + " dmg" if duration_str \
                        else atk.damage[atk.rank] + "+" + str(player.fighter.str_bonus) + " dmg"
                skill_str += chance_str + effect_str + duration_str + atk_str

                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + second_heading_y + 2 + y_margin,
                         fill(skill_str.capitalize(),
                              fill_chars), 0, 0, blt.TK_ALIGN_LEFT)
                # highlight icon of the selected skill
                blt.color("dark amber")
                y_margin += skill_str.count('\n')

            if i > 4:
                y_margin += 3
                i = 0

            blt.put(side_panel.offset_x + x_margin + 1 + i * 6,
                    side_panel.offset_y + second_heading_y + 5 + y_margin,
                    atk.icon)
            blt.color(None)

        third_heading_y = second_heading_y + 10

        # Utility skills
        for i, utl in enumerate(utility):
            if player.player.sel_utility and utl.name == player.player.sel_utility.name:
                title = "Utility (Use: 'Z')"
                blt.color("lighter blue")
                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + third_heading_y + y_margin,
                         fill(title, fill_chars), 0, 0, blt.TK_ALIGN_LEFT)

                # name of the selected skill
                blt.color(None)
                skill_str = utl.name
                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + third_heading_y + 2 + y_margin,
                         fill(skill_str.capitalize(),
                              fill_chars), 0, 0, blt.TK_ALIGN_LEFT)
                y_margin += skill_str.count('\n')
                blt.puts(side_panel.offset_x + x_margin,
                         side_panel.offset_y + third_heading_y + 8 + y_margin,
                         fill(utl.description.capitalize(),
                              fill_chars), 46 + y_margin, 0, blt.TK_ALIGN_LEFT)
                # highlight icon of the selected skill
                blt.color("dark amber")

            if i > 4:
                y_margin += 3
                i = 0

            blt.put(side_panel.offset_x + x_margin + 1 + i * 6,
                    side_panel.offset_y + third_heading_y + 5 + y_margin,
                    utl.icon)
            blt.layer(0)
            blt.put_ext(side_panel.offset_x + x_margin + i * 6,
                        side_panel.offset_y + third_heading_y + 5 + y_margin,
                        -2, -14, str(i + 1))
            blt.layer(1)
            blt.color(None)
Пример #14
0
 def draw(self, xy: vec, layer: int = 0) -> None:
     blt.color(self.fg_colour.blt_colour())
     if self.bg_colour is not None:
         blt.bkcolor(self.bg_colour.blt_colour())
     xy = xy + self.xy
     blt.put_ext(xy.x, xy.y, self.dxy.x, self.dxy.y, self.char)
Пример #15
0
def test_extended_smooth_scroll():
    random.seed()

    blt.set("window.title='Omni: extended output / smooth scroll'")
    blt.set("input.filter={keyboard+}")
    blt.composition(True)

    # Load resources
    blt.set("U+E000: ../Media/Tiles.png, size=32x32, alignment=top-left")

    screen_width = blt.state(blt.TK_WIDTH) * blt.state(blt.TK_CELL_WIDTH)
    screen_height = blt.state(blt.TK_HEIGHT) * blt.state(blt.TK_CELL_HEIGHT)
    hspeed = vspeed = 0
    hoffset = voffset = 0

    map_ = [[0] * map_size for _ in range(map_size)]

    for _ in range(map_size * map_size // 10):
        x = random.randrange(map_size)
        y = random.randrange(map_size)
        map_[y][x] = random.randrange(8)

    while True:
        hoffset -= hspeed
        voffset -= vspeed

        blt.clear()

        tx = hoffset % tile_size
        ty = voffset % tile_size
        ix = (hoffset - tx) // tile_size
        iy = (voffset - ty) // tile_size
        jx = (-ix) % map_size if ix < 0 else map_size - (ix % map_size)
        jy = (-iy) % map_size if iy < 0 else map_size - (iy % map_size)
        hc = (screen_width + 2 * tile_size - tx - 1) // tile_size
        vc = (screen_height + 2 * tile_size - ty - 1) // tile_size

        blt.puts(2, 1, "speed: %d, %d" % (hspeed, vspeed))
        blt.puts(2, 2, "offset: %d/%d, %d/%d" % (ix, jx, iy, jy))

        for y in range(vc + 1):
            my = (jy + y) % map_size
            for x in range(hc + 1):
                mx = (jx + x) % map_size
                c = map_[my][mx]
                blt.put_ext(0, 0, (x - 1) * tile_size + tx,
                            (y - 1) * tile_size + ty, 0xE000 + c)

        blt.refresh()

        if blt.has_input():
            key = blt.read()
            if key in (blt.TK_CLOSE, blt.TK_ESCAPE):
                break

        if blt.state(blt.TK_LEFT):
            if hspeed > -speed_cap: hspeed -= 1
        elif blt.state(blt.TK_RIGHT):
            if hspeed < speed_cap: hspeed += 1
        else:
            hspeed -= sgn(hspeed)

        if blt.state(blt.TK_UP):
            if vspeed > -speed_cap: vspeed -= 1
        elif blt.state(blt.TK_DOWN):
            if vspeed < speed_cap: vspeed += 1
        else:
            vspeed -= sgn(vspeed)

        blt.delay(1000 // fps)

    blt.set("U+E000: none")
    blt.set("input.filter={keyboard}")
    blt.composition(False)
Пример #16
0
def test_tilesets():
    blt.set("window.title='Omni: tilesets'")
    blt.composition(True)

    # Load tilesets
    blt.set("U+E100: ./Images/Runic.png, size=8x16")
    blt.set("U+E200: ./Images/Tiles.png, size=32x32, align=top-left")
    blt.set("U+E400: ./Images/test_tiles.png, size=16x16, align=top-left")
    blt.set(
        "U+E300: ./Fonts/fontawesome-webfont.ttf, size=24x24, spacing=3x2, codepage=./Fonts/fontawesome-codepage.txt"
    )
    blt.set(
        "zodiac font: ./Fonts/Zodiac-S.ttf, size=24x36, spacing=3x3, codepage=437"
    )

    blt.clear()
    blt.color("white")

    blt.print_(
        2, 1,
        "[color=orange]1.[/color] Of course, there is default font tileset.")

    blt.print_(
        2, 3,
        "[color=orange]2.[/color] You can load some arbitrary tiles and use them as glyphs:"
    )
    blt.print_(
        2 + 3, 4, "Fire rune ([color=red][U+E102][/color]), "
        "water rune ([color=lighter blue][U+E103][/color]), "
        "earth rune ([color=darker green][U+E104][/color])")

    blt.print_(
        2, 6,
        "[color=orange]3.[/color] Tiles are not required to be of the same size as font symbols:"
    )
    blt.put(2 + 3 + 0, 7, 0xE200 + 7)
    blt.put(2 + 3 + 5, 7, 0xE200 + 8)
    blt.put(2 + 3 + 10, 7, 0xE200 + 9)

    blt.print_(
        2, 10,
        "[color=orange]4.[/color] Like font characters, tiles may be freely colored and combined:"
    )

    blt.put_ext(2 + 3 + 0, 11, 0, 0, tiles['stone_wall'], [
        blt.color_from_name("red"),
        blt.color_from_name("red"),
        blt.color_from_name("blue"),
        blt.color_from_name("red")
    ])
    blt.put_ext(2 + 3 + 2, 11, 0, 0, tiles['stone_wall'], [
        blt.color_from_name("red"),
        blt.color_from_name("blue"),
        blt.color_from_name("red"),
        blt.color_from_name("red")
    ])
    blt.put_ext(2 + 3 + 0, 13, 0, 0, tiles['stone_wall'], [
        blt.color_from_name("red"),
        blt.color_from_name("red"),
        blt.color_from_name("blue"),
        blt.color_from_name("blue")
    ])
    blt.put_ext(2 + 3 + 2, 13, 0, 0, tiles['stone_wall'], [
        blt.color_from_name("blue"),
        blt.color_from_name("blue"),
        blt.color_from_name("red"),
        blt.color_from_name("red")
    ])

    blt.put_ext(2 + 3 + 0 + 5, 11, 0, 0, '#', [
        blt.color_from_name("yellow"),
        blt.color_from_name("black"),
        blt.color_from_name("black"),
        blt.color_from_name("black")
    ])
    blt.put_ext(2 + 3 + 1 + 5, 11, 0, 0, 'A', [
        blt.color_from_name("black"),
        blt.color_from_name("yellow"),
        blt.color_from_name("yellow"),
        blt.color_from_name("black")
    ])
    blt.put_ext(2 + 3 + 0 + 5, 12, 0, 0, 'A', [
        blt.color_from_name("black"),
        blt.color_from_name("black"),
        blt.color_from_name("yellow"),
        blt.color_from_name("yellow")
    ])
    blt.put_ext(2 + 3 + 1 + 5, 12, 0, 0, '@', [
        blt.color_from_name("dark yellow"),
        blt.color_from_name("dark yellow"),
        blt.color_from_name("dark yellow"),
        blt.color_from_name("dark yellow")
    ])

    blt.put_ext(2 + 3 + 0 + 7, 11, 0, 0, 'A', [
        blt.color_from_name("black"),
        blt.color_from_name("yellow"),
        blt.color_from_name("black"),
        blt.color_from_name("black")
    ])

    blt.put_ext(2 + 3 + 0 + 7, 12, 0, 0, 'A', [
        blt.color_from_name("yellow"),
        blt.color_from_name("yellow"),
        blt.color_from_name("black"),
        blt.color_from_name("black")
    ])
    '''
    # blt.color("lightest red")
    blt.put(2+3+4, 11,tiles['stairs'])
    # blt.color("purple")
    blt.put(2+3+8, 11, tiles['gold'])
    #blt.color("darkest red")
    blt.put(17, 11, 0xE400+0)
    blt.put(18, 11, 0xE400+0)
    blt.put(19, 11, 0xE400+1)
    blt.put(20, 11, 0xE400 + 0)
    blt.put(20, 11, 0xE400 + 2)

    blt.put(17, 12, 0xE400 + 10)
    blt.put(18, 12, 0xE400 + 10)
    blt.put(19, 12, 0xE400 + 11)
    blt.put(20, 12, 0xE400 + 10)
    blt.put(20, 12, 0xE400 + 12)
    '''
    blt.put(21, 11, 0xE400 + 0)
    blt.color("blue")
    blt.put(18, 11, '@')

    blt.color("white")
    order = [11, 10, 14, 12, 13]
    for i in range(len(order)):
        blt.put(30 + i * 4, 11, 0xE200 + order[i])
        blt.put(30 + (len(order) + 1) * 4, 11, 0xE200 + order[i])

    blt.put(30 + len(order) * 4, 11, 0xE200 + 15)

    blt.print_(
        2, 15,
        "[color=orange]5.[/color] And tiles can even come from TrueType fonts like this:"
    )
    for i in range(6):
        blt.put(5 + i * 5, 15, 0xE300 + i)

    blt.print_(5, 18, "...or like this:\n[font=zodiac]D F G S C")

    blt.refresh()

    key = None
    while key not in (blt.TK_CLOSE, blt.TK_ESCAPE):
        key = blt.read()

    # Clean up
    blt.set("U+E100: none; U+E200: none; U+E300: none; zodiac font: none")
    blt.composition(False)
Пример #17
0
 def draw(self):
     for draw_args in self.draw_iter():
         blt.put_ext(*draw_args)
Пример #18
0
def print_state(matrix, colors):
    for index, cell in np.ndenumerate(matrix):
        terminal.put_ext(index[1] + 1, index[0] + 1, 0, 0, 0x2588,
                         colors[int(cell)])
Пример #19
0
    def render(self):
        # No need to render endlessly
        # EDIT: Actually now I need it
        # if not self.re_render:
        #     return

        blt.clear()

        self.render_frames()
        # Gameboard related stuff
        blt.layer(0)

        # Coloring the different panels
        if DEBUG:
            for y in range(MESSAGE_PANEL.y, MESSAGE_PANEL.y + MESSAGE_PANEL.h):
                for x in range(MESSAGE_PANEL.x, MESSAGE_PANEL.x + MESSAGE_PANEL.w):
                    blt.bkcolor('green')
                    blt.put(x, y, ' ')

            for y in range(STATS_PANEL.y, STATS_PANEL.y + STATS_PANEL.h):
                for x in range(STATS_PANEL.x, STATS_PANEL.x + STATS_PANEL.w):
                    blt.bkcolor('blue')
                    blt.put(x, y, ' ')

            for y in range(STATS_ENEMY_PANEL.y, STATS_ENEMY_PANEL.y + STATS_ENEMY_PANEL.h):
                for x in range(STATS_ENEMY_PANEL.x, STATS_ENEMY_PANEL.x + STATS_ENEMY_PANEL.w):
                    blt.bkcolor('red')
                    blt.put(x, y, ' ')

            for y in range(BUTTONS_PANEL.y, BUTTONS_PANEL.y + BUTTONS_PANEL.h):
                for x in range(BUTTONS_PANEL.x, BUTTONS_PANEL.x + BUTTONS_PANEL.w):
                    blt.bkcolor('violet')
                    blt.put(x, y, ' ')


        # Board drawing
        for y, row in enumerate(self.gamemap.terrain):
            for x, tile in enumerate(row):
                blt.bkcolor(tile.color)
                blt.puts((x + self.map_offset.x) * TERRAIN_SCALE_X, (y + self.map_offset.y) * TERRAIN_SCALE_Y, '[font=terrain] [/font]')

        blt.layer(0)
        # Legal moves for current actors
        self.highlighted_cases = self.get_possible_movement(self.unit_turn)
        for highlight in self.highlighted_cases:
            color = 'turquoise'
            if highlight['valid'] == 'enemy':
                color = 'red'
            if highlight['valid'] == 'false':
                color = 'amber'
            blt.bkcolor(color)
            blt.puts((highlight['mov'].x + self.map_offset.x) * TERRAIN_SCALE_X,
                     (highlight['mov'].y + self.map_offset.y) * TERRAIN_SCALE_Y, '[font=terrain] [/font]')

        # Coordinates
        blt.bkcolor('black')

        # for y in range(10):
        #     blt.puts(0, (y + offset.y) * TERRAIN_SCALE_Y, f'[font=terrain]{str(y)}[/font]')
        #     blt.puts((self.gamemap.h + 1) * TERRAIN_SCALE_X, (y + offset.y) * TERRAIN_SCALE_Y, f'[font=terrain]{str(y)}[/font]')
        #     blt.puts((y + 1) * TERRAIN_SCALE_X, (offset.y - 1) * TERRAIN_SCALE_Y, f'[font=terrain]{chr(65 + y)}[/font]')
        #     blt.puts((y + 1) * TERRAIN_SCALE_X, (offset.y + self.gamemap.h) * TERRAIN_SCALE_Y, f'[font=terrain]{chr(65 + y)}[/font]')

        # actors
        blt.layer(2)
        # First, the daed actors, so that they are below the living ones
        # for actor in [a for a in self.actors if a.dead]:
        #     blt.color(actor.color)
        #     blt.puts((actor.x + self.map_offset.x) * TERRAIN_SCALE_X,
        #              (actor.y + self.map_offset.y) * TERRAIN_SCALE_Y, actor.charac)

        # Then the living ones
        for actor in [a for a in self.actors if not a.dead]:
            if actor.sprite:
                blt.put_ext((actor.x + self.map_offset.x) * TERRAIN_SCALE_X,
                            (actor.y + self.map_offset.y) * TERRAIN_SCALE_Y, 0, 0, actor.sprite)
            else:
                blt.color(actor.color)
                blt.puts(((actor.x + self.map_offset.x) * TERRAIN_SCALE_X),
                          (actor.y + self.map_offset.y) * TERRAIN_SCALE_Y, f'[offset={TERMINAL_CELL_SIZE_X/2},0]{actor.charac}[/offset]')

        # Text
        blt.layer(3)
        off_x = (self.gamemap.w + self.map_offset.x) * TERRAIN_SCALE_X
        off_y = (self.gamemap.h + self.map_offset.y) * TERRAIN_SCALE_Y
        blt.color('white')
        blt.bkcolor('black')
        self.render_stats(STATS_PANEL.x, STATS_PANEL.y, self.unit_turn)

        if self.under_mouse:
            self.render_stats(STATS_ENEMY_PANEL.x, STATS_ENEMY_PANEL.y, self.under_mouse)


        blt.puts(BUTTONS_PANEL.top_right.x - 8, BUTTONS_PANEL.top_right.y, 'End turn')

        if self.message_queue:
            blt.puts(MESSAGE_PANEL.x, MESSAGE_PANEL.y,
                     self.message_queue[-1], MESSAGE_PANEL.w, MESSAGE_PANEL.h, blt.TK_ALIGN_LEFT)

        self.re_render = False
Пример #20
0
def draw_entity(entity, fov_map):
    if libtcod.map_is_in_fov(fov_map, entity.x, entity.y):
        blt.color(blt.color_from_name(entity.color))
        blt.bkcolor(blt.color_from_name("transparent"))
        blt.put_ext(entity.x, entity.y, 0, -2, entity.char, None)
Пример #21
0
def test_tilesets():
    blt.set("window.title='Omni: tilesets'")
    blt.composition(True)

    # Load tilesets
    blt.set("U+E100: ./Images/Runic.png, size=8x16")
    blt.set("U+E200: ./Images/Tiles.png, size=32x32, align=top-left")
    blt.set("U+E400: ./Images/test_tiles.png, size=16x16, align=top-left")
    blt.set("U+E300: ./Fonts/fontawesome-webfont.ttf, size=24x24, spacing=3x2, codepage=./Fonts/fontawesome-codepage.txt")
    blt.set("zodiac font: ./Fonts/Zodiac-S.ttf, size=24x36, spacing=3x3, codepage=437")

    blt.clear()
    blt.color("white")

    blt.print_(2, 1, "[color=orange]1.[/color] Of course, there is default font tileset.")

    blt.print_(2, 3, "[color=orange]2.[/color] You can load some arbitrary tiles and use them as glyphs:")
    blt.print_(2+3, 4,
        "Fire rune ([color=red][U+E102][/color]), "
        "water rune ([color=lighter blue][U+E103][/color]), "
        "earth rune ([color=darker green][U+E104][/color])")

    blt.print_(2, 6, "[color=orange]3.[/color] Tiles are not required to be of the same size as font symbols:")
    blt.put(2+3+0, 7, 0xE200+7)
    blt.put(2+3+5, 7, 0xE200+8)
    blt.put(2+3+10, 7, 0xE200+9)

    blt.print_(2, 10, "[color=orange]4.[/color] Like font characters, tiles may be freely colored and combined:")

    blt.put_ext(2+3+0, 11, 0, 0, tiles['stone_wall'], [blt.color_from_name("red"),
                                                       blt.color_from_name("red"),
                                                       blt.color_from_name("blue"),
                                                       blt.color_from_name("red")])
    blt.put_ext(2 + 3 + 2, 11, 0, 0, tiles['stone_wall'], [blt.color_from_name("red"),
                                                           blt.color_from_name("blue"),
                                                           blt.color_from_name("red"),
                                                           blt.color_from_name("red")])
    blt.put_ext(2 + 3 + 0, 13, 0, 0, tiles['stone_wall'], [blt.color_from_name("red"),
                                                           blt.color_from_name("red"),
                                                           blt.color_from_name("blue"),
                                                           blt.color_from_name("blue")])
    blt.put_ext(2 + 3 + 2, 13, 0, 0, tiles['stone_wall'], [blt.color_from_name("blue"),
                                                           blt.color_from_name("blue"),
                                                           blt.color_from_name("red"),
                                                           blt.color_from_name("red")])

    blt.put_ext(2 + 3 + 0 + 5, 11, 0, 0, '#', [blt.color_from_name("yellow"),
                                                           blt.color_from_name("black"),
                                                           blt.color_from_name("black"),
                                                           blt.color_from_name("black")])
    blt.put_ext(2 + 3 + 1 + 5, 11, 0, 0, 'A', [blt.color_from_name("black"),
                                                           blt.color_from_name("yellow"),
                                                           blt.color_from_name("yellow"),
                                                           blt.color_from_name("black")])
    blt.put_ext(2 + 3 + 0 + 5, 12, 0, 0, 'A', [blt.color_from_name("black"),
                                                           blt.color_from_name("black"),
                                                           blt.color_from_name("yellow"),
                                                           blt.color_from_name("yellow")])
    blt.put_ext(2 + 3 + 1 + 5, 12, 0, 0,'@', [blt.color_from_name("dark yellow"),
                                                           blt.color_from_name("dark yellow"),
                                                           blt.color_from_name("dark yellow"),
                                                           blt.color_from_name("dark yellow")])

    blt.put_ext(2 + 3 + 0 + 7, 11, 0, 0, 'A', [blt.color_from_name("black"),
                                               blt.color_from_name("yellow"),
                                               blt.color_from_name("black"),
                                               blt.color_from_name("black")])

    blt.put_ext(2 + 3 + 0 + 7, 12, 0, 0, 'A', [blt.color_from_name("yellow"),
                                               blt.color_from_name("yellow"),
                                               blt.color_from_name("black"),
                                               blt.color_from_name("black")])


    '''
    # blt.color("lightest red")
    blt.put(2+3+4, 11,tiles['stairs'])
    # blt.color("purple")
    blt.put(2+3+8, 11, tiles['gold'])
    #blt.color("darkest red")
    blt.put(17, 11, 0xE400+0)
    blt.put(18, 11, 0xE400+0)
    blt.put(19, 11, 0xE400+1)
    blt.put(20, 11, 0xE400 + 0)
    blt.put(20, 11, 0xE400 + 2)

    blt.put(17, 12, 0xE400 + 10)
    blt.put(18, 12, 0xE400 + 10)
    blt.put(19, 12, 0xE400 + 11)
    blt.put(20, 12, 0xE400 + 10)
    blt.put(20, 12, 0xE400 + 12)
    '''
    blt.put(21, 11, 0xE400+0)
    blt.color("blue")
    blt.put(18, 11, '@')

    blt.color("white")
    order = [11, 10, 14, 12, 13]
    for i in range(len(order)):
        blt.put(30 + i * 4, 11, 0xE200 + order[i]);
        blt.put(30 + (len(order)+1) * 4, 11, 0xE200 + order[i])

    blt.put(30 + len(order) * 4, 11, 0xE200 + 15)

    blt.print_(2, 15, "[color=orange]5.[/color] And tiles can even come from TrueType fonts like this:")
    for i in range(6):
        blt.put(5 + i * 5, 15, 0xE300 + i)

    blt.print_(5, 18, "...or like this:\n[font=zodiac]D F G S C")

    blt.refresh()

    key = None
    while key not in (blt.TK_CLOSE, blt.TK_ESCAPE):
        key = blt.read()

    # Clean up
    blt.set("U+E100: none; U+E200: none; U+E300: none; zodiac font: none")
    blt.composition(False)
Пример #22
0
def test_extended_basics():
    # Setup
    blt.set("window.title='Omni: extended output / basics'")
    blt.set("0xE000: ../Media/Tiles.png, size=32x32, align=top-left")
    blt.composition(True)

    cx, cy = 10, 5
    n_symbols = 10
    radius = 5
    angle = 0.0
    fps = 25
    transparent, opaque = 0x00FFFFFF, 0xFFFFFFFF

    m00 = [0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFFFFF00]
    m01 = [opaque, opaque, transparent, transparent]

    m11 = [transparent, transparent, opaque, transparent]
    m12 = [transparent, opaque, transparent, transparent]
    m21 = [transparent, transparent, transparent, opaque]
    m22 = [opaque, transparent, transparent, transparent]

    while True:
        blt.clear()
        blt.color("white")

        blt.puts(
            2, 1,
            "[color=orange]1.[/color] put_ext(x, y, [color=orange]dx[/color], [color=orange]dy[/color], code)"
        )
        for i in range(n_symbols):
            angle_delta = 2 * pi / n_symbols
            dx = cos(angle + i * angle_delta) * radius * blt.state(
                blt.TK_CELL_WIDTH)
            dy = sin(angle + i * angle_delta) * radius * blt.state(
                blt.TK_CELL_WIDTH) - 4
            blt.color("white" if i > 0 else "orange")
            blt.put_ext(cx, cy, int(dx), int(dy), ord('a') + i)

        angle += 2 * pi / (2 * fps)

        blt.puts(
            2, 9,
            "[color=orange]2.[/color] put_ext(x, y, dx, dy, code, [color=orange]corners[/color])"
        )
        blt.put_ext(5, 11, 0, 0, 0xE000 + 19, m00)
        blt.put_ext(10, 11, 0, 0, 0xE000 + 19, m01)

        blt.puts(2, 14, "[color=orange]3.[/color] put_ext + composition")
        x1 = 5
        y1 = 16
        blt.put(x1 + 0, y1 + 0, 0xE000 + 19)
        blt.put(x1 + 0, y1 + 2, 0xE000 + 8)
        blt.put(x1 + 5, y1 + 0, 0xE000 + 19)
        blt.put(x1 + 9, y1 + 0, 0xE000 + 19)
        blt.put(x1 + 5, y1 + 2, 0xE000 + 19)
        blt.put(x1 + 9, y1 + 2, 0xE000 + 19)
        blt.put_ext(x1 + 5, y1 + 0, 0, 0, 0xE000 + 8, m11)
        blt.put_ext(x1 + 9, y1 + 0, 0, 0, 0xE000 + 8, m12)
        blt.put_ext(x1 + 5, y1 + 2, 0, 0, 0xE000 + 8, m21)
        blt.put_ext(x1 + 9, y1 + 2, 0, 0, 0xE000 + 8, m22)

        blt.refresh()

        if blt.has_input():
            key = blt.read()
            if key in (blt.TK_CLOSE, blt.TK_ESCAPE):
                break

        blt.delay(1000 // fps)

    # Clean up
    blt.composition(False)
    blt.set("0xE000: none")
Пример #23
0
def main():
    terminal.open()

    terminal.set("output.vsync=true")
    terminal.set(
        "window: title='Plasma Rain', resizeable=true, minimum-size=16x12")
    terminal.set("window: size={}x{}; font: ".format(INITIAL_SCREEN_WIDTH,
                                                     INITIAL_SCREEN_LENGTH) +
                 os.path.join(DIR_PATH, '../data/media/lucida.ttf') +
                 ", size={}x{}".format(TILE_WIDTH, TILE_LENGTH))
    terminal.set("input.filter= [keyboard+, mouse_move]"
                 )  # Only key release and mouse move trigger state updates
    terminal.composition(terminal.TK_ON)
    terminal.bkcolor(terminal.color_from_name("gray"))
    # grey (or gray), red, flame, orange, amber, yellow, lime,
    # chartreuse, green, sea, turquoise, cyan, sky, azure, blue,
    # han, violet, purple, fuchsia, magenta, pink, crimson, transparent
    terminal.color(terminal.color_from_name("black"))

    x_speed, y_speed, text_offset = (0, ) * 3

    # initialize blank tile_map
    tile_map: [[[Tile]]] = [[[EMPTY_TILE for _ in range(MAP_WIDTH)]
                             for _ in range(MAP_LENGTH)]
                            for _ in range(MAP_DEPTH)]
    unit_map: [[[Optional[Unit]]]] = [[[None for _ in range(MAP_WIDTH)]
                                       for _ in range(MAP_LENGTH)]
                                      for _ in range(MAP_DEPTH)]
    blueprints = build_blueprints(
        load_json(os.path.join(mods_dir, "vanilla/blueprints/"), pickle=False))
    unit_map[0][0][0] = Unit(blueprint=blueprints['unit']['Human'],
                             armor=blueprints['armor']['Suit'],
                             current_stats=[0, 0, 0, 0, 0],
                             overlay_stats=[0, 0, 0, 0, 0])
    zone = load_zone(os.path.join(DIR_PATH, '../data/placeholder/map2.json'),
                     blueprints)
    paste_zone(zone, tile_map, z=0, y=0)

    prev_frame_time = time.perf_counter()
    iterations = 0

    # tile_map will only render to screen inside the display, generally set to not overlap with UI
    # tile_map will only be scrollable between the offset bounds
    # coordinates are down-right = positive, "opposite" for the offset
    # set offset_leniency to 1 and you will only be allowed to scroll 1 more tile than enough to see every tile
    # in general the coordinate system is z, y, x: depth, length, width. No height because it's ambiguous.
    screen_width, screen_length, display_min_y, display_max_y, display_min_x, display_max_x, \
    min_y_offset, max_y_offset, min_x_offset, max_x_offset, x_scroll_leniency, y_scroll_leniency = (0,) * 12
    """
    Adjusts the bounds defined above, to be called when opening and resizing
    """
    def reset_bounds():
        # nonlocal allows modification of outer function's variables
        nonlocal screen_width, screen_length, display_min_y, display_max_y, display_min_x, display_max_x, \
            min_y_offset, max_y_offset, min_x_offset, max_x_offset, y_scroll_leniency, x_scroll_leniency

        screen_length = terminal.state(terminal.TK_HEIGHT) * TILE_LENGTH
        screen_width = terminal.state(terminal.TK_WIDTH) * TILE_WIDTH

        y_scroll_leniency = int(screen_length * SCROLL_LENIENCY)
        x_scroll_leniency = int(screen_length * SCROLL_LENIENCY)

        display_min_y = 0 + MARGIN_TOP * TILE_LENGTH
        display_max_y = screen_length - MARGIN_BOTTOM * TILE_LENGTH
        display_min_x = 0 + MARGIN_LEFT * TILE_WIDTH
        display_max_x = screen_width - MARGIN_RIGHT * TILE_WIDTH

        # TODO convert offsets into actual tile offsets, exclude the complex GUI stuff
        min_y_offset = min(0, -y_scroll_leniency)
        max_y_offset = max(
            0, -screen_length + MAP_WIDTH * TILE_LENGTH + y_scroll_leniency)
        min_x_offset = min(0, -x_scroll_leniency)
        max_x_offset = max(
            0, -screen_width + MAP_WIDTH * TILE_WIDTH + x_scroll_leniency)

    def get_highest_object_if_exists(start_z: int, tile_y: int, tile_x: int,
                                     include_tiles: bool = True, include_units: bool = False) -> \
            Union[None, Tile, Unit]:
        if tile_y not in range(0, MAP_WIDTH) or tile_x not in range(
                0, MAP_LENGTH):
            return None
        for zi in range(start_z, -1, -1):
            if include_units and unit_map[zi][tile_y][tile_x] is not None:
                return unit_map[zi][tile_y][tile_x]
            elif include_tiles and tile_map[zi][tile_y][tile_x] != EMPTY_TILE:
                return tile_map[zi][tile_y][tile_x]
        return None

    reset_bounds()

    y_offset, x_offset = max(display_min_y, 0), max(0, display_min_x)

    camera_height = 0

    proceed = True
    while proceed:
        # t = partial-tile offset in pixels
        # i = full-tile offset in tiles
        # c = number of tiles to render
        ty = y_offset % TILE_LENGTH
        tx = x_offset % TILE_WIDTH
        iy = y_offset // TILE_LENGTH
        ix = x_offset // TILE_WIDTH
        # vc = screen_length // TILE_SIZE + 1
        # hc = screen_width // TILE_SIZE + 1
        mouse_x = terminal.state(
            terminal.TK_MOUSE_X) - x_offset - display_min_x // TILE_WIDTH
        mouse_y = terminal.state(
            terminal.TK_MOUSE_Y) - y_offset - display_min_y // TILE_LENGTH

        x_offset = clamp(x_offset + x_speed, min_x_offset, max_x_offset)
        y_offset = clamp(y_offset + y_speed, min_y_offset, max_y_offset)

        terminal.clear()

        mouse_over_tile = get_highest_object_if_exists(camera_height, mouse_y,
                                                       mouse_x)
        mouse_over_tile = mouse_over_tile.blueprint.name if mouse_over_tile is not None else "Empty"
        mouse_over_unit = get_highest_object_if_exists(camera_height,
                                                       mouse_y,
                                                       mouse_x,
                                                       include_units=True,
                                                       include_tiles=False)
        mouse_over_unit = mouse_over_unit.blueprint.name if mouse_over_unit else "Empty"

        terminal.print(2, 0, "speed: {}, {}".format(x_speed, y_speed))
        terminal.print(
            2, 1, "offset: {}, {}, height : {}".format(ix, iy, camera_height))
        terminal.print(
            2, 2, "tile at ({}, {}): {}".format(mouse_x, mouse_y,
                                                mouse_over_tile))
        terminal.print(2, 3, "unit: {}".format(mouse_over_unit))

        higher_tile_already_rendered: [[bool]
                                       ] = [[False for _ in range(MAP_WIDTH)]
                                            for _ in range(MAP_LENGTH)]

        # print scrollable map
        for z in range(camera_height, -1,
                       -1):  # top has higher render priority
            for y in range(0, MAP_LENGTH):
                for x in range(0, MAP_WIDTH):
                    # s = final coords in pixels
                    sx = (x + ix) * TILE_WIDTH + tx + display_min_x
                    sy = (y + iy) * TILE_LENGTH + ty + display_min_y
                    # render only on-screen tiles
                    if (display_min_y <= sy <= display_max_y
                            and display_min_x <= sx <= display_max_x
                            and not higher_tile_already_rendered[y][x]
                            and (tile_map[z][y][x] != EMPTY_TILE
                                 or unit_map[z][y][x] is not None)):
                        if unit_map[z][y][x] is not None:
                            terminal.put_ext(0, 0, sx, sy,
                                             unit_map[z][y][x].blueprint.icon)
                            higher_tile_already_rendered[y][x] = True
                            # TODO why is it printing two chars?
                        else:
                            if z < camera_height and tile_map[z][y][
                                    x] != EMPTY_TILE:
                                terminal.put_ext(
                                    0, 0, sx, sy, 0x2588,
                                    (terminal.color_from_name('yellow'),
                                     terminal.color_from_name('red')) * 4)
                            terminal.put_ext(0, 0, sx, sy,
                                             tile_map[z][y][x].blueprint.icon)
                            higher_tile_already_rendered[y][x] = True

        terminal.refresh()

        while proceed and terminal.has_input():
            key = terminal.read()
            if key == terminal.TK_CLOSE or key == terminal.TK_ESCAPE:
                proceed = False
            elif key == terminal.TK_RESIZED:
                reset_bounds()
            elif key == terminal.TK_KP_PLUS:
                camera_height = clamp(camera_height + 1, 0, MAP_DEPTH - 1)
            elif key == terminal.TK_KP_MINUS:
                camera_height = clamp(camera_height - 1, 0, MAP_DEPTH - 1)
        if terminal.state(terminal.TK_LEFT):
            if x_speed < SPEED_CAP:
                x_speed += SPEED_ACCELERATION
            else:
                x_speed = SPEED_CAP
        elif terminal.state(terminal.TK_RIGHT):
            if x_speed > -SPEED_CAP:
                x_speed -= SPEED_ACCELERATION
            else:
                x_speed = -SPEED_CAP
        else:
            x_speed -= sgn(x_speed)
        if terminal.state(terminal.TK_UP):
            if y_speed < SPEED_CAP:
                y_speed += SPEED_ACCELERATION
            else:
                y_speed = SPEED_CAP
        elif terminal.state(terminal.TK_DOWN):
            if y_speed > -SPEED_CAP:
                y_speed -= SPEED_ACCELERATION
            else:
                y_speed = -SPEED_CAP
        else:
            y_speed -= sgn(y_speed)

        current_time = time.perf_counter()
        # If twice as slow as desirable warn us, check once every 10 seconds
        if iterations % (
                10 * FPS
        ) == 0 and iterations > 0 and current_time - prev_frame_time > 2 / FPS:
            print(
                "Lag detected. Desired frame_time: {}; actual frame_time: {}".
                format(1 / FPS, current_time - prev_frame_time))
        prev_frame_time = current_time
        iterations += 1

        terminal.delay(1000 // FPS)
    terminal.close()
Пример #24
0
 def put_ext(self, *args):
     if isinstance(args[0], Point):
         return _terminal.put_ext(args[0].x, args[0].y, args[1].x,
                                  args[1].y, *args[2:])
     else:
         return _terminal.put_ext(*args)
Пример #25
0
def clear_entity(entity):
    # erase the character that represents this object
    blt.bkcolor(blt.color_from_name("transparent"))
    blt.put_ext(entity.x, entity.y, 0, 0, ' ', None)
Пример #26
0
def test_dynamic_sprites():
    blt.set("window.title='Omni: dynamic sprites'")
    blt.set("U+E000: ../Media/Tiles.png, size=32x32, align=top-left")

    map_width = len(map_[0])
    map_height = len(map_)

    x0 = y0 = 0
    view_height, view_width = 10, 14
    minimap_scale = 4
    panel_width = (blt.state(blt.TK_WIDTH) - view_width * 4 - 1) * blt.state(
        blt.TK_CELL_WIDTH)
    margin = (panel_width - map_width * minimap_scale) // 2

    def draw_map():
        blt.color("white")
        for y in range(y0, y0 + view_height):
            for x in range(x0, x0 + view_width):
                code = map_[y][x]
                if code in palette:
                    s = palette[code]
                    blt.put((x - x0) * 4, (y - y0) * 2, 0xE000 + s.tile)

    def argb_from_color(col):
        return (col & 0xFF000000) >> 24, (col & 0xFF0000) >> 16, (
            col & 0xFF00) >> 8, col & 0xFF

    def blend_colors(one, two):
        a1, r1, g1, b1 = argb_from_color(one)
        a2, r2, g2, b2 = argb_from_color(two)
        f = a2 / 255
        r = int(r1 * (1 - f) + r2 * f)
        g = int(g1 * (1 - f) + g2 * f)
        b = int(b1 * (1 - f) + b2 * f)
        return blt.color_from_argb(a1, r, g, b)

    def make_minimap():
        minimap = [
            palette[code].color if code in palette else 0xFF000000
            for row in map_ for code in row
        ]

        for y in range(y0, y0 + view_height):
            for x in range(x0, x0 + view_width):
                minimap[y * map_width + x] = blend_colors(
                    minimap[y * map_width + x], 0x60FFFFFF)

        minimap = (c_uint32 * len(minimap))(*minimap)
        blt.set(
            "U+E100: %d, raw-size=%dx%d, resize=%dx%d, resize-filter=nearest" %
            (addressof(minimap), map_width, map_height, map_width * 4,
             map_height * 4))

    while True:
        blt.clear()

        draw_map()
        blt.color("light gray")
        for x in range(80):
            blt.put(x, view_height * 2, 0x2580)
        for y in range(view_height * 2):
            blt.put(view_width * 4, y, 0x2588)

        make_minimap()
        blt.color("white")
        blt.put_ext(view_width * 4 + 1, 0, margin, margin, 0xE100)

        blt.puts(
            1, view_height * 2 + 1,
            "[color=orange]Tip:[/color] use arrow keys to move viewport over the map"
        )

        blt.refresh()

        key = blt.read()

        if key in (blt.TK_CLOSE, blt.TK_ESCAPE):
            break
        elif key == blt.TK_RIGHT and x0 < map_width - view_width:
            x0 += 1
        elif key == blt.TK_LEFT and x0 > 0:
            x0 -= 1
        elif key == blt.TK_DOWN and y0 < map_height - view_height:
            y0 += 1
        elif key == blt.TK_UP and y0 > 0:
            y0 -= 1

    blt.set("U+E000: none; U+E100: none;")