Пример #1
0
class Follower(Object):
    following = None

    messages = Messages({
        'follow_ask': {
            'actor': 'You ask to follow {m$char{n.',
            'char': '{c$actor{n wants to follow you. Type "{clead $actor{n" '
                    'to let ${actor.him_her} follow you.',
            '*': '{c$actor{n wants to follow {m%char{n.'
        },
        'follow': {
            'actor': 'You start following {m$char{n.',
            '*': '{c$actor{n starts following {m$char{n.'
        },
        'unfollow': {
            'actor': 'You stop following {m$char{n.',
            '*': '{c$actor{n stops following {m$char{n.'
        }
    })

    @property
    def is_following(self):
        self._prune_following()
        return self.following is not None

    def _prune_following(self):
        before = self.following
        if self.following is not None:
            if not self.following.isa(FollowableObject):
                self.following = None
            elif self.location != self.following.location:
                self.stop_following()
        return before, self.following

    def start_following(self, char):
        if self.following == char:
            return
        if (not self.db.is_valid(char, FollowableObject)
                or self.location != char.location):
            raise InvalidObject(obj=char, msg="You can't follow that.")
        if self.is_following:
            self.stop_following()
        if not char.allows_following_by(self.ref()):
            self.emit_message('follow_ask', actor=self.ref(), char=char)
        else:
            self.following = char
            char.gain_follower(self.ref())
            self.emit_message('follow', actor=self.ref(), char=char)

    def stop_following(self, following=None):
        if following is None or self.following == following:
            following = self.following
            if self.db.is_valid(following, FollowableObject):
                following.lose_follower(self.ref())
            self.following = None
            self.emit_message('unfollow', actor=self.ref(), char=following)

    def do_follow(self, exit):
        if exit in self.context:
            exit.invoke(self.ref())
Пример #2
0
 def area_import(self, data, sandbox):
     super(AreaExportableBaseObject, self).area_import(data, sandbox)
     if 'id' in data:
         self.area_import_id = data['id']
     if self.obj_id is None:
         mudsling.game.db.register_object(self)
     if 'objmap' not in sandbox:
         sandbox['objmap'] = {}
     sandbox['objmap'][self.area_import_id] = self.ref()
     if sandbox.get('top', None) != self:
         # If this is a "top" (existing) object, don't rename it.
         if 'name' in data:
             self.set_name(data['name'])
         if 'aliases' in data:
             self.set_aliases(data['aliases'])
     if 'messages' in data:
         self.messages = Messages(messages=data['messages'])
Пример #3
0
class Thing(DescribableObject, ScriptableObject):
    """
    The basic object in the MUDSling core game world.

    Can be picked up, dropped, and given.
    """

    create_lock = locks.Lock('perm(create things)')

    messages = Messages({
        'drop': {
            'actor': "You drop $this.",
            '*': "$actor drops $this."
        },
        'drop_fail': {
            'actor': "You can't seem to drop $this.",
            '*': "$actor tries to drop $this, but fails."
        },
        'take': {
            'actor': "You take $this.",
            '*': "$actor takes $this."
        },
        'take_fail': {
            'actor': "You try to pick up $this, but fail.",
            '*': "$actor tires to pick up $this, but fails."
        },
        'give': {
            'actor': "You hand $this to $recipient.",
            'recipient': "$actor hands you $this.",
            '*': "$actor hands $this to $recipient."
        },
        'teleport_out': {
            'actor': "{bYou dematerialize.",
            '*': "{c$actor {bvanishes."
        },
        'teleport_in': {
            'actor': "{bYou materialize in {c$dest{b.",
            '*': "{c$actor {bmaterializes."
        },
    })
