Exemplo n.º 1
0
 def test_disable_stale_removal(self):
     # manual removal of stale tasks.
     timedelay = self.timedelay
     _TASK_HANDLER.stale_timeout = 0
     for pers in (False, True):
         t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
         t.cancel()
         self.assertFalse(t.active())
         _TASK_HANDLER.clock.advance(timedelay)  # make time pass
         if pers:
             self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
         self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
         # Make task handler's now time, after the stale timeout
         _TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + timedelay + 1)
         t2 = utils.delay(timedelay, dummy_func, self.char1.dbref)
         if pers:
             self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
         self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
         self.assertEqual(self.char1.ndb.dummy_var, False)
         # manual removal should still work
         _TASK_HANDLER.clean_stale_tasks()  # cleanup of stale tasks in in the save method
         if pers:
             self.assertFalse(t.get_id() in _TASK_HANDLER.to_save)
         self.assertFalse(t.get_id() in _TASK_HANDLER.tasks)
         _TASK_HANDLER.clear()
Exemplo n.º 2
0
 def test_server_restart(self):
     # emulate a server restart
     timedelay = self.timedelay
     utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=True)
     _TASK_HANDLER.clear(False)  # remove all tasks from task handler, do not save this change.
     _TASK_HANDLER.clock.advance(timedelay)  # advance twisted reactor time past callback time
     self.assertEqual(self.char1.ndb.dummy_var, False)  # task has not run
     _TASK_HANDLER.load()  # load persistent tasks from database.
     _TASK_HANDLER.create_delays()  # create new deffered instances from persistent tasks
     _TASK_HANDLER.clock.advance(timedelay)  # Clock must advance to trigger, even if past timedelay
     self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
Exemplo n.º 3
0
def _progressive_cmd_run(cmd, generator, response=None):
    """
    Progressively call the command that was given in argument. Used
    when `yield` is present in the Command's `func()` method.

    Args:
        cmd (Command): the command itself.
        generator (GeneratorType): the generator describing the processing.
        reponse (str, optional): the response to send to the generator.

    Raises:
        ValueError: If the func call yields something not identifiable as a
            time-delay or a string prompt.

    Note:
        This function is responsible for executing the command, if
        the func() method contains 'yield' instructions.  The yielded
        value will be accessible at each step and will affect the
        process.  If the value is a number, just delay the execution
        of the command.  If it's a string, wait for the user input.

    """
    global _GET_INPUT
    if not _GET_INPUT:
        from evennia.utils.evmenu import get_input as _GET_INPUT

    try:
        if response is None:
            value = next(generator)
        else:
            value = generator.send(response)
    except StopIteration:
        # duplicated from cmdhandler._run_command, to have these
        # run in the right order while staying inside the deferred
        cmd.at_post_cmd()
        if cmd.save_for_next:
            # store a reference to this command, possibly
            # accessible by the next command.
            cmd.caller.ndb.last_cmd = copy(cmd)
        else:
            cmd.caller.ndb.last_cmd = None
    else:
        if isinstance(value, (int, float)):
            utils.delay(value, _progressive_cmd_run, cmd, generator)
        elif isinstance(value, str):
            _GET_INPUT(cmd.caller,
                       value,
                       _process_input,
                       cmd=cmd,
                       generator=generator)
        else:
            raise ValueError(
                "unknown type for a yielded value in command: {}".format(
                    type(value)))
Exemplo n.º 4
0
    def at_start(self):
        """Set up the event system when starting.

        Note that this hook is called every time the server restarts
        (including when it's reloaded).  This hook performs the following
        tasks:

        -   Create temporarily stored events.
        -   Generate locals (individual events' namespace).
        -   Load eventfuncs, including user-defined ones.
        -   Re-schedule tasks that aren't set to fire anymore.
        -   Effectively connect the handler to the main script.

        """
        self.ndb.events = {}
        for typeclass, name, variables, help_text, custom_call, custom_add in EVENTS:
            self.add_event(typeclass, name, variables, help_text, custom_call,
                           custom_add)

        # Generate locals
        self.ndb.current_locals = {}
        self.ndb.fresh_locals = {}
        addresses = ["evennia.contrib.ingame_python.eventfuncs"]
        addresses.extend(
            getattr(settings, "EVENTFUNCS_LOCATIONS", ["world.eventfuncs"]))
        for address in addresses:
            if pypath_to_realpath(address):
                self.ndb.fresh_locals.update(all_from_module(address))

        # Restart the delayed tasks
        now = datetime.now()
        for task_id, definition in tuple(self.db.tasks.items()):
            future, obj, event_name, locals = definition
            seconds = (future - now).total_seconds()
            if seconds < 0:
                seconds = 0

            delay(seconds, complete_task, task_id)

        # Place the script in the CallbackHandler
        from evennia.contrib.ingame_python import typeclasses

        CallbackHandler.script = self
        DefaultObject.callbacks = typeclasses.EventObject.callbacks

        # Create the channel if non-existent
        try:
            self.ndb.channel = ChannelDB.objects.get(db_key="everror")
        except ChannelDB.DoesNotExist:
            self.ndb.channel = create_channel(
                "everror",
                desc="Event errors",
                locks="control:false();listen:perm(Builders);send:false()",
            )
Exemplo n.º 5
0
 def __init__(self, delay_in_seconds, callback=None, retval=None):
     """
     Arguments:
     delay_in_seconds (int or float) - delay until callback is fired
     callback (func) - callback to call, if any, after delay elapses
     retval (any, or None) - any arguments to return or input to
                             to callback
     """
     if retval:
         self.deferred = delay(delay_in_seconds, callback, retval)
     else:
         self.deferred = delay(delay_in_seconds, callback)
Exemplo n.º 6
0
def _do_flee(st_remaining, character, _, args):
    """Implements the 'flee' combat command."""
    ch = character.ndb.combat_handler
    # fleeing takes two subturns
    if st_remaining > 0:
        # first subturn: messaging only
        ch.combat_msg(
            "{actor} looks about frantically for an escape route.",
            actor=character)
    else:
        # second subturn: skill check and resolution
        min_range = ch.get_min_range(character)

        if min_range == 'melee':
            # very difficult
            target_num = 9
        elif min_range == 'reach':
            # moderately difficult
            target_num = 6
        else:  # min_range == 'ranged'
            # easiest
            target_num = 4

        ok = skill_check(character.skills.escape.actual, target_num)
        if ok:
            # successfully escaped
            ch.combat_msg(
                "{actor} seizes the opportunity to escape the fight!",
                actor=character)
            ch.remove_character(character)

            character.msg("{actor} knows they are safe, for a time...".format(
                actor=character.get_display_name(character)))

            # prevent re-attack for a time
            character.ndb.no_attack = True
            safe_time = 2 * (character.skills.escape + d_roll('1d6'))

            def enable_attack():
                del character.ndb.no_attack
                character.msg("{actor} feels somehow more vulnerable than just a moment ago.".format(
                    actor=character.get_display_name(character)))

            utils.delay(safe_time, enable_attack)
        else:
            # failed to escape
            ch.combat_msg(
                "{actor} tries to escape, but is boxed in.",
                actor=character)

    return 1 * COMBAT_DELAY
Exemplo n.º 7
0
def _do_flee(st_remaining, character, _, args):
    """Implements the 'flee' combat command."""
    ch = character.ndb.combat_handler
    # fleeing takes two subturns
    if st_remaining > 0:
        # first subturn: messaging only
        ch.combat_msg("{actor} looks about frantically for an escape route.",
                      actor=character)
    else:
        # second subturn: skill check and resolution
        min_range = ch.get_min_range(character)

        if min_range == 'melee':
            # very difficult
            target_num = 9
        elif min_range == 'reach':
            # moderately difficult
            target_num = 6
        else:  # min_range == 'ranged'
            # easiest
            target_num = 4

        ok = skill_check(character.skills.escape.actual, target_num)
        if ok:
            # successfully escaped
            ch.combat_msg(
                "{actor} seizes the opportunity to escape the fight!",
                actor=character)
            ch.remove_character(character)

            character.msg("{actor} knows they are safe, for a time...".format(
                actor=character.get_display_name(character)))

            # prevent re-attack for a time
            character.ndb.no_attack = True
            safe_time = 2 * (character.skills.escape + d_roll('1d6'))

            def enable_attack():
                del character.ndb.no_attack
                character.msg(
                    "{actor} feels somehow more vulnerable than just a moment ago."
                    .format(actor=character.get_display_name(character)))

            utils.delay(safe_time, enable_attack)
        else:
            # failed to escape
            ch.combat_msg("{actor} tries to escape, but is boxed in.",
                          actor=character)

    return 1 * COMBAT_DELAY
