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)
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)
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)
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)
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()
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]
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)
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
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