2021-11-18 11:31:21 -08:00
|
|
|
import dataclasses
|
|
|
|
|
import textwrap
|
2021-11-18 17:32:45 -08:00
|
|
|
from typing import Any, MutableMapping, List, Match, Optional, Sequence
|
2021-11-18 11:31:21 -08:00
|
|
|
from agame.action import Action
|
|
|
|
|
from agame.color import colorize
|
|
|
|
|
from agame.item import Item, ItemInst
|
|
|
|
|
from agame.room import Room
|
|
|
|
|
from agame.trigger import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = (
|
|
|
|
|
"Database",
|
|
|
|
|
"Game",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class Database:
|
|
|
|
|
# All items available to the game.
|
|
|
|
|
items: MutableMapping[str, Item] = dataclasses.field(default_factory=dict)
|
|
|
|
|
# All rooms available to the game.
|
|
|
|
|
rooms: MutableMapping[str, Room] = dataclasses.field(default_factory=dict)
|
2021-11-18 17:32:45 -08:00
|
|
|
# All variables available to the game.
|
|
|
|
|
vars: MutableMapping[str, Any] = dataclasses.field(default_factory=dict)
|
|
|
|
|
# All triggers available to the game.
|
|
|
|
|
triggers: List[Trigger] = dataclasses.field(
|
|
|
|
|
default_factory=lambda: [
|
|
|
|
|
GetTrigger(),
|
|
|
|
|
UseTrigger(),
|
|
|
|
|
PutTrigger(),
|
|
|
|
|
LookTrigger(),
|
|
|
|
|
ReadTrigger(),
|
|
|
|
|
OpenTrigger(),
|
|
|
|
|
CloseTrigger(),
|
|
|
|
|
GoTrigger(),
|
2021-11-18 19:31:38 -08:00
|
|
|
HelpTrigger(),
|
2021-11-18 17:32:45 -08:00
|
|
|
]
|
|
|
|
|
)
|
2021-11-18 11:31:21 -08:00
|
|
|
|
|
|
|
|
def add_item(self, item: Item):
|
|
|
|
|
self.items[item.id] = item
|
|
|
|
|
|
|
|
|
|
def add_items(self, *items: Item):
|
|
|
|
|
for item in items:
|
|
|
|
|
self.add_item(item)
|
|
|
|
|
|
|
|
|
|
def add_room(self, room: Room):
|
|
|
|
|
self.rooms[room.id] = room
|
|
|
|
|
|
|
|
|
|
def add_rooms(self, *rooms: Room):
|
|
|
|
|
for room in rooms:
|
|
|
|
|
self.add_room(room)
|
|
|
|
|
|
2021-11-18 17:32:45 -08:00
|
|
|
def add_var(self, var: str, value: Any):
|
|
|
|
|
self.vars[var] = value
|
|
|
|
|
|
|
|
|
|
def add_trigger(self, trigger: Trigger):
|
|
|
|
|
self.triggers += [trigger]
|
|
|
|
|
|
2021-11-18 19:31:38 -08:00
|
|
|
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
|
|
|
|
|
|
2021-11-18 11:31:21 -08:00
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class Game:
|
|
|
|
|
# Game room/items database
|
|
|
|
|
database: Database
|
|
|
|
|
# Current room.
|
|
|
|
|
room: Room
|
|
|
|
|
# Player inventory.
|
|
|
|
|
inventory: MutableMapping[str, ItemInst] = dataclasses.field(default_factory=dict)
|
|
|
|
|
|
|
|
|
|
def run_command(self, line: str):
|
|
|
|
|
line = line.strip()
|
|
|
|
|
if not line:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
trigger = None
|
|
|
|
|
match: Optional[Match] = None
|
2021-11-18 17:32:45 -08:00
|
|
|
for t in self.database.triggers:
|
2021-11-18 11:31:21 -08:00
|
|
|
match = t.pattern().fullmatch(line)
|
|
|
|
|
if match:
|
|
|
|
|
trigger = t
|
|
|
|
|
break
|
|
|
|
|
if not trigger:
|
|
|
|
|
self.say("I'm not sure what you mean.")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
assert match, "why were no patterns matched?"
|
|
|
|
|
trigger.trigger(self, match)
|
|
|
|
|
|
|
|
|
|
def do_actions(self, actions: Sequence[Action]):
|
|
|
|
|
"Executes the supplied actions."
|
|
|
|
|
for action in actions:
|
|
|
|
|
action.act(self)
|
|
|
|
|
|
|
|
|
|
def print_room(self):
|
|
|
|
|
"Prints this room's description."
|
2021-11-18 16:26:16 -08:00
|
|
|
self.say(f"(({self.room.name}))")
|
|
|
|
|
if isinstance(self.room.desc, str):
|
|
|
|
|
self.say(self.room.desc)
|
|
|
|
|
else:
|
|
|
|
|
assert isinstance(
|
|
|
|
|
self.room.desc, Sequence
|
|
|
|
|
), f"room.desc is not a list or a string from room id {room.id}"
|
|
|
|
|
self.say(*self.room.desc)
|
2021-11-18 11:31:21 -08:00
|
|
|
# Look at revealed text
|
2021-11-18 16:26:16 -08:00
|
|
|
for (index, item) in enumerate(self.room.items.values()):
|
|
|
|
|
if not item.revealed or item.room_desc is None or item.room_desc == []:
|
2021-11-18 11:31:21 -08:00
|
|
|
continue
|
2021-11-18 16:26:16 -08:00
|
|
|
|
|
|
|
|
if index != 0:
|
|
|
|
|
# Space this out with blank lines
|
|
|
|
|
self.say()
|
|
|
|
|
|
|
|
|
|
if isinstance(item.room_desc, str):
|
2021-11-18 11:31:21 -08:00
|
|
|
self.say(item.room_desc)
|
2021-11-18 16:26:16 -08:00
|
|
|
else:
|
|
|
|
|
assert isinstance(item.room_desc, Sequence)
|
|
|
|
|
self.say(*item.room_desc)
|
2021-11-18 11:31:21 -08:00
|
|
|
|
2021-11-18 16:26:16 -08:00
|
|
|
def say(self, *lines: str):
|
2021-11-18 11:31:21 -08:00
|
|
|
"Format, colorize, wrap, and print the message."
|
2021-11-18 16:26:16 -08:00
|
|
|
if lines:
|
|
|
|
|
head = textwrap.fill(lines[0])
|
|
|
|
|
print(colorize(head))
|
|
|
|
|
for line in lines[1:]:
|
|
|
|
|
message = textwrap.fill(line)
|
|
|
|
|
print()
|
|
|
|
|
print(colorize(message))
|
|
|
|
|
else:
|
|
|
|
|
print()
|
2021-11-18 11:31:21 -08:00
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def rooms(self):
|
|
|
|
|
"Shortcut property for `game.database.rooms`."
|
|
|
|
|
return self.database.rooms
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def items(self):
|
|
|
|
|
"Shortcut property for `game.database.items`."
|
|
|
|
|
return self.database.items
|
2021-11-18 17:32:45 -08:00
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def vars(self):
|
|
|
|
|
"Shortcut property for `game.database.vars`."
|
|
|
|
|
return self.database.vars
|