Exemplo n.º 8
0
    def connectionMade(self):
        """
        This is called when the connection is first established.

        """
        # initialize the session
        self.line_buffer = ""
        client_address = self.transport.client
        client_address = client_address[0] if client_address else None
        # this number is counted down for every handshake that completes.
        # when it reaches 0 the portal/server syncs their data
        self.handshakes = 8  # suppress-go-ahead, naws, ttype, mccp, mssp, msdp, gmcp, mxp

        self.init_session(self.protocol_key, client_address,
                          self.factory.sessionhandler)
        self.protocol_flags["ENCODING"] = settings.ENCODINGS[
            0] if settings.ENCODINGS else 'utf-8'
        # add this new connection to sessionhandler so
        # the Server becomes aware of it.
        self.sessionhandler.connect(self)
        # change encoding to ENCODINGS[0] which reflects Telnet default encoding

        # suppress go-ahead
        self.sga = suppress_ga.SuppressGA(self)
        # negotiate client size
        self.naws = naws.Naws(self)
        # negotiate ttype (client info)
        # Obs: mudlet ttype does not seem to work if we start mccp before ttype. /Griatch
        self.ttype = ttype.Ttype(self)
        # negotiate mccp (data compression) - turn this off for wireshark analysis
        self.mccp = Mccp(self)
        # negotiate mssp (crawler communication)
        self.mssp = mssp.Mssp(self)
        # oob communication (MSDP, GMCP) - two handshake calls!
        self.oob = telnet_oob.TelnetOOB(self)
        # mxp support
        self.mxp = Mxp(self)

        from evennia.utils.utils import delay
        # timeout the handshakes in case the client doesn't reply at all
        delay(2, callback=self.handshake_done, timeout=True)

        # TCP/IP keepalive watches for dead links
        self.transport.setTcpKeepAlive(1)
        # The TCP/IP keepalive is not enough for some networks;
        # we have to complement it with a NOP keep-alive.
        self.protocol_flags["NOPKEEPALIVE"] = True
        self.nop_keep_alive = None
        self.toggle_nop_keepalive()
Exemplo n.º 9
0
    def at_start(self):
        """Set up the event system when starting.

        Note that this hook is called every time the server restarts
        (including when it's reloaded).  This hook performs the following
        tasks:

        -   Create temporarily stored events.
        -   Generate locals (individual events' namespace).
        -   Load eventfuncs, including user-defined ones.
        -   Re-schedule tasks that aren't set to fire anymore.
        -   Effectively connect the handler to the main script.

        """
        self.ndb.events = {}
        for typeclass, name, variables, help_text, custom_call, custom_add in EVENTS:
            self.add_event(typeclass, name, variables, help_text, custom_call, custom_add)

        # Generate locals
        self.ndb.current_locals = {}
        self.ndb.fresh_locals = {}
        addresses = ["evennia.contrib.ingame_python.eventfuncs"]
        addresses.extend(getattr(settings, "EVENTFUNCS_LOCATIONS", ["world.eventfuncs"]))
        for address in addresses:
            if pypath_to_realpath(address):
                self.ndb.fresh_locals.update(all_from_module(address))

        # Restart the delayed tasks
        now = datetime.now()
        for task_id, definition in tuple(self.db.tasks.items()):
            future, obj, event_name, locals = definition
            seconds = (future - now).total_seconds()
            if seconds < 0:
                seconds = 0

            delay(seconds, complete_task, task_id)

        # Place the script in the CallbackHandler
        from evennia.contrib.ingame_python import typeclasses
        CallbackHandler.script = self
        DefaultObject.callbacks = typeclasses.EventObject.callbacks

        # Create the channel if non-existent
        try:
            self.ndb.channel = ChannelDB.objects.get(db_key="everror")
        except ChannelDB.DoesNotExist:
            self.ndb.channel = create_channel("everror", desc="Event errors",
                    locks="control:false();listen:perm(Builders);send:false()")
Exemplo n.º 10
0
    def connectionMade(self):
        """
        This is called when the connection is first established.

        """
        # initialize the session
        self.line_buffer = ""
        client_address = self.transport.client
        client_address = client_address[0] if client_address else None
        # this number is counted down for every handshake that completes.
        # when it reaches 0 the portal/server syncs their data
        self.handshakes = 8  # suppress-go-ahead, naws, ttype, mccp, mssp, msdp, gmcp, mxp

        self.init_session(self.protocol_key, client_address, self.factory.sessionhandler)
        self.protocol_flags["ENCODING"] = settings.ENCODINGS[0] if settings.ENCODINGS else 'utf-8'
        # add this new connection to sessionhandler so
        # the Server becomes aware of it.
        self.sessionhandler.connect(self)
        # change encoding to ENCODINGS[0] which reflects Telnet default encoding

        # suppress go-ahead
        self.sga = suppress_ga.SuppressGA(self)
        # negotiate client size
        self.naws = naws.Naws(self)
        # negotiate ttype (client info)
        # Obs: mudlet ttype does not seem to work if we start mccp before ttype. /Griatch
        self.ttype = ttype.Ttype(self)
        # negotiate mccp (data compression) - turn this off for wireshark analysis
        self.mccp = Mccp(self)
        # negotiate mssp (crawler communication)
        self.mssp = mssp.Mssp(self)
        # oob communication (MSDP, GMCP) - two handshake calls!
        self.oob = telnet_oob.TelnetOOB(self)
        # mxp support
        self.mxp = Mxp(self)

        from evennia.utils.utils import delay
        # timeout the handshakes in case the client doesn't reply at all
        delay(2, callback=self.handshake_done, timeout=True)

        # TCP/IP keepalive watches for dead links
        self.transport.setTcpKeepAlive(1)
        # The TCP/IP keepalive is not enough for some networks;
        # we have to complement it with a NOP keep-alive.
        self.protocol_flags["NOPKEEPALIVE"] = True
        self.nop_keep_alive = None
        self.toggle_nop_keepalive()
Exemplo n.º 11
0
    def at_say(self, speaker, message):
        """
        Called on this object if an object inside this object speaks.
        The string returned from this method is the final form of the
        speech.

        Args:
            speaker (Object): The object speaking.
            message (str): The words spoken.

        Returns:
            The message to be said (str) or None.

        Notes:
            You should not need to add things like 'you say: ' or
            similar here, that should be handled by the say command before
            this.

        """
        allow = self.callbacks.call("can_say", speaker, self, message,
                parameters=message)
        if not allow:
            return

        message = self.callbacks.get_variable("message")

        # Call the event "can_say" of other characters in the location
        for present in [o for o in self.contents if isinstance(
                o, DefaultCharacter) and o is not speaker]:
            allow = present.callbacks.call("can_say", speaker, present,
                    message, parameters=message)
            if not allow:
                return

            message = present.callbacks.get_variable("message")

        # We force the next event to be called after the message
        # This will have to change when the Evennia API adds new hooks
        delay(0, self.callbacks.call, "say", speaker, self, message,
                parameters=message)
        for present in [o for o in self.contents if isinstance(
                o, DefaultCharacter) and o is not speaker]:
            delay(0, present.callbacks.call, "say", speaker, present, message,
                    parameters=message)

        return message
Exemplo n.º 12
0
 def test_active(self):
     timedelay = self.timedelay
     t = utils.delay(timedelay, dummy_func, self.char1.dbref)
     self.assertTrue(_TASK_HANDLER.active(t.get_id()))
     self.assertTrue(t.active())
     _TASK_HANDLER.clock.advance(timedelay)  # make time pass
     self.assertFalse(_TASK_HANDLER.active(t.get_id()))
     self.assertFalse(t.active())
Exemplo n.º 13
0
 def test_short_deferred_call(self):
     # wait for deferred to call with a very short time
     timedelay = .1
     for pers in (False, True):
         t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
         self.assertTrue(t.active())
         _TASK_HANDLER.clock.advance(timedelay)  # make time pass
         self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
         self.assertFalse(t.exists())
         self.char1.ndb.dummy_var = False
Exemplo n.º 14
0
 def test_do_task(self):
     # call the task early with do_task
     for pers in (True, False):
         t = utils.delay(self.timedelay, dummy_func, self.char1.dbref, persistent=pers)
         # call the task early to test Task.call and TaskHandler.call_task
         result = t.do_task()
         self.assertTrue(result)
         self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
         self.assertFalse(t.exists())
         self.char1.ndb.dummy_var = False