Пример #4
0
class Container(Thing):
    """
    The basic container-like thing.

    Can be opened or closed, and provides commands for putting things in it.
    """
    import commands.container
    public_commands = all_commands(commands.container)
    messages = Messages({
        'close': {
            'actor': 'You close $this.',
            '*': '$actor closes $this.'
        },
        'open': {
            'actor': 'You open $this.',
            '*': '$actor opens $this.'
        },
        'add': {
            'actor': 'You put $thing in $this.',
            '*': '$actor puts $thing in $this.'
        },
        'add_fail': {
            'actor': 'You cannot put $thing in $this.'
        },
        'remove': {
            'actor': 'You remove $thing from $this.',
            '*': '$actor removes $thing from $this.'
        },
        'remove_fail': {
            'actor': 'You cannot remove $thing from $this.'
        }
    })
    _opened = False
    can_close = True

    @property
    def opened(self):
        return self._opened if self.can_close else True

    @property
    def closed(self):
        return not self._opened

    def propagate_sensation_up(self, sensation):
        return self.opened

    def open(self, opened_by=None):
        """
        Opens the container.

        Will emit the 'open' message with opened_by as the actor if specified.

        :param opened_by: The object responsible for opening the container.
        :type opened_by: mudsling.objects.Object
        """
        if not self._opened:
            self._opened = True
            if opened_by is not None and opened_by.location == self.location:
                self.emit_message('open', actor=opened_by)
            else:
                self.emit([self.ref(), ' opens.'])

    def close(self, closed_by=None):
        """
        Closes the container. Emits message if `closed_by` is specified.

        :param closed_by: The object responsible for closing the container.
        :type closed_by: mudsling.objects.Object
        """
        if self._opened:
            self._opened = False
            if closed_by is not None and closed_by.location == self.location:
                self.emit_message('close', actor=closed_by)
            else:
                self.emit([self.ref(), ' closes.'])

    def desc_title(self, viewer):
        name = super(Container, self).desc_title(viewer)
        if self.can_close:
            if self._opened:
                name += ' {g(open)'
            else:
                name += ' {y(closed)'
        return name

    def contents_visible_to(self, obj):
        if self._opened or obj in self.contents:
            return super(Container, self).contents_visible_to(obj)
        else:
            return []

    def describe_to(self, viewer):
        desc = super(Container, self).describe_to(viewer)
        if self._opened:
            desc['open container contents'] = \
                'Contents:\n' + self.contents_as_seen_by(viewer)
        return desc
