diff --git a/agame/game.py b/agame/game.py index 8c7f2e2..a77161c 100644 --- a/agame/game.py +++ b/agame/game.py @@ -33,6 +33,7 @@ class Database: OpenTrigger(), CloseTrigger(), GoTrigger(), + HelpTrigger(), ] ) @@ -56,6 +57,13 @@ class Database: def add_trigger(self, trigger: Trigger): self.triggers += [trigger] + def add_pre_trigger(self, trigger: Trigger): + """ + Add a trigger to the front of the trigger list. This can be used to + override the behavior of a trigger that is added by default. + """ + self.triggers = [trigger] + self.triggers + @dataclasses.dataclass class Game: diff --git a/agame/trigger.py b/agame/trigger.py index 4096785..62af989 100644 --- a/agame/trigger.py +++ b/agame/trigger.py @@ -1,7 +1,7 @@ import abc import re from typing import Match, Pattern, TYPE_CHECKING -from agame.util import search_item_name +from agame.util import search_item_name, trigger_help_builder if TYPE_CHECKING: from agame.game import Game @@ -22,6 +22,7 @@ if TYPE_CHECKING: # (put down)/drop [a[n]/the] x __all__ = ( "Trigger", + "HelpTrigger", "GetTrigger", "UseTrigger", "PutTrigger", @@ -30,6 +31,7 @@ __all__ = ( "OpenTrigger", "CloseTrigger", "GoTrigger", + "HELP", "GET", "USE", "PUT", @@ -39,6 +41,7 @@ __all__ = ( "CLOSE", "GO", ) +HELP = "help" GET = "get" USE = "use" PUT = "put" @@ -55,11 +58,34 @@ class Trigger(metaclass=abc.ABCMeta): def pattern() -> Pattern: pass + @staticmethod + @abc.abstractmethod + def help() -> str: + pass + @abc.abstractmethod def trigger(self, game: "Game", match: Match): pass +class HelpTrigger(Trigger): + @staticmethod + def pattern() -> Pattern: + return re.compile("help", re.IGNORECASE) + + @staticmethod + def help() -> str: + return trigger_help_builder("help") + + def trigger(self, game: "Game", _match: Match): + game.say("In this game, short commands are usually the best.") + game.say() + game.say("These are the available actions that are recognized:") + game.say() + for trigger in game.database.triggers: + game.say(trigger.help()) + + class GetTrigger(Trigger): @staticmethod def pattern() -> Pattern: @@ -71,6 +97,12 @@ class GetTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder( + ("get", "take", "grab", "pick up", "pickup"), "item" + ) + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: @@ -101,6 +133,10 @@ class UseTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder("use", ("item", "[with/on target]")) + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: @@ -152,6 +188,10 @@ class PutTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder("put", ("item", "[on/in target]")) + def trigger(self, game: "Game", match: Match): item_name = match["item"] @@ -167,6 +207,10 @@ class LookTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder("look", args=["[at]", "item"]) + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: @@ -193,6 +237,10 @@ class ReadTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder("read", "item") + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: @@ -219,6 +267,10 @@ class OpenTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder("open", "item") + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: @@ -243,6 +295,10 @@ class CloseTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder("close", "item") + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: @@ -267,6 +323,10 @@ class GoTrigger(Trigger): re.IGNORECASE | re.VERBOSE, ) + @staticmethod + def help() -> str: + return trigger_help_builder(["go", "go to", "goto", "leave", "exit"], "target") + def trigger(self, game: "Game", match: Match): item_name = match["item"] if not item_name: diff --git a/agame/util.py b/agame/util.py index a7283f3..7f59b67 100644 --- a/agame/util.py +++ b/agame/util.py @@ -1,4 +1,6 @@ -from typing import Iterable, Optional, TYPE_CHECKING +from typing import Iterable, Optional, Sequence, Union, TYPE_CHECKING +import dataclasses +import re if TYPE_CHECKING: from agame.item import ItemInst @@ -9,3 +11,32 @@ def search_item_name(seq: Iterable["ItemInst"], item_name: str) -> Optional["Ite if item.name.lower() == item_name.lower() or item_name.lower() in item.synonyms: return item return None + + +def trigger_help_builder( + actions: Union[str, Sequence[str]], args: Union[str, Sequence[str]] = () +) -> str: + actions = (actions,) if isinstance(actions, str) else actions + args = (args,) if isinstance(args, str) else args + + actions = ( + f"(({actions}))" + if isinstance(actions, str) + else "/".join(f"(({a}))" for a in actions) + ) + + def fmt_arg(a: str): + if "[" in a and "]" in a: + return f"{{{{{a}}}}}" + else: + return a + + if args: + args = ( + fmt_arg(args) + if isinstance(args, str) + else " ".join(fmt_arg(a) for a in args) + ) + return f"{actions} {args}" + else: + return actions