Exemplo n.º 15
0
 def test_call_early(self):
     # call a task early with call
     for pers in (True, False):
         t = utils.delay(self.timedelay, dummy_func, self.char1.dbref, persistent=pers)
         result = t.call()
         self.assertTrue(result)
         self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
         self.assertTrue(t.exists())
         self.assertTrue(t.active())
         self.char1.ndb.dummy_var = False
Exemplo n.º 16
0
    def connectionMade(self):
        """
        This is called when the connection is first established.
        """
        # initialize the session
        self.iaw_mode = False
        self.no_lb_mode = False
        client_address = self.transport.client
        # this number is counted down for every handshake that completes.
        # when it reaches 0 the portal/server syncs their data
        self.handshakes = 7  # naws, ttype, mccp, mssp, msdp, gmcp, mxp
        self.init_session("telnet", client_address,
                          self.factory.sessionhandler)

        # negotiate client size
        self.naws = naws.Naws(self)
        # negotiate ttype (client info)
        # Obs: mudlet ttype does not seem to work if we start mccp before ttype. /Griatch
        self.ttype = ttype.Ttype(self)
        # negotiate mccp (data compression) - turn this off for wireshark analysis
        self.mccp = Mccp(self)
        # negotiate mssp (crawler communication)
        self.mssp = mssp.Mssp(self)
        # oob communication (MSDP, GMCP) - two handshake calls!
        self.oob = telnet_oob.TelnetOOB(self)
        # mxp support
        self.mxp = Mxp(self)
        # keepalive watches for dead links
        self.transport.setTcpKeepAlive(1)

        self.transport.setTcpNoDelay(True)

        # add this new connection to sessionhandler so
        # the Server becomes aware of it.
        self.sessionhandler.connect(self)

        # timeout the handshakes in case the client doesn't reply at all
        from evennia.utils.utils import delay
        delay(2, callback=self.handshake_done, retval=True)

        # set up a keep-alive
        self.keep_alive = LoopingCall(self._write, IAC + NOP)
        self.keep_alive.start(30, now=False)
Exemplo n.º 17
0
    def set_task(self, seconds, obj, callback_name):
        """
        Set and schedule a task to run.

        Args:
            seconds (int, float): the delay in seconds from now.
            obj (Object): the typecalssed object connected to the event.
            callback_name (str): the callback's name.

        Notes:
            This method allows to schedule a "persistent" task.
            'utils.delay' is called, but a copy of the task is kept in
            the event handler, and when the script restarts (after reload),
            the differed delay is called again.
            The dictionary of locals is frozen and will be available
            again when the task runs.  This feature, however, is limited
            by the database: all data cannot be saved.  Lambda functions,
            class methods, objects inside an instance and so on will
            not be kept in the locals dictionary.

        """
        now = datetime.now()
        delta = timedelta(seconds=seconds)

        # Choose a free task_id
        used_ids = list(self.db.tasks.keys())
        task_id = 1
        while task_id in used_ids:
            task_id += 1

        # Collect and freeze current locals
        locals = {}
        for key, value in self.ndb.current_locals.items():
            try:
                dbserialize(value)
            except TypeError:
                continue
            else:
                locals[key] = value

        self.db.tasks[task_id] = (now + delta, obj, callback_name, locals)
        delay(seconds, complete_task, task_id)
Exemplo n.º 18
0
    def set_task(self, seconds, obj, callback_name):
        """
        Set and schedule a task to run.

        Args:
            seconds (int, float): the delay in seconds from now.
            obj (Object): the typecalssed object connected to the event.
            callback_name (str): the callback's name.

        Notes:
            This method allows to schedule a "persistent" task.
            'utils.delay' is called, but a copy of the task is kept in
            the event handler, and when the script restarts (after reload),
            the differed delay is called again.
            The dictionary of locals is frozen and will be available
            again when the task runs.  This feature, however, is limited
            by the database: all data cannot be saved.  Lambda functions,
            class methods, objects inside an instance and so on will
            not be kept in the locals dictionary.

        """
        now = datetime.now()
        delta = timedelta(seconds=seconds)

        # Choose a free task_id
        used_ids = list(self.db.tasks.keys())
        task_id = 1
        while task_id in used_ids:
            task_id += 1

        # Collect and freeze current locals
        locals = {}
        for key, value in self.ndb.current_locals.items():
            try:
                dbserialize(value)
            except TypeError:
                continue
            else:
                locals[key] = value

        self.db.tasks[task_id] = (now + delta, obj, callback_name, locals)
        delay(seconds, complete_task, task_id)
Exemplo n.º 19
0
    def connectionMade(self):
        """
        This is called when the connection is first established.
        """
        # initialize the session
        self.iaw_mode = False
        self.no_lb_mode = False
        client_address = self.transport.client
        # this number is counted down for every handshake that completes.
        # when it reaches 0 the portal/server syncs their data
        self.handshakes = 7 # naws, ttype, mccp, mssp, msdp, gmcp, mxp
        self.init_session("telnet", client_address, self.factory.sessionhandler)

        # negotiate client size
        self.naws = naws.Naws(self)
        # negotiate ttype (client info)
        # Obs: mudlet ttype does not seem to work if we start mccp before ttype. /Griatch
        self.ttype = ttype.Ttype(self)
        # negotiate mccp (data compression) - turn this off for wireshark analysis
        self.mccp = Mccp(self)
        # negotiate mssp (crawler communication)
        self.mssp = mssp.Mssp(self)
        # oob communication (MSDP, GMCP) - two handshake calls!
        self.oob = telnet_oob.TelnetOOB(self)
        # mxp support
        self.mxp = Mxp(self)
        # keepalive watches for dead links
        self.transport.setTcpKeepAlive(1)

        self.transport.setTcpNoDelay(True)

        # add this new connection to sessionhandler so
        # the Server becomes aware of it.
        self.sessionhandler.connect(self)

        # timeout the handshakes in case the client doesn't reply at all
        from evennia.utils.utils import delay
        delay(2, callback=self.handshake_done, retval=True)

        # set up a keep-alive
        self.keep_alive = LoopingCall(self._write, IAC + NOP)
        self.keep_alive.start(30, now=False)
Exemplo n.º 20
0
 def test_remove(self):
     timedelay = self.timedelay
     for pers in (False, True):
         t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
         self.assertTrue(t.active())
         success = t.remove()
         self.assertTrue(success)
         self.assertFalse(t.active())
         self.assertFalse(t.exists())
         _TASK_HANDLER.clock.advance(timedelay)  # make time pass
         self.assertEqual(self.char1.ndb.dummy_var, False)
Exemplo n.º 21
0
 def test_auto_stale_task_removal(self):
     # automated removal of stale tasks.
     timedelay = self.timedelay
     for pers in (False, True):
         t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
         t.cancel()
         self.assertFalse(t.active())
         _TASK_HANDLER.clock.advance(timedelay)  # make time pass
         if pers:
             self.assertTrue(t.get_id() in _TASK_HANDLER.to_save)
         self.assertTrue(t.get_id() in _TASK_HANDLER.tasks)
         # Make task handler's now time, after the stale timeout
         _TASK_HANDLER._now = datetime.now() + timedelta(seconds=_TASK_HANDLER.stale_timeout + timedelay + 1)
         # add a task to test automatic removal
         t2 = utils.delay(timedelay, dummy_func, self.char1.dbref)
         if pers:
             self.assertFalse(t.get_id() in _TASK_HANDLER.to_save)
         self.assertFalse(t.get_id() in _TASK_HANDLER.tasks)
         self.assertEqual(self.char1.ndb.dummy_var, False)
         _TASK_HANDLER.clear()
Exemplo n.º 22
0
    def portal_connect(self, portalsessiondata):
        """
        Called by Portal when a new session has connected.
        Creates a new, unlogged-in game session.

        Args:
            portalsessiondata (dict): a dictionary of all property:value
                keys defining the session and which is marked to be
                synced.

        """
        delayed_import()
        global _ServerSession, _AccountDB, _ScriptDB

        sess = _ServerSession()
        sess.sessionhandler = self
        sess.load_sync_data(portalsessiondata)
        sess.at_sync()
        # validate all scripts
        _ScriptDB.objects.validate()
        self[sess.sessid] = sess

        if sess.logged_in and sess.uid:
            # Session is already logged in. This can happen in the
            # case of auto-authenticating protocols like SSH or
            # webclient's session sharing
            account = _AccountDB.objects.get_account_from_uid(sess.uid)
            if account:
                # this will set account.is_connected too
                self.login(sess, account, force=True)
                return
            else:
                sess.logged_in = False
                sess.uid = None

        # show the first login command

        # this delay is necessary notably for Mudlet, which will fail on the connection screen
        # unless the MXP protocol has been negotiated. Unfortunately this may be too short for some
        # networks, the symptom is that < and > are not parsed by mudlet on first connection.
        delay(0.3, self._run_cmd_login, sess)
