going-rogue/erynrl/messages.py
Eryn Wells 6780b0495c 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.
2023-02-11 01:21:52 -08:00

93 lines
2.9 KiB
Python

# Eryn Wells <eryn@erynwells.me>
'''
Defines the classes the support the in-game message log. Messages are recorded to the log as game actions are handled. A
short buffer of messages is displayed in the game's HUD, and a full list of messages can be viewed by the player at any
time.
'''
import textwrap
from typing import List, Optional, Reversible, Tuple
import tcod
from .geometry import Rect
class Message:
'''A message in the message log
Attributes
----------
text : str
The text of the message
foreground : Tuple[int, int, int]
The foreground color to render the message with
count : int
The number of times this message has stacked
'''
def __init__(self, text: str, fg: Optional[Tuple[int, int, int]] = None):
self.text = text
self.foreground = fg
self.count = 1
@property
def full_text(self) -> str:
'''The full text of the message, including a count of repeats, if present'''
if self.count == 1:
return self.text
return f'{self.text} (x{self.count})'
def __str__(self) -> str:
return self.text
def __repr__(self) -> str:
return f'{self.__class__.__name__}({repr(self.text)}, fg={self.foreground})'
class MessageLog:
'''A buffer of messages sent to the player by the game'''
def __init__(self):
self.messages: List[Message] = []
def add_message(self, text: str, fg: Optional[Tuple[int, int, int]] = None, stack: bool = True):
'''
Add a message to the buffer
Parameters
----------
text : str
The text of the message
fg : Tuple[int, int, int], optional
A foreground color to render the text
stack : bool
If True and the previous message in the buffer is the same as the text given, increment the count of that
message rather than adding a new message to the buffer
'''
if stack and self.messages and self.messages[-1].text == text:
self.messages[-1].count += 1
else:
self.messages.append(Message(text, fg))
def render_to_console(self, console: tcod.console.Console, rect: Rect):
'''Render this message log to the given console in the given rect'''
self.render_messages(console, rect, self.messages)
@staticmethod
def render_messages(console: tcod.console.Console, rect: Rect, messages: Reversible[Message]):
'''Render a list of messages to the console in the given rect'''
y_offset = min(rect.size.height, len(messages)) - 1
for message in reversed(messages):
wrapped_text = textwrap.wrap(message.full_text, rect.size.width)
for line in wrapped_text:
console.print(x=rect.min_x, y=rect.min_y + y_offset, string=line, fg=message.foreground)
y_offset -= 1
if y_offset < 0:
break
if y_offset < 0:
break