moss-n-puddles/typeclasses/rooms.py
Howard Abrams 37f3e8d479 Tea and teacups.
Dabbler's room will produce a tea cup for each person.

A teapot can make different types of teas, or even just a random one.
2025-02-02 00:02:49 -08:00

322 lines
10 KiB
Python

"""
Room
Rooms are simple containers that has no location of their own.
"""
from evennia.objects.objects import DefaultRoom
from evennia.contrib.grid.extended_room import ExtendedRoom
from evennia.prototypes.spawner import spawn
from .objects import LightSource
from .drinkables import TEACUP_DESCS
from commands.drinkables import CmdSetTrolley
from utils.word_list import routput, character_has, Token
import random
# the system error-handling module is defined in the settings. We load the
# given setting here using utils.object_from_module. This way we can use
# it regardless of if we change settings later.
from django.conf import settings
from evennia import (
TICKER_HANDLER,
CmdSet,
Command,
DefaultExit,
DefaultRoom,
create_object,
default_cmds,
search_object,
syscmdkeys,
utils,
)
# from .objects import LightSource
_SEARCH_AT_RESULT = utils.object_from_module(settings.SEARCH_AT_RESULT)
from .objects import ObjectParent
class Room(ObjectParent, ExtendedRoom):
"""
Rooms are like any Object, except their location is None
(which is default). They also use basetype_setup() to
add locks so they cannot be puppeted or picked up.
(to change that, use at_object_creation instead)
See mygame/typeclasses/objects.py for a list of
properties and methods available on all Objects.
"""
is_dark = False
has_weather = False
pass
# -------------------------------------------------------------------------------
#
# Dark Room - a room with states
#
# This room limits the movemenets of its denizens unless they carry an active
# LightSource object (LightSource is defined in objects.LightSource)
#
# -------------------------------------------------------------------------------
DARK_MESSAGES = (
"It is pitch black. You are likely to be eaten by a grue.",
"It's pitch black. You fumble around but cannot find anything.",
"You don't see a thing. You feel around, managing to bump your fingers hard against something. Ouch!",
"You don't see a thing! Blindly grasping the air around you, you find nothing.",
"It's totally dark here. You almost stumble over something on the floor.",
"You are completely blind. For a moment you think you hear someone breathing nearby ... "
"\n ... surely you must be mistaken.",
)
ALREADY_LIGHTSOURCE = (
"You don't want to stumble around in blindness anymore. You already "
"found what you need. Let's get the fireplace lit already!"
)
FOUND_LIGHTSOURCE = (
"Your fingers bump against a candle on a candleabra."
"You pick it up, holding it firmly. Now you just need to"
" |wlight|n it using the flint and steel you carry with you."
)
class CmdLookDark(Command):
"""
Look around in darkness
Usage:
look
Look around in the darkness, trying
to find something.
"""
key = "look"
aliases = ["l", "feel", "search", "feel around", "fiddle"]
locks = "cmd:all()"
def func(self):
"""
Implement the command.
This works both as a look and a search command; there is a
random chance of eventually finding a light source.
"""
caller = self.caller
# count how many searches we've done
nr_searches = caller.ndb.dark_searches
if nr_searches is None:
nr_searches = 0
caller.ndb.dark_searches = nr_searches
if nr_searches < 4 and random.random() < 0.90:
# we don't find anything
caller.msg(random.choice(DARK_MESSAGES))
caller.ndb.dark_searches += 1
else:
# we could have found something!
if any(obj for obj in caller.contents if utils.inherits_from(obj, LightSource)):
# we already carry a LightSource object.
caller.msg(ALREADY_LIGHTSOURCE)
else:
# don't have a light source, create a new one.
create_object(LightSource, key="candle", location=caller)
caller.msg(FOUND_LIGHTSOURCE)
class CmdDarkHelp(Command):
"""
Help command for the dark state.
"""
key = "help"
locks = "cmd:all()"
def func(self):
"""
Replace the the help command with a not-so-useful help
"""
string = (
"Can't help you until you find some light! Try looking/feeling around for something to burn. "
"You shouldn't give up even if you don't find anything right away."
)
self.caller.msg(string)
class CmdDarkNoMatch(Command):
"""
This is a system command. Commands with special keys are used to
override special sitations in the game. The CMD_NOMATCH is used
when the given command is not found in the current command set (it
replaces Evennia's default behavior or offering command
suggestions)
"""
key = syscmdkeys.CMD_NOMATCH
locks = "cmd:all()"
def func(self):
"""Implements the command."""
self.caller.msg(
"Until you find some light, there's not much you can do. "
"Try feeling around, maybe you'll find something helpful!"
)
class DarkCmdSet(CmdSet):
"""
Groups the commands of the dark room together. We also import the
default say command here so that players can still talk in the
darkness.
We give the cmdset the mergetype "Replace" to make sure it
completely replaces whichever command set it is merged onto
(usually the default cmdset)
"""
key = "darkroom_cmdset"
mergetype = "Replace"
priority = 2
def at_cmdset_creation(self):
"""populate the cmdset."""
self.add(CmdLookDark())
self.add(CmdDarkHelp())
self.add(CmdDarkNoMatch())
self.add(default_cmds.CmdSay())
self.add(default_cmds.CmdQuit())
self.add(default_cmds.CmdHome())
class DarkRoom(Room):
"""A dark room. This tries to start the DarkState script on all
objects entering. The script is responsible for making sure it is
valid (that is, that there is no light source shining in the room).
The is_lit Attribute is used to define if the room is currently lit
or not, so as to properly echo state changes.
Since this room is meant as a sort of catch-all, we also make sure
to heal characters ending up here.
"""
def at_object_creation(self):
"""
Called when object is first created.
"""
super().at_object_creation()
# the room starts dark.
self.db.is_lit = False
self.cmdset.add(DarkCmdSet, persistent=True)
def at_init(self):
"""
Called when room is first recached (such as after a reload)
"""
self.check_light_state()
def _carries_light(self, obj):
"""
Checks if the given object carries anything that gives light.
Note that we do NOT look for a specific LightSource typeclass,
but for the Attribute is_giving_light - this makes it easy to
later add other types of light-giving items. We also accept
if there is a light-giving object in the room overall (like if
a candle was dropped in the room or the fireplace is lit).
"""
return (
obj.is_superuser
or obj.db.is_giving_light
or any(o for o in obj.contents if o.db.is_giving_light)
)
def _heal(self, character):
"""
Heal a character.
"""
health = character.db.health_max or 20
character.db.health = health
def check_light_state(self, exclude=None):
"""
This method checks if there are any light sources in the room.
If there isn't it makes sure to add the dark cmdset to all
characters in the room. It is called whenever characters enter
the room and also by the Light sources when they turn on.
Args:
exclude (Object): An object to not include in the light check.
"""
if any(self._carries_light(obj) for obj in self.contents if obj != exclude):
self.locks.add("view:all()")
self.cmdset.remove(DarkCmdSet)
self.db.is_lit = True
for char in (obj for obj in self.contents if obj.has_account):
# this won't do anything if it is already removed
char.msg("The room is lit up.")
else:
# noone is carrying light - darken the room
self.db.is_lit = False
self.locks.add("view:false()")
self.cmdset.add(DarkCmdSet, persistent=True)
for char in (obj for obj in self.contents if obj.has_account):
if char.is_superuser:
char.msg("You are Superuser, so you are not affected by the dark state.")
else:
# put players in darkness
char.msg("The room is completely dark.")
def at_object_receive(self, obj, source_location, move_type="move", **kwargs):
"""
Called when an object enters the room.
"""
if obj.has_account:
# a puppeted object, that is, a Character
self._heal(obj)
# in case the new guy carries light with them
self.check_light_state()
def at_object_leave(self, obj, target_location, move_type="move", **kwargs):
"""
In case people leave with the light, we make sure to clear the
DarkCmdSet if necessary. This also works if they are
teleported away.
"""
# since this hook is called while the object is still in the room,
# we exclude it from the light check, to ignore any light sources
# it may be carrying.
self.check_light_state(exclude=obj)
class DabblersRoom(Room):
teacup_prototype = {
"typeclass": "typeclasses.drinkables.TeaCup",
"key": "teacup",
"desc": "A nice teacup.",
}
def at_object_creation(self):
"""
called at creation
"""
self.cmdset.add_default(CmdSetTrolley, persistent=True)
def produce_teacup(self, caller):
if character_has(caller, "teacup"):
caller.msg("You already have a teacup.")
else:
cuptype = self.teacup_prototype
cuptype['desc'] = routput(random.choice(TEACUP_DESCS))
cup = spawn(cuptype)[0]
cup.location = caller
caller.msg("You pick up a teacup.")