Exemplo n.º 23
0
def _progressive_cmd_run(cmd, generator, response=None):
    """
    Progressively call the command that was given in argument. Used
    when `yield` is present in the Command's `func()` method.

    Args:
        cmd (Command): the command itself.
        generator (GeneratorType): the generator describing the processing.
        reponse (str, optional): the response to send to the generator.

    Raises:
        ValueError: If the func call yields something not identifiable as a
            time-delay or a string prompt.

    Note:
        This function is responsible for executing the command, if
        the func() method contains 'yield' instructions.  The yielded
        value will be accessible at each step and will affect the
        process.  If the value is a number, just delay the execution
        of the command.  If it's a string, wait for the user input.

    """
    global _GET_INPUT
    if not _GET_INPUT:
        from evennia.utils.evmenu import get_input as _GET_INPUT

    try:
        if response is None:
            value = generator.next()
        else:
            value = generator.send(response)
    except StopIteration:
        pass
    else:
        if isinstance(value, (int, float)):
            utils.delay(value, _progressive_cmd_run, cmd, generator)
        elif isinstance(value, basestring):
            _GET_INPUT(cmd.caller, value, _process_input, cmd=cmd, generator=generator)
        else:
            raise ValueError("unknown type for a yielded value in command: {}".format(type(value)))
Exemplo n.º 24
0
    def portal_connect(self, portalsessiondata):
        """
        Called by Portal when a new session has connected.
        Creates a new, unlogged-in game session.

        Args:
            portalsessiondata (dict): a dictionary of all property:value
                keys defining the session and which is marked to be
                synced.

        """
        delayed_import()
        global _ServerSession, _AccountDB, _ScriptDB

        sess = _ServerSession()
        sess.sessionhandler = self
        sess.load_sync_data(portalsessiondata)
        sess.at_sync()
        # validate all scripts
        _ScriptDB.objects.validate()
        self[sess.sessid] = sess

        if sess.logged_in and sess.uid:
            # Session is already logged in. This can happen in the
            # case of auto-authenticating protocols like SSH or
            # webclient's session sharing
            account = _AccountDB.objects.get_account_from_uid(sess.uid)
            if account:
                # this will set account.is_connected too
                self.login(sess, account, force=True)
                return
            else:
                sess.logged_in = False
                sess.uid = None

        # show the first login command, may delay slightly to allow
        # the handshakes to finish.
        delay(_DELAY_CMD_LOGINSTART, self._run_cmd_login, sess)
Exemplo n.º 25
0
    def portal_connect(self, portalsessiondata):
        """
        Called by Portal when a new session has connected.
        Creates a new, unlogged-in game session.

        Args:
            portalsessiondata (dict): a dictionary of all property:value
                keys defining the session and which is marked to be
                synced.

        """
        delayed_import()
        global _ServerSession, _AccountDB, _ScriptDB

        sess = _ServerSession()
        sess.sessionhandler = self
        sess.load_sync_data(portalsessiondata)
        sess.at_sync()
        # validate all scripts
        _ScriptDB.objects.validate()
        self[sess.sessid] = sess

        if sess.logged_in and sess.uid:
            # Session is already logged in. This can happen in the
            # case of auto-authenticating protocols like SSH or
            # webclient's session sharing
            account = _AccountDB.objects.get_account_from_uid(sess.uid)
            if account:
                # this will set account.is_connected too
                self.login(sess, account, force=True)
                return
            else:
                sess.logged_in = False
                sess.uid = None

        # show the first login command, may delay slightly to allow
        # the handshakes to finish.
        delay(_DELAY_CMD_LOGINSTART, self._run_cmd_login, sess)
Exemplo n.º 26
0
 def test_pause_unpause(self):
     # remove a canceled task
     timedelay = self.timedelay
     for pers in (False, True):
         t = utils.delay(timedelay, dummy_func, self.char1.dbref, persistent=pers)
         self.assertTrue(t.active())
         t.pause()
         self.assertTrue(t.paused)
         t.unpause()
         self.assertFalse(t.paused)
         self.assertEqual(self.char1.ndb.dummy_var, False)
         t.pause()
         _TASK_HANDLER.clock.advance(timedelay)  # make time pass
         self.assertEqual(self.char1.ndb.dummy_var, False)
         t.unpause()
         self.assertEqual(self.char1.ndb.dummy_var, 'dummy_func ran')
         self.char1.ndb.dummy_var = False
Exemplo n.º 27
0
    def at_say(self, speaker, message):
        """
        Called on this object if an object inside this object speaks.
        The string returned from this method is the final form of the
        speech.

        Args:
            speaker (Object): The object speaking.
            message (str): The words spoken.

        Returns:
            The message to be said (str) or None.

        Notes:
            You should not need to add things like 'you say: ' or
            similar here, that should be handled by the say command before
            this.

        """
        allow = self.callbacks.call("can_say",
                                    speaker,
                                    self,
                                    message,
                                    parameters=message)
        if not allow:
            return

        message = self.callbacks.get_variable("message")

        # Call the event "can_say" of other characters in the location
        for present in [
                o for o in self.contents
                if isinstance(o, DefaultCharacter) and o is not speaker
        ]:
            allow = present.callbacks.call("can_say",
                                           speaker,
                                           present,
                                           message,
                                           parameters=message)
            if not allow:
                return

            message = present.callbacks.get_variable("message")

        # We force the next event to be called after the message
        # This will have to change when the Evennia API adds new hooks
        delay(0,
              self.callbacks.call,
              "say",
              speaker,
              self,
              message,
              parameters=message)
        for present in [
                o for o in self.contents
                if isinstance(o, DefaultCharacter) and o is not speaker
        ]:
            delay(0,
                  present.callbacks.call,
                  "say",
                  speaker,
                  present,
                  message,
                  parameters=message)

        return message
Exemplo n.º 28
0
def process_next_action(combat_handler):
    """
    Callback that handles processing combat actions

    Args:
        combat_handler: instance of a combat handler

    Returns:
        None

    Based on the combat handler's data, this callback
    selects an appropriate `_do_*` function to execute
    the combat action. These handlers are all called
    with the signature:

        `_do_action(subturns_remaining, character, target, args)`

    Each `_do_*` function should return a time delay
    in seconds before the next call to `process_next_action`
    should be run.
    """
    turn_actions = combat_handler.db.turn_actions
    turn_order = combat_handler.db.turn_order
    actor_idx = combat_handler.db.actor_idx
    delay = 0

    if actor_idx >= len(turn_order):
        # finished a subturn
        # reset counters and see who is dodging during the next action
        combat_handler.ndb.actions_taken = defaultdict(int)
        combat_handler.db.actor_idx = actor_idx = 0
        for dbref, char in combat_handler.db.characters.items():
            if char.nattributes.has('dodging'):
                del char.ndb.dodging
            action_count = 0

            # set the dodging nattribute on any characters
            # with 'dodge' as their action
            for action, _, _, duration in combat_handler.db.turn_actions[dbref]:
                if action == 'dodge':
                    char.ndb.dodging = True
                    break
                action_count += duration
                if action_count >= 1:
                    break

        # and increment the subturn
        combat_handler.db.subturn += 1

    if combat_handler.db.subturn > ACTIONS_PER_TURN:
        # turn is over; notify the handler to start the next
        combat_handler.begin_turn()
        return

    current_charid = turn_order[actor_idx]
    if combat_handler.ndb.actions_taken[current_charid] < 1:
        action, character, target, duration = \
            turn_actions[current_charid].popleft()

        combat_handler.ndb.actions_taken[current_charid] += duration

        args = action.split('/')[1:]

        # we decrement the duration for this turn
        duration -= 1
        if duration > 0:
            # action takes more than one subturn; return it to the queue
            turn_actions[current_charid].append(
                (action,
                 character,
                 target,
                 duration)
            )
            combat_handler.db.actor_idx += 1

        action_func = '_do_{}'.format(action.split('/')[0])

        # action_func receives the number of subturns remaining in the action
        delay = globals()[action_func](duration, character, target, args)
    else:
        combat_handler.db.actor_idx += 1

    if len(combat_handler.db.characters) < 2:
        combat_handler.stop()
    else:
        utils.delay(delay, process_next_action, combat_handler)
