Initial commit with a mostly working engine.

Basic commands are being parsed. I think the only weird part is the
'use' command because it needs to possibly target two things. A tiny
test example is provided in __main__, this will probably be broken out
later.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2021-11-18 11:31:21 -08:00
commit dd2128beb1
15 changed files with 1650 additions and 0 deletions

247
agame/trigger.py Normal file
View File

@@ -0,0 +1,247 @@
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]
# open x
# close x
# go/(go to)/leave/exit x
# give [a[n]/the] x to [a[n]/the] y
# push [a[n]/the] x
# pull [a[n]/the] x
# (put down)/drop [a[n]/the] x
__all__ = (
"Trigger",
"GetTrigger",
"UseTrigger",
"PutTrigger",
"LookTrigger",
"OpenTrigger",
"CloseTrigger",
"GoTrigger",
"GET",
"USE",
"PUT",
"LOOK",
"OPEN",
"CLOSE",
"GO",
)
GET = "get"
USE = "use"
PUT = "put"
LOOK = "look"
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.lower())
if item 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.lower()
)
if not item:
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.lower()
)
if not target:
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.lower())
if item and LOOK in item.triggers:
actions = item.triggers[LOOK]
game.do_actions(actions)
else:
game.say("Can't see 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.lower())
if item and OPEN in item.triggers:
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.lower())
if item and CLOSE in item.triggers:
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|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.lower())
if item and GO in item.triggers:
actions = item.triggers[GO]
game.do_actions(actions)