Add dialog actions
Rudimentary user input for dialogs has been added. * Displays must add support for showing dialog options, and receiving user input for dialog selections. * A selected dialog can optionally perform actions, and then directs the game into the next dialog state. * Dialog options may be conditionally shown with variables. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
import platform
|
||||
from typing import Optional, Sequence, Tuple
|
||||
from agame.action import Action
|
||||
from agame.dialog import DialogOption
|
||||
from .display import Display
|
||||
|
||||
if platform.system() in ("Linux", "Darwin", "Unix"):
|
||||
@@ -14,5 +17,31 @@ class BasicDisplay(Display):
|
||||
def line(self, line: str = ""):
|
||||
print(line)
|
||||
|
||||
def input(self) -> str:
|
||||
return input("> ")
|
||||
def input(self, prompt: Optional[str] = None) -> str:
|
||||
if prompt is None:
|
||||
prompt = "> "
|
||||
return input(prompt)
|
||||
|
||||
def wait_for_ack(self, prompt: Optional[str] = "Press enter to continue..."):
|
||||
input(prompt or "")
|
||||
|
||||
def dialog_options(self, options: Sequence[DialogOption]) -> DialogOption:
|
||||
valid_opts = range(1, len(options) + 1)
|
||||
|
||||
def print_opts():
|
||||
for i, opt in enumerate(options):
|
||||
self.line(f"{i}. {opt.text}")
|
||||
|
||||
print_opts()
|
||||
while True:
|
||||
got = self.input("? ")
|
||||
try:
|
||||
choice = int(got)
|
||||
except ValueError:
|
||||
print_opts()
|
||||
continue
|
||||
if choice in valid_opts:
|
||||
break
|
||||
index = choice - 1
|
||||
selection = options[index]
|
||||
return selection
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import abc
|
||||
from typing import Optional
|
||||
from typing import Optional, Sequence, Tuple, TYPE_CHECKING
|
||||
from agame.action import Action
|
||||
from agame.dialog import DialogOption
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agame.game import Game
|
||||
|
||||
|
||||
class Display(metaclass=abc.ABCMeta):
|
||||
@@ -14,7 +19,7 @@ class Display(metaclass=abc.ABCMeta):
|
||||
"Print a single line to the display."
|
||||
|
||||
@abc.abstractmethod
|
||||
def input(self) -> str:
|
||||
def input(self, prompt: Optional[str] = None) -> str:
|
||||
"Get player input."
|
||||
|
||||
@abc.abstractmethod
|
||||
@@ -28,5 +33,31 @@ class Display(metaclass=abc.ABCMeta):
|
||||
user.
|
||||
"""
|
||||
|
||||
def dialog_options(self, options: Sequence[DialogOption]) -> DialogOption:
|
||||
"""
|
||||
Displays, and gets, an answer from a list of dialog options.
|
||||
|
||||
Returns the selected DialogOption.next value and actions.
|
||||
"""
|
||||
valid_opts = range(1, len(options) + 1)
|
||||
|
||||
def print_opts():
|
||||
for i, opt in enumerate(options):
|
||||
self.line(f"{i}. {opt.text}")
|
||||
|
||||
print_opts()
|
||||
while True:
|
||||
got = self.input("? ")
|
||||
try:
|
||||
choice = int(got)
|
||||
except ValueError:
|
||||
print_opts()
|
||||
continue
|
||||
if choice in valid_opts:
|
||||
break
|
||||
index = choice - 1
|
||||
selection = options[index]
|
||||
return selection
|
||||
|
||||
def finish(self):
|
||||
"Final cleanup code for this display."
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import sys
|
||||
import termios
|
||||
from typing import Optional, TextIO
|
||||
from typing import Optional, Sequence, TextIO, Tuple, TYPE_CHECKING
|
||||
from agame.color import colorize
|
||||
from agame.action import Action
|
||||
from agame.dialog import DialogOption
|
||||
from .display import Display
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agame.game import Game
|
||||
|
||||
ESC = "\u001b"
|
||||
ANSI_MOVE_BOTTOM = f"{ESC}[999;0f"
|
||||
|
||||
@@ -45,10 +50,12 @@ class ANSIDisplay(Display):
|
||||
self.stdout.write(line)
|
||||
self.stdout.write("\n")
|
||||
|
||||
def input(self) -> str:
|
||||
def input(self, prompt: Optional[str] = None) -> str:
|
||||
if prompt is None:
|
||||
prompt = self.input_prompt
|
||||
try:
|
||||
self.nocbreak()
|
||||
return input(self.input_prompt)
|
||||
return input(prompt)
|
||||
finally:
|
||||
self.cbreak()
|
||||
|
||||
@@ -59,6 +66,33 @@ class ANSIDisplay(Display):
|
||||
self.line(prompt)
|
||||
self.stdin.read(1)
|
||||
|
||||
def dialog_options(self, options: Sequence[DialogOption]) -> DialogOption:
|
||||
valid_opts = range(1, len(options) + 1)
|
||||
|
||||
def print_opts():
|
||||
for i, opt in zip(valid_opts, options):
|
||||
# TODO - format/colorize this so it stands out
|
||||
self.line(f"{i}. {opt.text}")
|
||||
|
||||
print_opts()
|
||||
while True:
|
||||
got = self.input("? ")
|
||||
try:
|
||||
choice = int(got)
|
||||
except ValueError:
|
||||
print_opts()
|
||||
continue
|
||||
if choice in valid_opts:
|
||||
break
|
||||
index = choice - 1
|
||||
selection = options[index]
|
||||
return selection
|
||||
|
||||
def finish(self):
|
||||
# Clean up by restoring the old terminal state
|
||||
fd = self.stdin.fileno()
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, self.__old_attrs)
|
||||
|
||||
def cbreak(self):
|
||||
fd = self.stdin.fileno()
|
||||
new = termios.tcgetattr(fd)
|
||||
@@ -71,11 +105,6 @@ class ANSIDisplay(Display):
|
||||
new[3] |= termios.ECHO | termios.ICANON
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, new)
|
||||
|
||||
def finish(self):
|
||||
# Clean up by restoring the old terminal state
|
||||
fd = self.stdin.fileno()
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, self.__old_attrs)
|
||||
|
||||
def clear(self):
|
||||
self.stdout.write(f"{ESC}[2J")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user