Пример #5
0
class Character(BaseCharacter, DescribableObject, SensingObject, HasGender,
                FollowableObject, Follower):
    """Core character class."""

    # Do not export characters to area files.
    area_exportable = False

    import commands.admin.building as building_commands
    import commands.character as character_commands
    private_commands = all_commands(
        character_commands,

        # Building commands are administrative, but they apply in a "physical"
        # manner to the game world, so they are attached to the Character
        # instead of the Player.
        building_commands,
    )
    _say_cmd = character_commands.SayCmd
    _emote_cmd = character_commands.EmoteCmd
    _look_cmd = character_commands.LookCmd
    del character_commands, building_commands

    from rooms import Room, Exit
    object_settings = {
        # The classes to use when creating rooms and exits with @dig.
        ObjSetting(name='building.room_class',
                   type=type,
                   attr='building_room_class',
                   default=lambda o: config.getclass('Classes', 'room class'),
                   parser=parsers.ObjClassStaticParser),
        ObjSetting(name='building.exit_class',
                   type=type,
                   attr='building_exit_class',
                   default=lambda o: config.getclass('Classes', 'exit class'),
                   parser=parsers.ObjClassStaticParser),
    }
    del Room, Exit

    messages = Messages({
        'say': {
            'actor': 'You say, "{g$speech{n".',
            '*': '{c$actor{n says, "{c$speech{n".'
        },
        'teleport_out': {
            'actor': "{bYou dematerialize.",
            '*': "{c$actor {bvanishes."
        },
        'teleport_in': {
            'actor': "{bYou materialize in {c$dest{b.",
            '*': "{c$actor {bmaterializes."
        },
        'take': {
            'actor': 'You take $obj.',
            '*': '$actor takes $obj.'
        },
        'take_fail': {
            'actor': 'You fail to take $obj.',
        },
        'drop': {
            'actor': 'You drop $obj.',
            '*': '$actor drops $obj.'
        },
        'drop_fail': {
            'actor': "You can't seem to drop $obj.",
        },
        'give': {
            'actor': 'You give $obj to $recipient.',
            'recipient': "$actor hands you $obj.",
            '*': '$actor gives $obj to $recipient.'
        },
        'give_fail': {
            'actor': 'You fail to give $obj to $recipient.'
        }
    })

    def is_possessable_by(self, player):
        """
        Core characters are ONLY possessable by players!
        :rtype: bool
        """
        return player.is_valid(Player)

    @property
    def player(self):
        """
        :rtype: Player or None
        """
        player = super(Character, self).player
        return player if player.is_valid(Player) else None

    def room_group(self, cls=None):
        from mudslingcore.rooms import RoomGroup
        cls = cls or RoomGroup
        for loc in self._location_walker():
            if loc.isa(RoomGroup) and loc.isa(cls):
                return loc
        return None

    def preemptive_command_match(self, raw):
        if raw.startswith('"'):
            return self._say_cmd(raw, '"', raw[1:], self.game, self.ref(),
                                 self.ref(), True)
        elif raw.startswith(':'):
            return self._emote_cmd(raw, ':', raw[1:], self.game, self.ref(),
                                   self.ref(), True)
        return None

    def match_literals(self, search, cls=None, err=False):
        if '->' in search and self.has_perm('use nested matching'):
            parts = filter(len, map(string.strip, search.split('->')))
            if len(parts) > 1:
                matches = self.match_object(parts[0], err=False)
                if len(matches) == 1:
                    for part in parts[1:]:
                        try:
                            matches = matches[0].match_contents(part)
                        except AttributeError:
                            matches = []
                            break
                    matches = obj_utils.filter_by_class(matches, cls=cls)
                    if err and len(matches) > 1:
                        raise AmbiguousMatch(query=search, matches=matches)
                    if matches:
                        return matches
        return super(Character, self).match_literals(search, cls=cls, err=err)

    @hook('after_moved')
    def __after_moved(self, moved_from, moved_to, by=None, via=None):
        if self.game.db.is_valid(moved_to, DescribableObject):
            cmd = self._look_cmd('look', 'look', '', self.game, self.ref(),
                                 self.ref())
            cmd.execute()

    def vision_sense(self, sensation):
        self.msg(sensation.content_for(self))

    def hearing_sense(self, sensation):
        content = self._format_msg(sensation.content_for(self))
        if isinstance(sensation, Speech):
            msg = [sensation.origin, ' says, "{c', content, '{n".']
        elif 'bare' in sensation.traits:
            msg = content
        else:
            msg = ["{mYou hear: {n", content]
        self.msg(msg)

    def say(self, speech):
        if not isinstance(speech, Speech):
            speech = Speech(str(speech), origin=self.ref())
        self.emit(speech, exclude=(self.ref(), ))
        self.tell('You say, "{g', speech.content, '{n".')

    def _prepare_emote(self,
                       pose,
                       sep=' ',
                       prefix='',
                       suffix='',
                       show_name=True):
        msg = [prefix, self.ref() if show_name else '', sep]
        if isinstance(pose, list):
            msg.extend(pose)
        else:
            msg.append(pose)
        if suffix:
            msg.extend((sep, suffix))
        return msg

    def emote(self, pose, sep=' ', prefix='', suffix='', show_name=True):
        self.emit(self._prepare_emote(pose, sep, prefix, suffix, show_name))
