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.
322 lines
10 KiB
Python
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.")
|