Exemplo n.º 29
0
    def func(self):
        """Implement function using the Msg methods"""
        char = self.character
        sent_messages = Msg.objects.get_messages_by_sender(char, exclude_channel_messages=True)
        recd_messages = Msg.objects.get_messages_by_receiver(char)
        if 'last' in self.switches:
            self.mail_check()
            if sent_messages:
                recv = ', '.join('%s%s|n' % (obj.STYLE, obj.key) for obj in sent_messages[-1].receivers)
                self.msg("You last mailed |w%s|n: |w%s" % (recv, sent_messages[-1].message))
            else:
                self.msg("You haven't mailed anyone yet.")
            self.mail_check()
            return
        if 'check' in self.switches:
            if not self.mail_check():
                if not ('silent' in self.switches and 'quiet' in self.switches):
                    self.msg('Your %s mailbox has no new mail.' % char.location.get_display_name(self.character))
        if not self.args or not self.rhs:
            mail = sent_messages + recd_messages
            mail.sort(lambda x, y: cmp(x.db_date_created, y.db_date_created))
            number = 5
            if self.args:
                try:
                    number = int(self.args)
                except ValueError:
                    self.msg("Usage: mail [<character> = msg]")
                    return
            if len(mail) > number:
                mail_last = mail[-number:]
            else:
                mail_last = mail
            template = "|w%s|n |w%s|n to |w%s|n: %s"
            mail_last = "\n ".join(template %
                                   (utils.datetime_format(mail.date_created),
                                    ', '.join('%s' % obj.get_display_name(self.character) for obj in mail.senders),
                                    ', '.join(['%s' % obj.get_display_name(self.character) for obj in mail.receivers]),
                                    mail.message) for mail in mail_last)
            if mail_last:
                string = "Your latest letters:\n %s" % mail_last
            else:
                string = "You haven't mailed anyone yet."
            self.msg(string)
            char.nattributes.remove('new_mail')  # Removes the notice.
            return
        # Send mode
        if not self.lhs:
            if sent_messages:  # If no recipients provided,
                receivers = sent_messages[-1].receivers  # default to sending to the last character mailed.
            else:
                self.msg("Who do you want to mail?")
                return
        else:  # Build a list of comma-delimited recipients.
            receivers = self.lhslist
        rec_objs = []
        received = []
        r_strings = []
        for receiver in set(receivers):
            if isinstance(receiver, basestring):
                c_obj = char.search(receiver, global_search=True, exact=True)
            elif hasattr(receiver, 'location'):
                c_obj = receiver
            else:
                self.msg("Who do you want to mail?")
                return
            if c_obj:
                if not c_obj.access(char, 'mail'):
                    r_strings.append("You are not able to mail %s." % c_obj)
                    continue
                rec_objs.append(c_obj)
        if not rec_objs:
            self.msg("No one found to mail.")
            return
        message = self.rhs.strip()
        if message.startswith(':'):  # Format as pose if message begins with a :
            message = "%s%s|n %s" % (char.STYLE, char.key, message.strip(':'))
        create.create_message(char, message, receivers=rec_objs)

        def letter_delivery():
            # c_obj.msg('%s %s' % (header, message))
            c_obj.msg('|/A letter has arrived in %s%s|n mailbox for you.|/' % (c_obj.home.STYLE, c_obj.home.key))

        for c_obj in rec_objs:  # Notify character of mail delivery.
            received.append('%s%s|n' % (c_obj.STYLE, c_obj.key))
            if hasattr(c_obj, 'sessions') and not c_obj.sessions.count():
                r_strings.append("|r%s|n is currently asleep, and won't read the letter until later." % received[-1])
                c_obj.ndb.new_mail = True
            else:  # Tell the receiving characters about receiving a letter if they are online.
                utils.delay(20, callback=letter_delivery)
        if r_strings:
            self.msg("\n".join(r_strings))
        stamp_count = len(rec_objs)
        stamp_plural = 'a stamp' if stamp_count == 1 else '%i stamps' % stamp_count
        self.msg('Mail delivery costs %s.' % stamp_plural)
        char.location.msg_contents('|g%s|n places %s on an envelope and slips it into the %s%s|n mailbox.'
                                   % (char.key, stamp_plural, char.location.STYLE, char.location.key))
        self.msg("Your letter to %s will be delivered soon. You wrote: %s" % (', '.join(received), message))
        self.mail_check()
Exemplo n.º 30
0
def _do_strike(st_remaining, character, target, args):
    """Implements the 'strike' combat command."""
    ch = character.ndb.combat_handler

    if character.db.position != 'STANDING' and 'end' not in args:
        # if you are in any wrestling position other
        # than free standing, all you can do is wrestle to break free
        return _do_wrestle(character, target, ['break'])

    # confirm the target is still in combat,
    if target.id not in ch.db.characters:
        character.msg("{defender} has left combat.".format(
            defender=target.get_display_name(character)))
        return 0.2 * COMBAT_DELAY

    # is within range,
    attack_range = ch.get_range(character, target)
    if attack_range != 'melee':
        ch.combat_msg(
            "{actor} is too far away from {target} to strike them.",
            actor=character,
            target=target)
        return 0.2 * COMBAT_DELAY

    # and has at least one free hand
    strikes = sum(1 if character.equip.get(slot) is None else 0
                  for slot in character.db.slots
                  if slot.startswith('wield'))

    if strikes <= 0:
        ch.combat_msg(
            "{actor} goes to punch, but does not have a free hand.",
            actor=character)
        return 0.2 * COMBAT_DELAY

    # check whether the target is dodging
    dodging = target.nattributes.has('dodging')

    if dodging:
        # attacker must take the worse of two standard rolls
        atk_roll = min((std_roll(), std_roll()))
    else:
        atk_roll = std_roll()

    damage = (atk_roll + character.traits.ATKU) - target.traits.DEF
    if damage > 0:
        if dodging:
            ch.combat_msg(
                "{actor} tries to dodge, but",
                actor=target
            )

        if any((arg.startswith('s') for arg in args)):
            # 'subdue': deduct stamina instead of health
            if target.traits.SP >= damage:
                target.traits.SP.current -= damage
                status = 'dmg_sp'
            else:
                # if we don't have enough SP, damages HP instead
                damage = damage - target.traits.SP
                target.traits.SP.current = 0
                target.traits.HP.current -= damage
                status = 'dmg_hp'

        else:
            target.traits.HP.current -= damage
            status = 'dmg_hp'
    else:
        if dodging:
            status = 'dodged'
        else:
            status = 'missed'

    messages = {
        'dmg_hp': "{actor} strikes {target} savagely with their fist.",
        'dmg_sp': "{actor} strikes {target} hard in the chest, and {target} "
                   "staggers from the blow.",
        'dodged': "{target} dodges a punch from {actor}.",
        'missed': "{actor} attempts to punch {target} and misses."
    }

    ch.combat_msg(
        messages[status],
        actor=character,
        target=target)

    # handle Power Points
    if atk_roll > 0:
        # this attack has earned PPs
        character.traits.PP.current += atk_roll

    if character.traits.PP.actual > 0:
        # handle equipment abilities
        pass

    if target.traits.HP.actual <= 0:
        # target has died
        resolve_death(character, target, ch)

    if 'end' not in args:
        if strikes > 1:
            # we have two free hands; do a second strike
            args.append('end')
            utils.delay(0.5 * COMBAT_DELAY, _do_strike, 0, character, target, args)

        return 1 * COMBAT_DELAY
Exemplo n.º 31
0
 def test_called(self):
     timedelay = self.timedelay
     t = utils.delay(timedelay, dummy_func, self.char1.dbref)
     self.assertFalse(t.called)
     _TASK_HANDLER.clock.advance(timedelay)  # make time pass
     self.assertTrue(t.called)