Пример #6
0
class Player(BasePlayer, ConfigurableObject, ChannelUser, EditorSessionHost,
             MailRecipient):
    """
    Core player class.
    """
    import commands.player as player_commands
    import commands.admin.system
    import commands.admin.perms
    import commands.admin.tasks
    import commands.admin.objects
    import commands.admin.players
    import commands.admin.areas
    import commands.scripting
    private_commands = all_commands(commands.admin.system,
                                    commands.admin.perms, commands.admin.tasks,
                                    commands.admin.objects,
                                    commands.admin.players,
                                    commands.admin.areas, commands.scripting,
                                    player_commands)
    del player_commands
    channels = {}

    messages = Messages({
        'teleport': {
            'actor': "You teleported {c$obj{n to {g$where{n.",
            'obj': "{c$actor{n teleported you to {g$where{n."
        },
        'teleport_failed': {
            'actor': "You fail to teleport {c$obj{n to {y$where{n."
        }
    })

    def __init__(self, **kwargs):
        super(Player, self).__init__(**kwargs)
        self.channels = {}

    def authenticate(self, password, session=None):
        applicable = bans.check_bans(session, self)
        if applicable:
            raise Exception("Connection banned: %s" % applicable[0])
        return super(Player, self).authenticate(password, session)

    def preemptive_command_match(self, raw):
        """
        :type raw: str
        """
        if raw.startswith(';') and self.has_perm("eval code"):
            import commands.admin.system
            return commands.admin.system.EvalCmd(raw, ';', raw[1:], self.game,
                                                 self.ref(), self.ref())
        if raw.startswith('?'):
            import commands.player
            cmd = commands.player.HelpCmd(raw, '?', raw[1:], self.game,
                                          self.ref(), self.ref())
            cmd.match_syntax(cmd.argstr)
            return cmd
        return None

    def find_help_topic(self, search):
        # fltr = lambda e: e.lock.eval(e, self.ref())
        def fltr(e):
            return e.lock.eval(e, self.ref())

        try:
            topic = help.help_db.find_topic(search, entryFilter=fltr)
        except FailedMatch:
            topic = self.find_command_help_topic(search)
        return topic

    def find_command_help_topic(self, search):
        #: :type: list of BaseObject
        totry = [self]
        if self.is_possessing:
            totry.append(self.possessing)
        #: :type: list of mudsling.commands.Command
        cmds = []
        for obj in totry:
            cmds = obj.match_command(search)
            if len(cmds):
                break
        if len(cmds) == 1:
            _, cmd = cmds[0]
        elif len(cmds) > 1:
            raise AmbiguousMatch(query=search, matches=cmds)
        else:
            raise FailedMatch(query=search)
        topic = help.CommandHelpEntry(cmd)
        return topic

    def session_attached(self, session):
        super(Player, self).session_attached(session)
        try:
            player_role = self.game.db.match_role('Core Player')
        except MatchError as e:
            logging.warning("Cannot confirm %s (%d) has Player role: %s",
                            self.name, self.obj_id, e.message)
        else:
            if not self.has_role(player_role):
                self.add_role(player_role)
Пример #7
0
class Wearable(mudslingcore.objects.Thing):
    """
    A generic wearable object.
    """
    worn_by = None
    public_commands = [WearCmd, UnwearCmd]
    messages = Messages({
        'wear': {
            'actor': 'You put on $this.',
            '*': '$actor puts on $this.'
        },
        'unwear': {
            'actor': 'You take $this off.',
            '*': '$actor takes $this off.'
        }
    })

    @property
    def is_worn(self):
        return self.worn_by is not None

    def check_wear_status(self):
        this = self.ref()
        try:
            if this not in self.worn_by.wearing:
                raise ValueError
            if self.worn_by is not None and self.location != self.worn_by:
                raise ValueError
        except (AttributeError, ValueError):
            wearer = self.worn_by
            self.worn_by = None
            if self.db.is_valid(wearer, Wearer) and this in wearer.wearing:
                wearer.wearing.remove(this)

    def is_worn_by(self, wearer):
        return wearer.ref() == self.worn_by.ref()

    def before_wear(self, wearer):
        wearer = wearer.ref()
        if self.worn_by == wearer:
            raise AlreadyWearingError()
        elif self.is_worn:
            raise CannotWearError()

    def on_wear(self, wearer):
        """
        Called after a wearable has been worn by a wearer.
        """
        self.worn_by = wearer.ref()
        if wearer.isa(Object) and wearer.has_location:
            self.emit_message('wear', location=wearer.location, actor=wearer)

    def before_unwear(self, wearer):
        if self.worn_by != wearer.ref():
            raise NotWearingError()

    def on_unwear(self):
        prev = self.worn_by
        self.worn_by = None
        if self.db.is_valid(prev, Object) and prev.has_location:
            self.emit_message('unwear', location=prev.location, actor=prev)

    @hook('before_moved')
    def __before_moved(self, moving_from, moving_to, by=None, via=None):
        self.check_wear_status()
        if self.is_worn:
            if by == self.worn_by:
                msg = "You must unwear %s before discarding it."
                msg = msg % by.name_for(self)
            else:
                msg = "%s is currently worn." % self.name
            raise errors.MoveDenied(self.ref(), moving_from, moving_to,
                                    self.ref(), msg=msg)
