275 lines
9 KiB
Python
275 lines
9 KiB
Python
"""
|
|
Room
|
|
|
|
Rooms are simple containers that has no location of their own.
|
|
|
|
"""
|
|
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
from re import match
|
|
|
|
from django.utils.translation import gettext as _
|
|
|
|
from evennia import utils
|
|
from evennia.utils import gametime, logger, delay
|
|
from evennia.utils.utils import iter_to_str
|
|
from evennia.contrib.grid.extended_room import ExtendedRoom
|
|
from evennia.contrib.rpg.rpsystem import ContribRPRoom
|
|
|
|
from django.conf import settings
|
|
|
|
from typeclasses.pets import Hunger
|
|
from typeclasses.objects import ObjectParent, Listener
|
|
from utils.word_list import fix_msg
|
|
|
|
_SEARCH_AT_RESULT = utils.object_from_module(settings.SEARCH_AT_RESULT)
|
|
|
|
|
|
def articlize_character(character, capitalize=False):
|
|
"""
|
|
Return a simple character with article prefix.
|
|
"""
|
|
if capitalize:
|
|
article = "A"
|
|
else:
|
|
article = "a"
|
|
|
|
# Trim off any trailing periods that show up here.
|
|
if match(r".*\.$", character):
|
|
character = character[:-1]
|
|
|
|
# logger.info(f"rooms: articlize_character({character})")
|
|
if character == "" or match(r"^\|[A-z][A-Z]", character):
|
|
return character
|
|
elif match(r"^\|[A-z][aeiou]", character):
|
|
return f"{article}n " + character
|
|
else:
|
|
return f"{article} " + character
|
|
|
|
|
|
def articlize_characters(characters):
|
|
return ", ".join([articlize_character(c) for c in characters])
|
|
|
|
|
|
def captialize_characters(characters):
|
|
"""
|
|
Capitalizes and prefixes an article on string of characters.
|
|
Assuming one per line.
|
|
"""
|
|
char_list = characters.split("\n")
|
|
updated = [articlize_character(ch, True) for ch in char_list]
|
|
return " " + ", ".join(updated)
|
|
|
|
|
|
class Room(ObjectParent, ExtendedRoom, ContribRPRoom, Listener):
|
|
"""
|
|
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
|
|
appearance_template = """
|
|
|
|
{header}
|
|
|c{name}{extra_name_info}|n
|
|
{desc}
|
|
{exits}
|
|
{things}
|
|
{characters}
|
|
{footer}
|
|
"""
|
|
|
|
def get_time(self):
|
|
"""
|
|
Return tuple of current time of this location.
|
|
(Minute, Hour, Time of Day, Season)
|
|
"""
|
|
timestamp = gametime.gametime(absolute=True)
|
|
datestamp = datetime.fromtimestamp(timestamp)
|
|
return (datestamp.minute, datestamp.hour,
|
|
self.get_time_of_day(), self.get_season())
|
|
|
|
def get_display_header(self, looker, **kwargs):
|
|
"""
|
|
Possibly send an image to webclients.
|
|
"""
|
|
image = self.db.title_image
|
|
if image:
|
|
timed = self.attributes.get("title_time", None)
|
|
if timed:
|
|
# This makes: mellow-marsh-evening
|
|
image = f"{image}-{self.get_time_of_day()}"
|
|
|
|
season = self.attributes.get("title_season", None)
|
|
if season:
|
|
# This makes: frog-meadow-winter
|
|
image = f"{image}-{self.get_season()}"
|
|
|
|
level = self.attributes.get("title_level", None)
|
|
if level:
|
|
# This makes: cozy-house-2
|
|
image = f"{image}-{level}"
|
|
|
|
looker.msg(image=(
|
|
f"https://www.howardabrams.com/cozy-players-guide/{image}.jpg",
|
|
{"type": "background-pane"}
|
|
))
|
|
return "|n"
|
|
|
|
def get_display_footer(self, looker, **kwargs):
|
|
return "|n"
|
|
|
|
def get_display_characters(self, looker, *args, **kwargs):
|
|
chars = [c for c in self.contents_get(content_type="character")
|
|
if not c.attributes.get('transformed')]
|
|
vchars = self.filter_visible(chars, looker, **kwargs)
|
|
num_chars = len(vchars)
|
|
|
|
char_list = iter_to_str(
|
|
[articlize_character(char.get_display_name(looker, pose=True, **kwargs))
|
|
for char in vchars])
|
|
|
|
# We add the word 'also' in case we showed there were objects:
|
|
objs = self.contents_get(content_type="object")
|
|
if len(objs) > 0:
|
|
also = 'also '
|
|
else:
|
|
also = ''
|
|
|
|
if num_chars > 1:
|
|
return f"You {also}see the following characters: {char_list}."
|
|
|
|
if num_chars > 0:
|
|
character = char_list.strip()
|
|
if character.startswith('|'):
|
|
comp_char = character[2:].lower()
|
|
else:
|
|
comp_char = character.lower()
|
|
|
|
if match(r"an? |the ", comp_char):
|
|
return f"You {also}see {character}."
|
|
elif match(r"[aeiou]", comp_char):
|
|
return f"You {also}see an {character}."
|
|
else:
|
|
return f"You {also}see a {character}."
|
|
return ''
|
|
|
|
def get_display_things(self, looker, *args, **kwargs):
|
|
"""
|
|
Replace
|
|
"""
|
|
things = self.filter_visible(self.contents_get(content_type="object"),
|
|
looker, **kwargs)
|
|
tchars = [c for c in self.contents_get(content_type="character")
|
|
if c.attributes.get('transformed')]
|
|
|
|
grouped_things = defaultdict(list)
|
|
for thing in things + tchars:
|
|
grouped_things[thing.get_display_name(looker, **kwargs)].append(thing)
|
|
|
|
thing_names = []
|
|
for thingname, thinglist in sorted(grouped_things.items()):
|
|
nthings = len(thinglist)
|
|
thing = thinglist[0]
|
|
singular, plural = thing.get_numbered_name(nthings, looker, key=thingname)
|
|
if nthings > 1:
|
|
thing_names.append(plural)
|
|
elif thing.db.plural:
|
|
thing_names.append(thing.get_display_name(looker))
|
|
else:
|
|
thing_names.append(singular)
|
|
|
|
thing_names = iter_to_str(thing_names, endsep=_(", and"))
|
|
return _(f"You notice {thing_names}." if thing_names else "")
|
|
|
|
def msg_contents(self, text, exclude=None, from_obj=None, mapping=None,
|
|
raise_funcparse_errors=False, **kwargs):
|
|
text = fix_msg(text)
|
|
super().msg_contents(text, exclude, from_obj, mapping,
|
|
raise_funcparse_errors)
|
|
|
|
|
|
class DabblersRoom(Room):
|
|
def get_display_desc(self, looker):
|
|
fire = self.search("fire")
|
|
full_desc = self.db.initial_desc
|
|
if fire.hunger() == Hunger.RAVENOUS:
|
|
self.db.title_level = 0
|
|
full_desc += " " + self.db.fire_out
|
|
elif fire.hunger() == Hunger.HUNGRY:
|
|
self.db.title_level = 1
|
|
full_desc += " " + self.db.fire_dim
|
|
elif fire.hunger() == Hunger.FULL:
|
|
self.db.title_level = 2
|
|
full_desc += " " + self.db.fire_full
|
|
else:
|
|
self.db.title_level = 3
|
|
full_desc += " " + self.db.fire_on
|
|
full_desc += " " + self.db.final_desc
|
|
|
|
return full_desc
|
|
|
|
|
|
class OpenableRoom(Room):
|
|
"""
|
|
Rooms that accept the "open" and "close" command.
|
|
|
|
This will move an exit in and out of this location.
|
|
Set the follow properties:
|
|
|
|
@set here/open_exit = $search(red door)
|
|
@set here/open_exit_msg = "$You() $conj(open) the door!"
|
|
@set here/close_exit_msg = "$You() $conj(close) the door!"
|
|
@set here/close_automatic = 240
|
|
"""
|
|
def do_open(self, opener, item):
|
|
"""
|
|
Move the 'open_exit' to this location.
|
|
"""
|
|
the_exit = self.db.open_exit
|
|
the_item = self.db.open_item
|
|
if the_exit and ((the_item and item.lower() ==the_item.lower()) or
|
|
the_exit.key.lower().endswith(item)):
|
|
if the_exit.location ==self:
|
|
opener.msg(f"The {item} is already open.")
|
|
return True
|
|
the_exit.location = self
|
|
opener.announce_action(self.db.open_exit_msg or
|
|
f"$You() $conj(open) the {the_exit.name}")
|
|
if self.db.close_automatic:
|
|
delay(self.db.close_automatic, self.do_close)
|
|
return True
|
|
|
|
def do_close(self, closer=None, item=None):
|
|
"""
|
|
Move the 'open_exit' exit to Limbo.
|
|
"""
|
|
the_exit = self.db.open_exit
|
|
|
|
# If a user is trying to close an item (an automatic close,
|
|
# item will be None), so we just put in this check here:
|
|
the_item = self.db.open_item
|
|
if item and ((the_item and item.lower() ==the_item.lower()) or
|
|
the_exit.key.lower().endswith(item)):
|
|
pass
|
|
else:
|
|
return None
|
|
|
|
if the_exit:
|
|
if the_exit.location == self: # It is opened and can be closed:
|
|
the_exit.location = None
|
|
if closer:
|
|
closer.announce_action(self.db.close_exit_msg or
|
|
f"$You() $conj(close) the {the_exit.name}")
|
|
else:
|
|
self.msg_contents(self.db.close_exit_msg)
|
|
elif closer:
|
|
closer.msg(f"The {item} is already closed.")
|
|
return True
|