Files
ages/agame/game.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

131 lines
3.6 KiB
Python

import dataclasses
import textwrap
from typing import Any, MutableMapping, Match, Optional, Sequence
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)
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)
@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)
# Variables.
vars: MutableMapping[str, Any] = dataclasses.field(default_factory=dict)
def run_command(self, line: str):
line = line.strip()
if not line:
return
triggers: Sequence[Trigger] = [
GetTrigger(),
UseTrigger(),
PutTrigger(),
LookTrigger(),
ReadTrigger(),
OpenTrigger(),
CloseTrigger(),
GoTrigger(),
]
trigger = None
match: Optional[Match] = None
for t in triggers:
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."
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)
# Look at revealed text
for (index, item) in enumerate(self.room.items.values()):
if not item.revealed or item.room_desc is None or item.room_desc == []:
continue
if index != 0:
# Space this out with blank lines
self.say()
if isinstance(item.room_desc, str):
self.say(item.room_desc)
else:
assert isinstance(item.room_desc, Sequence)
self.say(*item.room_desc)
def say(self, *lines: str):
"Format, colorize, wrap, and print the message."
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()
@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