Пример #8
0
class FollowableObject(Object):
    messages = Messages({
        'follow_invite': {
            'actor': 'You invite $charlist to follow you.',
            '*': '{c$actor{n invites $charlist to follow ${actor.him_her}.'
        },
        'follow_uninvite': {
            'actor': 'You stop leading $charlist.',
            '*': '{c$actor{n stops leading $charlist.'
        }
    })
    allow_follow = {}
    followers = []

    @hook('after_moved')
    def __after_moved(self, moved_from, moved_to, by=None, via=None):
        from mudslingcore.rooms import Exit
        if self.db.is_valid(via, Exit):
            self._beckon_followers(via)

    def _beckon_followers(self, exit):
        self._prune_followers(ignore_location=True)
        for follower in self.followers:
            follower.do_follow(exit)

    def _prune_allow_follow(self):
        if 'allow_follow' not in self.__dict__:
            self.allow_follow = {}
        remove = []
        now = time_utils.unixtime()
        for char, timestamp in self.allow_follow.iteritems():
            if now - timestamp > 120:
                remove.append(char)
        for char in remove:
            del self.allow_follow[char]
        return remove

    def _prune_followers(self, ignore_location=False):
        if 'followers' not in self.__dict__:
            self.followers = []
        prune = set()
        for f in self.followers:
            if not f.isa(Follower):
                prune.add(f)
            if not ignore_location and f.location != self.location:
                prune.add(f)
        for f in prune:
            self._terminate_follower(f)
        return prune

    def allows_following_by(self, char):
        self._prune_allow_follow()
        return char in self.allow_follow

    def follow_invite(self, charlist):
        self._prune_allow_follow()
        newly_allowed = []
        for char in charlist:
            if char not in self.allow_follow:
                newly_allowed.append(char)
                self.allow_follow[char] = time_utils.unixtime()
        if newly_allowed:
            names = RenderList(newly_allowed, format='{g%s{n')
            self.emit_message('follow_invite', actor=self.ref(), charlist=names)
        return newly_allowed

    def follow_uninvite(self, charlist):
        self._prune_allow_follow()
        uninvite = [c for c in charlist
                    if c in self.allow_follow or c in self.followers]
        if uninvite:
            names = RenderList(uninvite, format='{g%s{n')
            self.emit_message('follow_uninvite', actor=self.ref(),
                              charlist=names)
        for char in uninvite:
            if char in self.allow_follow:
                del self.allow_follow[char]
            if char in self.followers:
                self._terminate_follower(char)
        return uninvite

    def gain_follower(self, char):
        self._prune_followers()
        if char not in self.followers:
            self.followers.append(char)

    def lose_follower(self, char):
        self._prune_followers()
        if char in self.followers:
            self.followers.remove(char)

    def _terminate_follower(self, follower):
        """Make something stop following this from this side"""
        if follower.isa(Follower):
            follower.stop_following(self.ref())
        else:
            self.lose_follower(follower)