Exemplo n.º 32
0
def password(caller, input):
    """Ask the user to enter the password to this account.

    This is assuming the user exists (see 'create_username' and
    'create_password').  This node "loops" if needed:  if the
    user specifies a wrong password, offers the user to try
    again or to go back by entering 'b'.
    If the password is correct, then login.

    """
    input = input.strip()
    text = ""
    options = ({
        "key": "_default",
        "desc": "Enter your password.",
        "goto": "password",
    }, )

    # Check the password
    account = caller.db._account

    # If the account is locked, the user has to wait (maximum
    # 3 seconds) before retrying
    if account.db._locked:
        text = "|gPlease wait, you cannot enter your password yet.|n"
        return text, options

    bans = ServerConfig.objects.conf("server_bans")
    banned = bans and (any(tup[0] == account.name.lower() for tup in bans) \
            or any(tup[2].match(caller.address) for tup in bans if tup[2]))

    if not account.check_password(input):
        text = dedent("""
            |rIncorrect password.|n
                Type |wb|n to go back to the login screen.
                Or wait 3 seconds before trying a new password.
        """.strip("\n"))

        # Loops on the same node, lock for 3 seconds
        account.db._locked = True
        delay(3, _wrong_password, account)
        options = (
            {
                "key": "b",
                "desc": "Go back to the login screen.",
                "goto": "pre_start",
            },
            {
                "key": "_default",
                "desc": "Enter your password again.",
                "goto": "password",
            },
        )
    elif banned:
        # This is a banned IP or name
        string = dedent("""
            |rYou have been banned and cannot continue from here.|n
            If you feel this ban is in error, please email an admin.
        """.strip("\n"))
        caller.msg(string)
        caller.sessionhandler.disconnect(caller, "Good bye!  Disconnecting...")
    else:
        # The password is correct, we can log into the account.
        caller.msg(echo=True)
        if not account.email:
            # Redirects to the node to set an email address
            text = _text_email_address(account)
            options = ({
                "key": "_default",
                "desc": "Enter your email address.",
                "goto": "email_address",
            }, )
        elif not account.db.valid:
            # Redirects to the node for the validation code
            text = "Enter your 4-digit validation code."
            options = ({
                "key": "_default",
                "desc": "Enter your validation code.",
                "goto": "validate_account",
            }, )
        else:
            _login(caller, account)
            text = ""
            options = _options_choose_characters(account)
            if not account.db._playable_characters:
                options = ({
                    "key": "_default",
                    "desc": "Enter your new character's first name.",
                    "goto": "create_first_name",
                }, )

    return text, options
Exemplo n.º 33
0
    def func(self):
        """Performs the summon, accounting for in-world conditions."""

        char = self.character
        loc = char.location
        account = self.account
        args = self.args
        lhs, rhs = self.lhs, self.rhs
        # opt = self.switches  # TODO - add code to make the switches work.

        if char and char.ndb.currently_moving:
            account.msg("You can not summon while moving. (|rstop|n, then try again.)")
            return
        if not args:
            char.msg("Could not find target to summon. Usage: summon <character or NPC>")
            return
        session_list = SESSIONS.get_sessions()
        target = []
        for session in session_list:
            if not (session.logged_in and session.get_puppet()):
                continue
            puppet = session.get_puppet()
            if lhs.lower() in puppet.get_display_name(char, plain=True).lower():
                target.append(puppet)
        if len(target) < 1:
            char.msg("Error: character not found.")
            return
        if len(target) > 1:  # Too many partial matches, try exact matching.
            char.msg("Error: character not found.")
            return
        else:
            target[0].msg("You are being summoned to %s " % loc.get_display_name(target[0]) +
                          "by %s" % char.get_display_name(target[0]) +
                          ". A portal should appear soon that will take you to " +
                          "%s." % char.get_display_name(target[0]))
            char.msg("You begin to summon a portal between %s" % target[0].get_display_name(char) +
                     " and your location.")
        # Check the pool for objects to use.  Filter a list of objects tagged "pool" by those located in None.
        obj_pool = [each for each in evennia.search_tag('pool', category='portal') if not each.location]
        print('Object pool total: %i' % len(obj_pool))
        if len(obj_pool) < 2:
            char.msg('Portals are currently out of stock or in use elsewhere.')
            return
        portal_enter, portal_exit = obj_pool[-2:]

        def open_portal():
            """Move inflatable portals into place."""
            portal_enter.move_to(target[0].location)
            portal_exit.move_to(loc)

        delay(10, callback=open_portal)  # 10 seconds later, the portal (exit pair) appears.

        def close_portal():
            """Remove and store inflatable portals in Nothingness."""
            for each in portal_enter.contents:
                each.move_to(target[0].location)
            for each in portal_exit.contents:
                each.move_to(loc)
            portal_enter.location.msg_contents("|r%s|n vanishes into |222Nothingness|n." % portal_enter)
            portal_exit.location.msg_contents("|r%s|n vanishes into |222Nothingness|n." % portal_exit)
            portal_enter.move_to(None, to_none=True)
            portal_exit.move_to(None, to_none=True)

        delay(180, callback=close_portal)  # Wait, then move portal objects to the portal pool in Nothingness
Exemplo n.º 34
0
    def at_traverse(self, traversing_object, target_location, **kwargs):
        """
        This implements the actual traversal. The traverse lock has
        already been checked (in the Exit command) at this point.

        Args:
            traversing_object (Object): Object traversing us.
            target_location (Object): Where target is going.

        Returns:
            bool: True if the traverse is allowed to happen

        """
        currently_moving = traversing_object.ndb.currently_moving
        if currently_moving and not currently_moving.called:
            currently_moving.cancel()
            traversing_object.db.travel_direction = None

        itemcoordinates = self.location.wilderness.db.itemcoordinates

        current_coordinates = itemcoordinates[traversing_object]
        new_coordinates = get_new_coordinates(current_coordinates, self.key)

        if not self.at_traverse_coordinates(
            traversing_object, current_coordinates, new_coordinates
        ):
            return False

        if not traversing_object.at_before_move(None):
            return False

        string = "{object} va verso {direction}."
        mapping = {"object": traversing_object, "direction": self.key}
        room = traversing_object.location
        room.msg_contents(string, exclude=(traversing_object,), mapping=mapping)

        script = WildernessScript.objects.get(
            db_key=traversing_object.db.last_wilderness
        )

        if not script.is_valid_coordinates(new_coordinates):
            self.at_failed_traverse(traversing_object)
            return False

        def move_callback():
            self.location.wilderness.move_obj(traversing_object, new_coordinates)

            opposite = {
                "nord": "sud",
                "nordest": "sudovest",
                "est": "ovest",
                "sudest": "nordovest",
                "sud": "nord",
                "sudovest": "nordest",
                "ovest": "est",
                "nordovest": "sudest",
            }

            string = "{object} arriva da {direction}."
            mapping = {"object": traversing_object, "direction": opposite[self.key]}
            room = traversing_object.location
            room.msg_contents(string, exclude=(traversing_object,), mapping=mapping)

            traversing_object.at_after_move(None)

        move_speed = traversing_object.db.move_speed or "cammina"
        move_delay = slow_exit.MOVE_DELAY.get(move_speed, 2)

        if self.location.db.water:
            move_speed = "nuota"
            move_delay = slow_exit.MOVE_DELAY.get(move_speed, 2)

        if kwargs.get("forced"):
            traversing_object.msg("\n|cLa corrente ti spinge verso %s.|n" % self.key)
            move_delay = 2
        else:
            traversing_object.msg(
                "Inizi a %s verso %s." % (slow_exit.SPEED_VERBS[move_speed], self.key)
            )

        deferred = utils.delay(move_delay, move_callback)
        traversing_object.ndb.currently_moving = deferred
        return True
Exemplo n.º 35
0
def _do_strike(st_remaining, character, target, args):
    """Implements the 'strike' combat command."""
    ch = character.ndb.combat_handler

    if character.db.position != 'STANDING' and 'end' not in args:
        # if you are in any wrestling position other
        # than free standing, all you can do is wrestle to break free
        return _do_wrestle(character, target, ['break'])

    # confirm the target is still in combat,
    if target.id not in ch.db.characters:
        character.msg("{defender} has left combat.".format(
            defender=target.get_display_name(character)))
        return 0.2 * COMBAT_DELAY

    # is within range,
    attack_range = ch.get_range(character, target)
    if attack_range != 'melee':
        ch.combat_msg("{actor} is too far away from {target} to strike them.",
                      actor=character,
                      target=target)
        return 0.2 * COMBAT_DELAY

    # and has at least one free hand
    strikes = sum(1 if character.equip.get(slot) is None else 0
                  for slot in character.db.slots if slot.startswith('wield'))

    if strikes <= 0:
        ch.combat_msg("{actor} goes to punch, but does not have a free hand.",
                      actor=character)
        return 0.2 * COMBAT_DELAY

    # check whether the target is dodging
    dodging = target.nattributes.has('dodging')

    if dodging:
        # attacker must take the worse of two standard rolls
        atk_roll = min((std_roll(), std_roll()))
    else:
        atk_roll = std_roll()

    damage = (atk_roll + character.traits.ATKU) - target.traits.DEF
    if damage > 0:
        if dodging:
            ch.combat_msg("{actor} tries to dodge, but", actor=target)

        if any((arg.startswith('s') for arg in args)):
            # 'subdue': deduct stamina instead of health
            if target.traits.SP >= damage:
                target.traits.SP.current -= damage
                status = 'dmg_sp'
            else:
                # if we don't have enough SP, damages HP instead
                damage = damage - target.traits.SP
                target.traits.SP.current = 0
                target.traits.HP.current -= damage
                status = 'dmg_hp'

        else:
            target.traits.HP.current -= damage
            status = 'dmg_hp'
    else:
        if dodging:
            status = 'dodged'
        else:
            status = 'missed'

    messages = {
        'dmg_hp': "{actor} strikes {target} savagely with their fist.",
        'dmg_sp': "{actor} strikes {target} hard in the chest, and {target} "
        "staggers from the blow.",
        'dodged': "{target} dodges a punch from {actor}.",
        'missed': "{actor} attempts to punch {target} and misses."
    }

    ch.combat_msg(messages[status], actor=character, target=target)

    # handle Power Points
    if atk_roll > 0:
        # this attack has earned PPs
        character.traits.PP.current += atk_roll

    if character.traits.PP.actual > 0:
        # handle equipment abilities
        pass

    if target.traits.HP.actual <= 0:
        # target has died
        resolve_death(character, target, ch)

    if 'end' not in args:
        if strikes > 1:
            # we have two free hands; do a second strike
            args.append('end')
            utils.delay(0.5 * COMBAT_DELAY, _do_strike, 0, character, target,
                        args)

        return 1 * COMBAT_DELAY
Exemplo n.º 36
0
    def func(self):
        """
            Performs the summon, accounting for in-world conditions.
            join: Implies one way portal to target
            summon: Implies one way portal to summoner
            meet: Implies two-way portal, both can meet.
        """

        char = self.character
        cmd = self.cmdstring
        loc = char.location
        account = self.account
        args = self.args
        lhs, rhs = self.lhs, self.rhs
        opt = self.switches

        message_private = ' in a private room that does not allow portals to form.'

        if char and char.ndb.currently_moving:
            account.msg(
                "You can not open a portal while moving. (|lcstop|lt|rStop|n|le, then try again.)"
            )
            return
        if not args and 'vanish' not in opt:
            char.msg('Usage: {} <character or NPC>'.format(cmd))
            return
        session_list = SESSIONS.get_sessions()
        target = []
        # Check for private flag on source room. It must be controlled by summoner if private.
        if loc.tags.get('private',
                        category='flags') and not loc.access(char, 'control'):
            char.msg('You are' + message_private)
            return
        # Check object pool filtered by tagged "pool" and located in None.
        obj_pool = [
            each for each in evennia.search_tag('pool', category='portal')
            if not each.location
        ]
        print('Object pool total: %i' % len(obj_pool))
        if len(obj_pool) < 2:
            char.msg('Portals are currently out of stock or in use elsewhere.')
            return
        portal_enter, portal_exit = obj_pool[-2:]
        for session in session_list:
            if not (session.logged_in and session.get_puppet()):
                continue
            puppet = session.get_puppet()
            if lhs.lower() in puppet.get_display_name(char,
                                                      plain=True).lower():
                target.append(puppet)
        if len(target) < 1:
            char.msg("Specific character name not found.")
            return
        elif len(target) > 1:  # Too many partial matches, try exact matching.
            char.msg("Unique character name not found.")
            return
        first_target = target[0]
        target = list(set(target))  # Remove duplicate character sessions
        target = first_target
        # Check for private flag on destination room. If so, check for in/out locks.
        there = target.location
        if there and there.tags.get(
                'private',
                category='flags') and not there.access(char, 'control'):
            char.msg('Destination of portal is' + message_private)
            return
        # Check if A can walk to B, or B to A depending on meet or summon,
        # because sometimes a portal might not be needed.
        meet_message = 'You are being invited to meet {summoner} in {loc}.'
        join_message = 'You are being joined by {summoner} from {loc}.'
        summon_message = 'You are being summoned to {loc} by {summoner}.'
        message = meet_message if 'meet' in cmd else (
            summon_message if 'summon' in cmd else join_message)
        loc_name = loc.get_display_name(target)
        target_name = target.get_display_name(char)
        char_name = char.get_display_name(target)
        target.msg(message.format(summoner=char_name, loc=loc_name))
        target.msg('A portal should appear soon.')
        char.msg("You begin to open a portal connecting %s" % target_name +
                 " and your location.")

        def open_portal():
            """Move inflatable portals into place."""
            # If in or out, join or summon, lock portals, depending.
            enter_lock, exit_lock = 'all()', 'all()'
            if 'only' in opt:
                enter_lock = 'id({}) OR id({})'.format(target.id, char.id)
                exit_lock = 'id({}) OR id({})'.format(target.id, char.id)
            if 'in' in opt or 'join' in cmd:
                enter_lock = 'none()'
            if 'out' in opt or 'summon' in cmd:
                exit_lock = 'none()'
            portal_enter.locks.add('enter:' + enter_lock)
            portal_exit.locks.add('enter:' + exit_lock)
            quiet = True if ('quiet' in opt or 'silent' in opt) else False
            portal_enter.move_to(target.location, quiet=quiet)
            if quiet:
                target.msg('{} quietly appears in {}.'.format(
                    portal_enter.get_display_name(target), loc_name))
                char.msg('{} quietly appears in {}.'.format(
                    portal_exit.get_display_name(char),
                    loc.get_display_name(char)))
            portal_exit.move_to(loc, quiet=quiet)

        delay(10, callback=open_portal
              )  # 10 seconds later, the portal (exit pair) appears.

        def close_portal():
            """Remove and store inflatable portals in Nothingness."""
            vanish_message = '|r{}|n vanishes into ' + settings.NOTHINGNESS + '.'
            for every in portal_enter.contents:
                every.move_to(target.location)
            for every in portal_exit.contents:
                every.move_to(loc)
            # if not quiet:
            if portal_enter.location:
                portal_enter.location.msg_contents(
                    vanish_message.format(portal_enter))
                portal_enter.move_to(None, to_none=True)  # , quiet=quiet)
            if portal_exit.location:
                portal_exit.location.msg_contents(
                    vanish_message.format(portal_exit))
                portal_exit.move_to(None, to_none=True)  # , quiet=quiet)

        delay(
            180, callback=close_portal
        )  # Wait, then move portal objects to the portal pool in Nothingness
Exemplo n.º 37
0
def process_next_action(combat_handler):
    """
    Callback that handles processing combat actions

    Args:
        combat_handler: instance of a combat handler

    Returns:
        None

    Based on the combat handler's data, this callback
    selects an appropriate `_do_*` function to execute
    the combat action. These handlers are all called
    with the signature:

        `_do_action(subturns_remaining, character, target, args)`

    Each `_do_*` function should return a time delay
    in seconds before the next call to `process_next_action`
    should be run.
    """
    turn_actions = combat_handler.db.turn_actions
    turn_order = combat_handler.db.turn_order
    actor_idx = combat_handler.db.actor_idx
    delay = 0

    if actor_idx >= len(turn_order):
        # finished a subturn
        # reset counters and see who is dodging during the next action
        combat_handler.ndb.actions_taken = defaultdict(int)
        combat_handler.db.actor_idx = actor_idx = 0
        for dbref, char in combat_handler.db.characters.items():
            if char.nattributes.has('dodging'):
                del char.ndb.dodging
            action_count = 0

            # set the dodging nattribute on any characters
            # with 'dodge' as their action
            for action, _, _, duration in combat_handler.db.turn_actions[
                    dbref]:
                if action == 'dodge':
                    char.ndb.dodging = True
                    break
                action_count += duration
                if action_count >= 1:
                    break

        # and increment the subturn
        combat_handler.db.subturn += 1

    if combat_handler.db.subturn > ACTIONS_PER_TURN:
        # turn is over; notify the handler to start the next
        combat_handler.begin_turn()
        return

    current_charid = turn_order[actor_idx]
    if combat_handler.ndb.actions_taken[current_charid] < 1:
        action, character, target, duration = \
            turn_actions[current_charid].popleft()

        combat_handler.ndb.actions_taken[current_charid] += duration

        args = action.split('/')[1:]

        # we decrement the duration for this turn
        duration -= 1
        if duration > 0:
            # action takes more than one subturn; return it to the queue
            turn_actions[current_charid].append(
                (action, character, target, duration))
            combat_handler.db.actor_idx += 1

        action_func = '_do_{}'.format(action.split('/')[0])

        # action_func receives the number of subturns remaining in the action
        delay = globals()[action_func](duration, character, target, args)
    else:
        combat_handler.db.actor_idx += 1

    if len(combat_handler.db.characters) < 2:
        combat_handler.stop()
    else:
        utils.delay(delay, process_next_action, combat_handler)
