Files
ages/agame/trigger.py
Alek Ratzloff 7f86aafc05 Update example game some, update engine some
* Add RevealAction/UnrevealAction for revealing/hiding items in a room
* Add a lot of checks for items being revealed when it's attempted to be
  triggered
* Implement TeleportAction (mostly)
* For all Check* family of actions, the `yes` and `no` values may be
  just be a single action instead of an array of actions
* Change up how room descriptions and stuff work, mostly so that you can
  specify multiple lines in an array so you can preserve paragraph
  breaks when displayed.
* Example game has some more content

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2021-11-18 16:26:16 -08:00

282 lines
7.9 KiB
Python

import abc
import re
from typing import Match, Pattern, TYPE_CHECKING
from agame.util import search_item_name
if TYPE_CHECKING:
from agame.game import Game
# Triggers:
# get/take/grab/pick up [a[n]/the] x
# use [a[n]/the] x [with/on [a[n]/the] y]
# put [a[n]/the] x on/in [a[n]/the] y
# look [[at] [a[n]/the] x]
# read [a[n]/the] x
# open x
# close x
# go/(go to)/leave/exit x
# give [a[n]/the] x to [a[n]/the] y - TODO
# push [a[n]/the] x - TODO
# pull [a[n]/the] x - TODO
# (put down)/drop [a[n]/the] x
__all__ = (
"Trigger",
"GetTrigger",
"UseTrigger",
"PutTrigger",
"LookTrigger",
"ReadTrigger",
"OpenTrigger",
"CloseTrigger",
"GoTrigger",
"GET",
"USE",
"PUT",
"LOOK",
"READ",
"OPEN",
"CLOSE",
"GO",
)
GET = "get"
USE = "use"
PUT = "put"
LOOK = "look"
READ = "read"
OPEN = "open"
CLOSE = "close"
GO = "go"
class Trigger(metaclass=abc.ABCMeta):
@staticmethod
@abc.abstractmethod
def pattern() -> Pattern:
pass
@abc.abstractmethod
def trigger(self, game: "Game", match: Match):
pass
class GetTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>get|take|grab|pick[ ]*up)
(([ ]+(an?|the))?[ ]+(?P<item>.+))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
otrigger = match["trigger"].lower().capitalize()
game.say(f"{otrigger} what?")
return
item = game.room.search_item_name(item_name)
if item and item.revealed and GET in item.triggers:
actions = item.triggers[GET]
# if there are any actions, do them. else do the default action
game.do_actions(actions)
else:
game.say("Can't get that.")
class UseTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
# TODO(low) - wouldn't it be cool to specify "use" actions?
# e.g. you have a gun item and you want to be allowed to use "shoot" in order to
# use the gun.
return re.compile(
r"""
(?P<trigger>use)
(([ ]+(an?|the))?[ ]+(?P<item>.+?)
([ ]+(with|on)([ ]+(an?|the))?[ ]+(?P<target>.+))?)?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
game.say("Use what?")
return
target_name = match["target"]
# Get the item from inventory or room
item = game.room.search_item_name(item_name) or search_item_name(
game.inventory.values(), item_name
)
if not item or not item.revealed:
game.say(f"I'm not sure what you mean by {item_name}.")
return
target = None
if target_name:
# Get the target from inventory or room
target = game.room.search_item_name(target_name) or search_item_name(
game.inventory.values(), target_name
)
if not target or not target.revealed:
game.say(f"I'm not sure what you mean by {target_name}.")
# Check if the target can be used on something
elif target.id in item.use_actions:
game.do_actions(item.use_actions[target.id])
else:
game.say("I'm not sure how to do that.")
elif USE in item.triggers:
# Check if the item can be used by itself
game.do_actions(item.triggers[USE])
elif item.use_actions:
# This item can be used with *something*, but we don't know what.
game.say(f"Use (({item_name})) with what?")
else:
# This can't be used.
game.say("I can't really use that.")
class PutTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>put)
([ ]+((an?|the)[ ]+)?(?P<item>.+?)
((on|in)[ ]+)?((an?|the)[ ]+)?[ ]+(?P<target>.+))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
class LookTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>look)
([ ]+(at[ ]+)?((an?|the)[ ]+)?(?P<item>.+?))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
game.print_room()
return
item = game.room.search_item_name(item_name) or search_item_name(
game.inventory.values(), item_name
)
if item and LOOK in item.triggers and item.revealed:
actions = item.triggers[LOOK]
game.do_actions(actions)
else:
game.say("Can't see that.")
class ReadTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>read)
([ ]+((an?|the)[ ]+)?(?P<item>.+?))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
game.print_room()
return
item = game.room.search_item_name(item_name) or search_item_name(
game.inventory.values(), item_name
)
if item and READ in item.triggers and item.revealed:
actions = item.triggers[READ]
game.do_actions(actions)
else:
game.say("Can't read that.")
class OpenTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>open)
(((an?|the)[ ]+)?[ ]+(?P<item>.+?))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
game.say("Open what?")
return
item = game.room.search_item_name(item_name)
if item and OPEN in item.triggers and item.revealed:
actions = item.triggers[OPEN]
game.do_actions(actions)
else:
game.say("Can't open that.")
class CloseTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>close)
(((an?|the)[ ]+)?[ ]+(?P<item>.+?))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
game.say("Close what?")
return
item = game.room.search_item_name(item_name)
if item and CLOSE in item.triggers and item.revealed:
actions = item.triggers[CLOSE]
game.do_actions(actions)
else:
game.say("Can't close that.")
class GoTrigger(Trigger):
@staticmethod
def pattern() -> Pattern:
return re.compile(
r"""
(?P<trigger>go([ ]*to)?|leave|exit)
(((an?|the)[ ]+)?[ ]+(?P<item>.+?))?
""",
re.IGNORECASE | re.VERBOSE,
)
def trigger(self, game: "Game", match: Match):
item_name = match["item"]
if not item_name:
otrigger = match["trigger"].lower().capitalize()
game.say(f"{otrigger} where?")
return
item = game.room.search_item_name(item_name)
if item and GO in item.triggers and item.revealed:
actions = item.triggers[GO]
game.do_actions(actions)
else:
game.say("Can't go there.")