Пример #9
0
class Exit(areas.AreaExportableBaseObject):
    """
    Core exit class.

    Transisions an object between two rooms.

    :ivar source: The room where this exit exists.
    :ivar dest: The room to which this exit leads.
    """

    #: :type: Room
    source = None
    #: :type: Room
    dest = None
    _exit_cmd = None

    messages = Messages({
        'leave': {
            'actor': None,
            '*': "$actor leaves for $exit.",
        },
        'leave_failed': {
            'actor': "You can't go that way."
        },
        'arrive': {  # Shown in destination when transiting this exit.
            'actor': None,
            '*': "$actor has arrived."
        },
    })

    def area_export(self, sandbox):
        export = super(Exit, self).area_export(sandbox)
        if self.game.db.is_valid(self.source, areas.AreaExportableBaseObject):
            export['source'] = areas.export_weak_ref(self.source)
        if self.game.db.is_valid(self.dest, areas.AreaExportableBaseObject):
            export['dest'] = areas.export_weak_ref(self.dest)
        return export

    def area_import(self, data, sandbox):
        super(Exit, self).area_import(data, sandbox)
        if 'source' in data:
            areas.import_weak_ref(self.ref(), 'source', data['source'],
                                  sandbox)
        if 'dest' in data:
            areas.import_weak_ref(self.ref(), 'dest', data['dest'], sandbox)

    def emit_message(self, key, exclude=None, **keywords):
        """
        Emit a messate template to the exit's room.

        Will try to emit the same message key with the suffix '_other_side' on
        the exit's counterpart on the other side.

        :param key: The message key to emit.
        :param exclude: Any objects to exclude from receipt of the message.
        :param keywords: Keywords for use in the template.

        :return: List of those objects that received the message.
        :rtype: list
        """
        suffix = '_other_side'
        original = not key.endswith(suffix)
        room = self.source
        keywords['this'] = self.ref()
        keywords['room'] = room
        if original:
            keywords['origin_exit'] = keywords['this']
            keywords['origin_room'] = room
        msg = self.get_message(key, **keywords)
        receivers = room.msg_contents(msg, exclude=exclude)
        counterpart = self.counterpart
        if original and counterpart is not None:
            receivers.extend(
                self.counterpart.emit_message(key + suffix,
                                              exclude=exclude,
                                              **keywords))
        return receivers

    @property
    def counterpart(self):
        """
        Find an exit's counterpart in its destination.
        :return: The corresponding exit back to this exit's source from this
            exit's destination.
        :rtype: Exit or None
        """
        v = self.game.db.is_valid
        if v(self.source, Room) and v(self.dest, Room):
            counterparts = self.dest.exits_to(self.source)
            if len(counterparts) > 0:
                return counterparts[0]
        return None

    def invoke(self, obj):
        """
        Check if obj may pass through this exit, and if so, pass obj through
        this exit.

        :param obj: The object attempting to pass through the exit.
        :type obj: mudsling.objects.Object
        """
        if self.transit_allowed(obj):
            self._move(obj)
        else:
            obj.emit(
                self.get_message('leave_failed',
                                 actor=obj,
                                 exit=self.ref(),
                                 dest=self.dest,
                                 room=self.source,
                                 source=self.source))

    def _invalid_enter_allowed(self, what, exit=None):
        """
        This is used as the enter_allowed call in the event that the
        destination is not a valid Room.
        """
        what.msg("You cannot walk through an exit to nowhere!")
        return False

    def transit_allowed(self, obj):
        """
        Returns True if obj has been allowed to continue on an actual attempt
        to transit the exit. This is not a simple permission check, it is a
        permission check AND resulting in-game effects of the actual attempt to
        transit the exit.

        :see: Room.leave_allowed and Room.enter_allowed

        :param obj: The object under consideration.
        :type obj: mudsling.objects.Object
        """
        # Acquire references to the transition attempt result methods from the
        # involved rooms, else placeholder functions.
        leave_allowed = (obj.location.leave_allowed
                         if obj.has_location and obj.location.is_valid(Room)
                         else lambda w, e: True)
        enter_allowed = (self.dest.enter_allowed
                         if self.dest is not None and self.dest.is_valid(Room)
                         else self._invalid_enter_allowed)
        me = self.ref()
        return leave_allowed(obj, me) and enter_allowed(obj, me)

    def _move(self, obj):
        """
        Pass the object through this exit to its destination.
        No permission checks -- just do it!

        :param obj: The object to pass through the exit.
        :type obj: mudsling.objects.Object
        """
        msg_keys = {
            'actor': obj.ref(),
            'exit': self.ref(),
            'dest': self.dest,
            'room': self.source,
            'source': self.source,
        }
        obj.emit(self.get_message('leave', **msg_keys))
        obj.move_to(self.dest, via=self.ref())
        if obj.has_location and obj.location == self.dest:
            obj.emit(self.get_message('arrive', **msg_keys))

    def exit_list_name(self):
        """
        Return the string to show when the exit is listed.
        """
        aliases = self.aliases
        return "%s {c<{n%s{c>{n" % (self.name,
                                    aliases[0] if aliases else self.name)

    def _call_counterpart(self, fname, *a, **kw):
        c = self.counterpart
        if c is not None:
            return getattr(c, fname)(*a, **kw)