Move all the interface stuff to interface.Interface
Draw three windows with frames: - map window - info window (hit point bar; turn count) - message window Clean up the UI code in the Engine.
This commit is contained in:
parent
df4df06013
commit
6780b0495c
5 changed files with 114 additions and 21 deletions
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
import random
|
import random
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, MutableSet, NoReturn, Optional
|
from typing import TYPE_CHECKING, List, MutableSet, NoReturn, Optional
|
||||||
|
|
||||||
import tcod
|
import tcod
|
||||||
|
|
||||||
|
@ -15,8 +15,7 @@ from .actions.result import ActionResult
|
||||||
from .ai import HostileEnemy
|
from .ai import HostileEnemy
|
||||||
from .events import GameOverEventHandler, MainGameEventHandler
|
from .events import GameOverEventHandler, MainGameEventHandler
|
||||||
from .geometry import Point, Rect, Size
|
from .geometry import Point, Rect, Size
|
||||||
from .interface import color
|
from .interface import Interface
|
||||||
from .interface.percentage_bar import PercentageBar
|
|
||||||
from .map import Map
|
from .map import Map
|
||||||
from .map.generator import RoomsAndCorridorsGenerator
|
from .map.generator import RoomsAndCorridorsGenerator
|
||||||
from .map.generator.room import BSPRoomGenerator
|
from .map.generator.room import BSPRoomGenerator
|
||||||
|
@ -104,26 +103,15 @@ class Engine:
|
||||||
self.update_field_of_view()
|
self.update_field_of_view()
|
||||||
|
|
||||||
# Interface elements
|
# Interface elements
|
||||||
self.hit_points_bar = PercentageBar(position=Point(4, 45), width=20, colors=list(color.HealthBar.bar_colors()))
|
self.interface = Interface(Size(80, 50), self.map, self.message_log)
|
||||||
|
|
||||||
self.message_log.add_message('Greetings adventurer!', fg=(127, 127, 255), stack=False)
|
self.message_log.add_message('Greetings adventurer!', fg=(127, 127, 255), stack=False)
|
||||||
|
|
||||||
def print_to_console(self, console):
|
def print_to_console(self, console):
|
||||||
'''Print the whole game to the given console.'''
|
'''Print the whole game to the given console.'''
|
||||||
self.map.highlight_points(self.__mouse_path_points or [])
|
self.map.highlight_points(self.__mouse_path_points or [])
|
||||||
|
|
||||||
self.map.print_to_console(console)
|
self.interface.update(self.hero, self.current_turn)
|
||||||
|
self.interface.draw(console)
|
||||||
console.print(x=1, y=45, string='HP:')
|
|
||||||
hp, max_hp = self.hero.fighter.hit_points, self.hero.fighter.maximum_hit_points
|
|
||||||
self.hit_points_bar.percent_filled = hp / max_hp
|
|
||||||
self.hit_points_bar.render_to_console(console)
|
|
||||||
console.print(x=6, y=45, string=f'{hp}/{max_hp}', fg=color.WHITE)
|
|
||||||
|
|
||||||
console.print(x=1, y=46, string=f'Turn: {self.current_turn}')
|
|
||||||
|
|
||||||
messages_rect = Rect(Point(x=27, y=45), Size(width=40, height=5))
|
|
||||||
self.message_log.render_to_console(console, messages_rect)
|
|
||||||
|
|
||||||
entities_at_mouse_position = []
|
entities_at_mouse_position = []
|
||||||
for ent in sorted(self.entities, key=lambda e: e.render_order.value):
|
for ent in sorted(self.entities, key=lambda e: e.render_order.value):
|
||||||
|
|
|
@ -1 +1,79 @@
|
||||||
# Eryn Wells <eryn@erynwells.me>
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from tcod.console import Console
|
||||||
|
|
||||||
|
from .color import HealthBar
|
||||||
|
from .percentage_bar import PercentageBar
|
||||||
|
from .window import Window
|
||||||
|
from ..geometry import Point, Rect, Size
|
||||||
|
from ..map import Map
|
||||||
|
from ..messages import MessageLog
|
||||||
|
from ..object import Hero
|
||||||
|
|
||||||
|
|
||||||
|
class Interface:
|
||||||
|
def __init__(self, size: Size, map: Map, message_log: MessageLog):
|
||||||
|
self.map_window = MapWindow(Rect(Point(0, 0), Size(size.width, size.height - 5)), map)
|
||||||
|
self.info_window = InfoWindow(Rect(Point(0, size.height - 5), Size(28, 5)))
|
||||||
|
self.message_window = MessageLogWindow(Rect(Point(28, size.height - 5), Size(size.width - 28, 5)), message_log)
|
||||||
|
|
||||||
|
def update(self, hero: Hero, turn_count: int):
|
||||||
|
self.info_window.turn_count = turn_count
|
||||||
|
self.info_window.update_hero(hero)
|
||||||
|
|
||||||
|
def draw(self, console: Console):
|
||||||
|
self.map_window.draw(console)
|
||||||
|
self.info_window.draw(console)
|
||||||
|
self.message_window.draw(console)
|
||||||
|
|
||||||
|
|
||||||
|
class MapWindow(Window):
|
||||||
|
def __init__(self, bounds: Rect, map: Map):
|
||||||
|
super().__init__(bounds)
|
||||||
|
self.map = map
|
||||||
|
|
||||||
|
def draw(self, console):
|
||||||
|
super().draw(console)
|
||||||
|
|
||||||
|
# TODO: Get a 2D slice of tiles from the map given a rect based on the window's drawable area
|
||||||
|
drawable_area = self.drawable_area
|
||||||
|
self.map.print_to_console(console, drawable_area)
|
||||||
|
|
||||||
|
|
||||||
|
class InfoWindow(Window):
|
||||||
|
def __init__(self, bounds: Rect):
|
||||||
|
super().__init__(bounds, framed=True)
|
||||||
|
|
||||||
|
self.turn_count: int = 0
|
||||||
|
|
||||||
|
drawable_area = self.drawable_area
|
||||||
|
self.hit_points_bar = PercentageBar(
|
||||||
|
position=Point(drawable_area.min_x + 6, drawable_area.min_y),
|
||||||
|
width=20,
|
||||||
|
colors=list(HealthBar.bar_colors()))
|
||||||
|
|
||||||
|
def update_hero(self, hero: Hero):
|
||||||
|
hp, max_hp = hero.fighter.hit_points, hero.fighter.maximum_hit_points
|
||||||
|
self.hit_points_bar.percent_filled = hp / max_hp
|
||||||
|
|
||||||
|
def draw(self, console):
|
||||||
|
super().draw(console)
|
||||||
|
|
||||||
|
drawable_area = self.drawable_area
|
||||||
|
console.print(x=drawable_area.min_x + 2, y=drawable_area.min_y, string='HP:')
|
||||||
|
self.hit_points_bar.render_to_console(console)
|
||||||
|
|
||||||
|
if self.turn_count:
|
||||||
|
console.print(x=drawable_area.min_x, y=drawable_area.min_y + 1, string=f'Turn: {self.turn_count}')
|
||||||
|
|
||||||
|
|
||||||
|
class MessageLogWindow(Window):
|
||||||
|
def __init__(self, bounds: Rect, message_log: MessageLog):
|
||||||
|
super().__init__(bounds, framed=True)
|
||||||
|
self.message_log = message_log
|
||||||
|
|
||||||
|
def draw(self, console):
|
||||||
|
super().draw(console)
|
||||||
|
self.message_log.render_to_console(console, self.drawable_area)
|
||||||
|
|
25
erynrl/interface/window.py
Normal file
25
erynrl/interface/window.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
|
from tcod.console import Console
|
||||||
|
|
||||||
|
from ..geometry import Rect
|
||||||
|
|
||||||
|
|
||||||
|
class Window:
|
||||||
|
def __init__(self, bounds: Rect, *, framed: bool = True):
|
||||||
|
self.bounds = bounds
|
||||||
|
self.is_framed = framed
|
||||||
|
|
||||||
|
@property
|
||||||
|
def drawable_area(self) -> Rect:
|
||||||
|
if self.is_framed:
|
||||||
|
return self.bounds.inset_rect(1, 1, 1, 1)
|
||||||
|
return self.bounds
|
||||||
|
|
||||||
|
def draw(self, console: Console):
|
||||||
|
if self.is_framed:
|
||||||
|
console.draw_frame(
|
||||||
|
self.bounds.origin.x,
|
||||||
|
self.bounds.origin.y,
|
||||||
|
self.bounds.size.width,
|
||||||
|
self.bounds.size.height)
|
|
@ -12,7 +12,7 @@ import numpy as np
|
||||||
import numpy.typing as npt
|
import numpy.typing as npt
|
||||||
import tcod
|
import tcod
|
||||||
|
|
||||||
from ..geometry import Point, Size
|
from ..geometry import Point, Rect, Size
|
||||||
from .generator import MapGenerator
|
from .generator import MapGenerator
|
||||||
from .tile import Empty, Shroud
|
from .tile import Empty, Shroud
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class Map:
|
||||||
for pt in points if points:
|
for pt in points if points:
|
||||||
self.highlighted[pt.x, pt.y] = True
|
self.highlighted[pt.x, pt.y] = True
|
||||||
|
|
||||||
def print_to_console(self, console: tcod.Console) -> None:
|
def print_to_console(self, console: tcod.Console, bounds: Rect) -> None:
|
||||||
'''Render the map to the console.'''
|
'''Render the map to the console.'''
|
||||||
size = self.size
|
size = self.size
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import tcod
|
||||||
|
|
||||||
from .geometry import Rect
|
from .geometry import Rect
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
'''A message in the message log
|
'''A message in the message log
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ class Message:
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'{self.__class__.__name__}({repr(self.text)}, fg={self.foreground})'
|
return f'{self.__class__.__name__}({repr(self.text)}, fg={self.foreground})'
|
||||||
|
|
||||||
|
|
||||||
class MessageLog:
|
class MessageLog:
|
||||||
'''A buffer of messages sent to the player by the game'''
|
'''A buffer of messages sent to the player by the game'''
|
||||||
|
|
||||||
|
@ -76,12 +78,12 @@ class MessageLog:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def render_messages(console: tcod.console.Console, rect: Rect, messages: Reversible[Message]):
|
def render_messages(console: tcod.console.Console, rect: Rect, messages: Reversible[Message]):
|
||||||
'''Render a list of messages to the console in the given rect'''
|
'''Render a list of messages to the console in the given rect'''
|
||||||
y_offset = min(rect.size.height, len(messages))
|
y_offset = min(rect.size.height, len(messages)) - 1
|
||||||
|
|
||||||
for message in reversed(messages):
|
for message in reversed(messages):
|
||||||
wrapped_text = textwrap.wrap(message.full_text, rect.size.width)
|
wrapped_text = textwrap.wrap(message.full_text, rect.size.width)
|
||||||
for line in wrapped_text:
|
for line in wrapped_text:
|
||||||
console.print(x=rect.min_x, y=rect.min_y + y_offset - 1, string=line, fg=message.foreground)
|
console.print(x=rect.min_x, y=rect.min_y + y_offset, string=line, fg=message.foreground)
|
||||||
y_offset -= 1
|
y_offset -= 1
|
||||||
|
|
||||||
if y_offset < 0:
|
if y_offset < 0:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue