Beispiel #1
0
    def do_attack(self, *args, **kwargs):
        """
        Called regularly when in attacking mode. In attacking mode
        the mob will bring its weapons to bear on any targets
        in the room.
        """
        if random.random() < 0.01 and self.db.irregular_msgs:
            self.location.msg_contents(random.choice(self.db.irregular_msgs))
        # first make sure we have a target
        target = self._find_target(self.location)
        if not target:
            # no target, start looking for one
            self.start_hunting()
            return

        # we use the same attack commands as defined in
        # tutorial_world.objects.Weapon, assuming that
        # the mob is given a Weapon to attack with.
        attack_cmd = random.choice(
            ("thrust", "pierce", "stab", "slash", "chop"))
        self.execute_cmd("%s %s" % (attack_cmd, target))

        # analyze the current state
        if target.db.health <= 0:
            # we reduced the target to <= 0 health. Move them to the
            # defeated room
            target.msg(self.db.defeat_msg)
            self.location.msg_contents(self.db.defeat_msg_room % target.key,
                                       exclude=target)
            send_defeated_to = search_object(self.db.send_defeated_to)
            if send_defeated_to:
                target.move_to(send_defeated_to[0], quiet=True)
            else:
                logger.log_err("Mob: mob.db.send_defeated_to not found: %s" %
                               self.db.send_defeated_to)
Beispiel #2
0
    def do_attack(self, *args, **kwargs):
        """
        Called regularly when in attacking mode. In attacking mode
        the mob will bring its weapons to bear on any targets
        in the room.
        """
        if random.random() < 0.01 and self.db.irregular_msgs:
            self.location.msg_contents(random.choice(self.db.irregular_msgs))
        # first make sure we have a target
        target = self._find_target(self.location)
        if not target:
            # no target, start looking for one
            self.start_hunting()
            return

        # we use the same attack commands as defined in
        # tutorial_world.objects.Weapon, assuming that
        # the mob is given a Weapon to attack with.
        attack_cmd = random.choice(("thrust", "pierce", "stab", "slash", "chop"))
        self.execute_cmd("%s %s" % (attack_cmd, target))

        # analyze the current state
        if target.db.health <= 0:
            # we reduced the target to <= 0 health. Move them to the
            # defeated room
            target.msg(self.db.defeat_msg)
            self.location.msg_contents(self.db.defeat_msg_room % target.key, exclude=target)
            send_defeated_to = search_object(self.db.send_defeated_to)
            if send_defeated_to:
                target.move_to(send_defeated_to[0], quiet=True)
            else:
                logger.log_err("Mob: mob.db.send_defeated_to not found: %s" % self.db.send_defeated_to)
Beispiel #3
0
    def handle_error(self, callback, trace):
        """
        Handle an error in a callback.

        Args:
            callback (dict): the callback representation.
            trace (list): the traceback containing the exception.

        Notes:
            This method can be useful to override to change the default
            handling of errors.  By default, the error message is sent to
            the character who last updated the callback, if connected.
            If not, display to the everror channel.

        """
        callback_name = callback["name"]
        number = callback["number"]
        obj = callback["obj"]
        oid = obj.id
        logger.log_err("An error occurred during the callback {} of "
                       "{} (#{}), number {}\n{}".format(
                           callback_name, obj, oid, number + 1,
                           "\n".join(trace)))

        # Create the error message
        line = "|runknown|n"
        lineno = "|runknown|n"
        for error in trace:
            if error.startswith('  File "<string>", line '):
                res = RE_LINE_ERROR.search(error)
                if res:
                    lineno = int(res.group(1))

                    # Try to extract the line
                    try:
                        line = raw(callback["code"].splitlines()[lineno - 1])
                    except IndexError:
                        continue
                    else:
                        break

        exc = raw(trace[-1].strip("\n").splitlines()[-1])
        err_msg = "Error in {} of {} (#{})[{}], line {}:" \
            " {}\n{}".format(callback_name, obj,
                             oid, number + 1, lineno, line, exc)

        # Inform the last updater if connected
        updater = callback.get("updated_by")
        if updater is None:
            updater = callback["created_by"]

        if updater and updater.sessions.all():
            updater.msg(err_msg)
        else:
            err_msg = "Error in {} of {} (#{})[{}], line {}:" \
                " {}\n          {}".format(callback_name, obj,
                                           oid, number + 1, lineno, line, exc)
            self.ndb.channel.msg(err_msg)
Beispiel #4
0
    def handle_error(self, callback, trace):
        """
        Handle an error in a callback.

        Args:
            callback (dict): the callback representation.
            trace (list): the traceback containing the exception.

        Notes:
            This method can be useful to override to change the default
            handling of errors.  By default, the error message is sent to
            the character who last updated the callback, if connected.
            If not, display to the everror channel.

        """
        callback_name = callback["name"]
        number = callback["number"]
        obj = callback["obj"]
        oid = obj.id
        logger.log_err("An error occurred during the callback {} of " \
                "{} (#{}), number {}\n{}".format(callback_name, obj,
                oid, number + 1, "\n".join(trace)))

        # Create the error message
        line = "|runknown|n"
        lineno = "|runknown|n"
        for error in trace:
            if error.startswith('  File "<string>", line '):
                res = RE_LINE_ERROR.search(error)
                if res:
                    lineno = int(res.group(1))

                    # Try to extract the line
                    try:
                        line = raw(callback["code"].splitlines()[lineno - 1])
                    except IndexError:
                        continue
                    else:
                        break

        exc = raw(trace[-1].strip("\n").splitlines()[-1])
        err_msg = "Error in {} of {} (#{})[{}], line {}:" \
                " {}\n{}".format(callback_name, obj,
                oid, number + 1, lineno, line, exc)

        # Inform the last updater if connected
        updater = callback.get("updated_by")
        if updater is None:
            updater = callback["created_by"]

        if updater and updater.sessions.all():
            updater.msg(err_msg)
        else:
            err_msg = "Error in {} of {} (#{})[{}], line {}:" \
                    " {}\n          {}".format(callback_name, obj,
                    oid, number + 1, lineno, line, exc)
            self.ndb.channel.msg(err_msg)
Beispiel #5
0
 def set_dead(self):
     self.db.is_dead = True
     self.ndb.is_attacking = False
     self.ndb.is_immortal = True
     send_defeated_to = search_object(self.db.send_defeated_to)
     if send_defeated_to:
         self.move_to(send_defeated_to[0], quiet=True)
     else:
         self.msg(send_defeated_to + " 未找到")
         logger.log_err("Mob: mob.db.send_defeated_to not found: %s" %
                        self.db.send_defeated_to)
     self.set_alive()
Beispiel #6
0
    def get_object(self, key):
        """
        Find a named *non-character* object for this state in this room.

        Args:
            key (str): Object to search for.
        Returns:
            obj (Object): Object in the room.

        """
        match = EvscaperoomObject.objects.filter_family(
            db_key__iexact=key,
            db_tags__db_category=self.room.tagcategory.lower())
        if not match:
            logger.log_err(f"get_object: No match for '{key}' in state ")
            return None
        return match[0]
Beispiel #7
0
def complete_task(task_id):
    """
    Mark the task in the event handler as complete.

    Args:
        task_id (int): the task ID.

    Note:
        This function should be called automatically for individual tasks.

    """
    try:
        script = ScriptDB.objects.get(db_key="event_handler")
    except ScriptDB.DoesNotExist:
        logger.log_trace("Can't get the event handler.")
        return

    if task_id not in script.db.tasks:
        logger.log_err("The task #{} was scheduled, but it cannot be " \
                "found".format(task_id))
        return

    delta, obj, callback_name, locals = script.db.tasks.pop(task_id)
    script.call(obj, callback_name, locals=locals)
Beispiel #8
0
def complete_task(task_id):
    """
    Mark the task in the event handler as complete.

    Args:
        task_id (int): the task ID.

    Note:
        This function should be called automatically for individual tasks.

    """
    try:
        script = ScriptDB.objects.get(db_key="event_handler")
    except ScriptDB.DoesNotExist:
        logger.log_trace("Can't get the event handler.")
        return

    if task_id not in script.db.tasks:
        logger.log_err("The task #{} was scheduled, but it cannot be " \
                "found".format(task_id))
        return

    delta, obj, callback_name, locals = script.db.tasks.pop(task_id)
    script.call(obj, callback_name, locals=locals)
Beispiel #9
0
    def call(self, obj, callback_name, *args, **kwargs):
        """
        Call the connected callbacks.

        Args:
            obj (Object): the Evennia typeclassed object.
            callback_name (str): the callback name to call.
            *args: additional variables for this callback.

        Kwargs:
            number (int, optional): call just a specific callback.
            parameters (str, optional): call a callback with parameters.
            locals (dict, optional): a locals replacement.

        Returns:
            True to report the callback was called without interruption,
            False otherwise.

        """
        # First, look for the callback type corresponding to this name
        number = kwargs.get("number")
        parameters = kwargs.get("parameters")
        locals = kwargs.get("locals")

        # Errors should not pass silently
        allowed = ("number", "parameters", "locals")
        if any(k for k in kwargs if k not in allowed):
            raise TypeError("Unknown keyword arguments were specified " \
                    "to call callbacks: {}".format(kwargs))

        event = self.get_events(obj).get(callback_name)
        if locals is None and not event:
            logger.log_err("The callback {} for the object {} (typeclass " \
                    "{}) can't be found".format(callback_name, obj, type(obj)))
            return False

        # Prepare the locals if necessary
        if locals is None:
            locals = self.ndb.fresh_locals.copy()
            for i, variable in enumerate(event[0]):
                try:
                    locals[variable] = args[i]
                except IndexError:
                    logger.log_trace("callback {} of {} ({}): need variable " \
                            "{} in position {}".format(callback_name, obj,
                            type(obj), variable, i))
                    return False
        else:
            locals = {key: value for key, value in locals.items()}

        callbacks = self.get_callbacks(obj).get(callback_name, [])
        if event:
            custom_call = event[2]
            if custom_call:
                callbacks = custom_call(callbacks, parameters)

        # Now execute all the valid callbacks linked at this address
        self.ndb.current_locals = locals
        for i, callback in enumerate(callbacks):
            if not callback["valid"]:
                continue

            if number is not None and callback["number"] != number:
                continue

            try:
                exec(callback["code"], locals, locals)
            except InterruptEvent:
                return False
            except Exception:
                etype, evalue, tb = sys.exc_info()
                trace = traceback.format_exception(etype, evalue, tb)
                self.handle_error(callback, trace)

        return True
Beispiel #10
0
    def call(self, obj, callback_name, *args, **kwargs):
        """
        Call the connected callbacks.

        Args:
            obj (Object): the Evennia typeclassed object.
            callback_name (str): the callback name to call.
            *args: additional variables for this callback.

        Kwargs:
            number (int, optional): call just a specific callback.
            parameters (str, optional): call a callback with parameters.
            locals (dict, optional): a locals replacement.

        Returns:
            True to report the callback was called without interruption,
            False otherwise.

        """
        # First, look for the callback type corresponding to this name
        number = kwargs.get("number")
        parameters = kwargs.get("parameters")
        locals = kwargs.get("locals")

        # Errors should not pass silently
        allowed = ("number", "parameters", "locals")
        if any(k for k in kwargs if k not in allowed):
            raise TypeError("Unknown keyword arguments were specified " \
                    "to call callbacks: {}".format(kwargs))

        event = self.get_events(obj).get(callback_name)
        if locals is None and not event:
            logger.log_err("The callback {} for the object {} (typeclass " \
                    "{}) can't be found".format(callback_name, obj, type(obj)))
            return False

        # Prepare the locals if necessary
        if locals is None:
            locals = self.ndb.fresh_locals.copy()
            for i, variable in enumerate(event[0]):
                try:
                    locals[variable] = args[i]
                except IndexError:
                    logger.log_trace("callback {} of {} ({}): need variable " \
                            "{} in position {}".format(callback_name, obj,
                            type(obj), variable, i))
                    return False
        else:
            locals = {key: value for key, value in locals.items()}

        callbacks = self.get_callbacks(obj).get(callback_name, [])
        if event:
            custom_call = event[2]
            if custom_call:
                callbacks = custom_call(callbacks, parameters)

        # Now execute all the valid callbacks linked at this address
        self.ndb.current_locals = locals
        for i, callback in enumerate(callbacks):
            if not callback["valid"]:
                continue

            if number is not None and callback["number"] != number:
                continue

            try:
                exec(callback["code"], locals, locals)
            except InterruptEvent:
                return False
            except Exception:
                etype, evalue, tb = sys.exc_info()
                trace = traceback.format_exception(etype, evalue, tb)
                self.handle_error(callback, trace)

        return True
Beispiel #11
0
def get_next_wait(format):
    """
    Get the length of time in seconds before format.

    Args:
        format (str): a time format matching the set calendar.

    Returns:
        until (int or float): the number of seconds until the event.
        usual (int or float): the usual number of seconds between events.
        format (str): a string format representing the time.

    Notes:
        The time format could be something like "2018-01-08 12:00".  The
        number of units set in the calendar affects the way seconds are
        calculated.

    """
    calendar = getattr(settings, "EVENTS_CALENDAR", None)
    if calendar is None:
        logger.log_err("A time-related event has been set whereas " \
                "the gametime calendar has not been set in the settings.")
        return
    elif calendar == "standard":
        rsu = standard_rsu
        units = ["min", "hour", "day", "month", "year"]
    elif calendar == "custom":
        rsu = custom_rsu
        back = dict([(value, name) for name, value in UNITS.items()])
        sorted_units = sorted(back.items())
        del sorted_units[0]
        units = [n for v, n in sorted_units]

    params = {}
    for delimiter in ("-", ":"):
        format = format.replace(delimiter, " ")

    pieces = list(reversed(format.split()))
    details = []
    i = 0
    for uname in units:
        try:
            piece = pieces[i]
        except IndexError:
            break

        if not piece.isdigit():
            logger.log_trace("The time specified '{}' in {} isn't " \
                    "a valid number".format(piece, format))
            return

        # Convert the piece to int
        piece = int(piece)
        params[uname] = piece
        details.append("{}={}".format(uname, piece))
        if i < len(units):
            next_unit = units[i + 1]
        else:
            next_unit = None
        i += 1

    params["sec"] = 0
    details = " ".join(details)
    until = rsu(**params)
    usual = -1
    if next_unit:
        kwargs = {next_unit: 1}
        usual = gametime_to_realtime(**kwargs)
    return until, usual, details
Beispiel #12
0
def get_next_wait(format):
    """
    Get the length of time in seconds before format.

    Args:
        format (str): a time format matching the set calendar.

    Returns:
        until (int or float): the number of seconds until the event.
        usual (int or float): the usual number of seconds between events.
        format (str): a string format representing the time.

    Notes:
        The time format could be something like "2018-01-08 12:00".  The
        number of units set in the calendar affects the way seconds are
        calculated.

    """
    calendar = getattr(settings, "EVENTS_CALENDAR", None)
    if calendar is None:
        logger.log_err("A time-related event has been set whereas " \
                "the gametime calendar has not been set in the settings.")
        return
    elif calendar == "standard":
        rsu = standard_rsu
        units = ["min", "hour", "day", "month", "year"]
    elif calendar == "custom":
        rsu = custom_rsu
        back = dict([(value, name) for name, value in UNITS.items()])
        sorted_units = sorted(back.items())
        del sorted_units[0]
        units = [n for v, n in sorted_units]

    params = {}
    for delimiter in ("-", ":"):
        format = format.replace(delimiter, " ")

    pieces = list(reversed(format.split()))
    details = []
    i = 0
    for uname in units:
        try:
            piece = pieces[i]
        except IndexError:
            break

        if not piece.isdigit():
            logger.log_trace("The time specified '{}' in {} isn't " \
                    "a valid number".format(piece, format))
            return

        # Convert the piece to int
        piece = int(piece)
        params[uname] = piece
        details.append("{}={}".format(uname, piece))
        if i < len(units):
            next_unit = units[i + 1]
        else:
            next_unit = None
        i += 1

    params["sec"] = 0
    details = " ".join(details)
    until = rsu(**params)
    usual = -1
    if next_unit:
        kwargs = {next_unit: 1}
        usual = gametime_to_realtime(**kwargs)
    return until, usual, details