Exemplo n.º 38
0
    def func(self):
        """Performs the summon, accounting for in-world conditions."""

        char = self.character
        loc = char.location
        account = self.account
        args = self.args
        lhs, rhs = self.lhs, self.rhs
        # opt = self.switches  # TODO - add code to make the switches work.

        if char and char.ndb.currently_moving:
            account.msg(
                "You can not summon while moving. (|rstop|n, then try again.)")
            return
        if not args:
            char.msg(
                "Could not find target to summon. Usage: summon <character or NPC>"
            )
            return
        session_list = SESSIONS.get_sessions()
        target = []
        for session in session_list:
            if not (session.logged_in and session.get_puppet()):
                continue
            puppet = session.get_puppet()
            if lhs.lower() in puppet.get_display_name(char,
                                                      plain=True).lower():
                target.append(puppet)
        if len(target) < 1:
            char.msg("Error: character not found.")
            return
        if len(target) > 1:  # Too many partial matches, try exact matching.
            char.msg("Error: character not found.")
            return
        else:
            target[0].msg(
                "You are being summoned to %s " %
                loc.get_display_name(target[0]) +
                "by %s" % char.get_display_name(target[0]) +
                ". A portal should appear soon that will take you to " +
                "%s." % char.get_display_name(target[0]))
            char.msg("You begin to summon a portal between %s" %
                     target[0].get_display_name(char) + " and your location.")
        # Check the pool for objects to use.  Filter a list of objects tagged "pool" by those located in None.
        obj_pool = [
            each for each in evennia.search_tag('pool', category='portal')
            if not each.location
        ]
        print('Object pool total: %i' % len(obj_pool))
        if len(obj_pool) < 2:
            char.msg('Portals are currently out of stock or in use elsewhere.')
            return
        portal_enter, portal_exit = obj_pool[-2:]

        def open_portal():
            """Move inflatable portals into place."""
            portal_enter.move_to(target[0].location)
            portal_exit.move_to(loc)

        delay(10, callback=open_portal
              )  # 10 seconds later, the portal (exit pair) appears.

        def close_portal():
            """Remove and store inflatable portals in Nothingness."""
            for each in portal_enter.contents:
                each.move_to(target[0].location)
            for each in portal_exit.contents:
                each.move_to(loc)
            portal_enter.location.msg_contents(
                "|r%s|n vanishes into |222Nothingness|n." % portal_enter)
            portal_exit.location.msg_contents(
                "|r%s|n vanishes into |222Nothingness|n." % portal_exit)
            portal_enter.move_to(None, to_none=True)
            portal_exit.move_to(None, to_none=True)

        delay(
            180, callback=close_portal
        )  # Wait, then move portal objects to the portal pool in Nothingness
Exemplo n.º 39
0
 def _idle_spawner(self):
     """
     This method is called when it is time for the spawning to cease. It then attempts to start
     spawning again once the wait period is over.
     """
     utils.delay(30, self.start_spawner)
Exemplo n.º 40
0
def set_roundtime(owner):
    now = time.time()
    delay(2, unbusy, owner, persistent=True)
    owner.db.busy = True
    owner.db.roundtime = now
Exemplo n.º 41
0
    def func(self):
        """Implement function using the Msg methods"""
        char = self.character
        sent_messages = Msg.objects.get_messages_by_sender(
            char, exclude_channel_messages=True)
        recd_messages = Msg.objects.get_messages_by_receiver(char)
        if 'last' in self.switches:
            self.mail_check()
            if sent_messages:
                recv = ', '.join('%s%s|n' % (obj.STYLE, obj.key)
                                 for obj in sent_messages[-1].receivers)
                self.msg("You last mailed |w%s|n: |w%s" %
                         (recv, sent_messages[-1].message))
            else:
                self.msg("You haven't mailed anyone yet.")
            self.mail_check()
            return
        if 'check' in self.switches:
            if not self.mail_check():
                if not ('silent' in self.switches
                        and 'quiet' in self.switches):
                    self.msg('Your %s mailbox has no new mail.' %
                             char.location.get_display_name(self.character))
        if not self.args or not self.rhs:
            mail = sent_messages + recd_messages
            mail.sort(lambda x, y: cmp(x.db_date_created, y.db_date_created))
            number = 5
            if self.args:
                try:
                    number = int(self.args)
                except ValueError:
                    self.msg("Usage: mail [<character> = msg]")
                    return
            if len(mail) > number:
                mail_last = mail[-number:]
            else:
                mail_last = mail
            template = "|w%s|n |w%s|n to |w%s|n: %s"
            mail_last = "\n ".join(
                template %
                (utils.datetime_format(mail.date_created), ', '.join(
                    '%s' % obj.get_display_name(self.character)
                    for obj in mail.senders), ', '.join([
                        '%s' % obj.get_display_name(self.character)
                        for obj in mail.receivers
                    ]), mail.message) for mail in mail_last)
            if mail_last:
                string = "Your latest letters:\n %s" % mail_last
            else:
                string = "You haven't mailed anyone yet."
            self.msg(string)
            char.nattributes.remove('new_mail')  # Removes the notice.
            return
        # Send mode
        if not self.lhs:
            if sent_messages:  # If no recipients provided,
                receivers = sent_messages[
                    -1].receivers  # default to sending to the last character mailed.
            else:
                self.msg("Who do you want to mail?")
                return
        else:  # Build a list of comma-delimited recipients.
            receivers = self.lhslist
        rec_objs = []
        received = []
        r_strings = []
        for receiver in set(receivers):
            if isinstance(receiver, basestring):
                c_obj = char.search(receiver, global_search=True, exact=True)
            elif hasattr(receiver, 'location'):
                c_obj = receiver
            else:
                self.msg("Who do you want to mail?")
                return
            if c_obj:
                if not c_obj.access(char, 'mail'):
                    r_strings.append("You are not able to mail %s." % c_obj)
                    continue
                rec_objs.append(c_obj)
        if not rec_objs:
            self.msg("No one found to mail.")
            return
        message = self.rhs.strip()
        if message.startswith(
                ':'):  # Format as pose if message begins with a :
            message = "%s%s|n %s" % (char.STYLE, char.key, message.strip(':'))
        create.create_message(char, message, receivers=rec_objs)

        def letter_delivery():
            # c_obj.msg('%s %s' % (header, message))
            c_obj.msg('|/A letter has arrived in %s%s|n mailbox for you.|/' %
                      (c_obj.home.STYLE, c_obj.home.key))

        for c_obj in rec_objs:  # Notify character of mail delivery.
            received.append('%s%s|n' % (c_obj.STYLE, c_obj.key))
            if hasattr(c_obj, 'sessions') and not c_obj.sessions.count():
                r_strings.append(
                    "|r%s|n is currently asleep, and won't read the letter until later."
                    % received[-1])
                c_obj.ndb.new_mail = True
            else:  # Tell the receiving characters about receiving a letter if they are online.
                utils.delay(20, callback=letter_delivery)
        if r_strings:
            self.msg("\n".join(r_strings))
        stamp_count = len(rec_objs)
        stamp_plural = 'a stamp' if stamp_count == 1 else '%i stamps' % stamp_count
        self.msg('Mail delivery costs %s.' % stamp_plural)
        char.location.msg_contents(
            '|g%s|n places %s on an envelope and slips it into the %s%s|n mailbox.'
            % (char.key, stamp_plural, char.location.STYLE, char.location.key))
        self.msg("Your letter to %s will be delivered soon. You wrote: %s" %
                 (', '.join(received), message))
        self.mail_check()