def is_valid_inheritance(obj): valid = (not inherits_from(obj, DefaultCharacter) and not inherits_from(obj, DefaultRoom) and not inherits_from(obj, DefaultExit)) if not valid: caller.msg("Invalid typeclass for %s" % (obj)) return valid
def func(self): """Execute the command.""" room = self.caller.location if not inherits_from(room, "typeclasses.rooms.VehicleRoom"): self.msg("|rIt seems you are not in a vehicle.|n") return vehicle = room.location # A VehicleRoom should be contained in a vehicle... but let's check if not inherits_from(vehicle, "typeclasses.vehicles.Vehicle"): self.msg("|rAre you, or are you not, in a vehicle? Hard to say.|n..") return # Make sure we are currently driving if vehicle.db.driver is not self.caller: self.msg("|gYou aren't driving {}.|n".format(vehicle.key)) return # Proceed to turn name = self.raw_string.strip().lower() if name.startswith("turn "): name = name[5:] direction = vehicle.db.direction infos = get_direction(name) if infos is None or infos["direction"] in (8, 9): self.msg("|gThe direction you specified is unknown.|n") return vehicle.db.expected_direction = infos["direction"] self.msg("You prepare to turn {} on the next open crossroad.".format(infos["name"]))
def find_location(self): #Check to see if the torch is in the hands (inventory) of player # or if the torch is in a a room. held_by = self.location room = None if inherits_from(held_by, 'characters.characters.Character'): room = held_by.location elif inherits_from(held_by, 'rooms.rooms.Room'): room = held_by return room, held_by
def parse(self): super(JsonPlayerCommand, self).parse() if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): # caller is an Object/Character self.character = self.caller self.caller = self.caller.player elif utils.inherits_from(self.caller, "evennia.players.players.DefaultPlayer"): # caller was already a Player self.character = self.caller.get_puppet(self.session) else: self.character = None
def at_say(self, message, **kwargs): """ Display the actual say (or whisper) of self. This hook should display the actual say/whisper of the object in its location. It should both alert the object (self) and its location that some text is spoken. The overriding of messages or `mapping` allows for simple customization of the hook without re-writing it completely. Args: message (str): The text to be conveyed by self. msg_self (str, optional): The message to echo to self. msg_location (str, optional): The message to echo to self's location. receiver (Object, optional): An eventual receiver of the message (by default only used by whispers). msg_receiver(str, optional): Specific message for receiver only. mapping (dict, optional): Additional mapping in messages. Kwargs: whisper (bool): If this is a whisper rather than a say. Kwargs can be used by other verbal commands in a similar way. Notes: Messages can contain {} markers, which must If used, `msg_self`, `msg_receiver` and `msg_location` should contain references to other objects between braces, the way `location.msg_contents` would allow. For instance: msg_self = 'You say: "{speech}"' msg_location = '{object} says: "{speech}"' msg_receiver = '{object} whispers: "{speech}"' The following mappings can be used in both messages: object: the object speaking. location: the location where object is. speech: the text spoken by self. You can use additional mappings if you want to add other information in your messages. """ super(EventCharacter, self).at_say(message, **kwargs) location = getattr(self, "location", None) location = location if location and inherits_from(location, "evennia.objects.objects.DefaultRoom") else None if location and not kwargs.get("whisper", False): location.callbacks.call("say", self, location, message, parameters=message) # Call the other characters' "say" event presents = [obj for obj in location.contents if obj is not self and inherits_from(obj, "evennia.objects.objects.DefaultCharacter")] for present in presents: present.callbacks.call("say", self, present, message, parameters=message)
def func(self): """Execute the command.""" room = self.caller.location if not inherits_from(room, "typeclasses.rooms.VehicleRoom"): self.msg("It seems you are not in a vehicle.") return vehicle = room.location # A VehicleRoom should be contained in a vehicle... but let's check if not inherits_from(vehicle, "typeclasses.vehicles.Vehicle"): self.msg( "Are you, or are you not, in a vehicle? Hard to say...") return # We can only control the steering wheel from the front seat if room is not vehicle.contents[0]: self.msg("You aren't in the front seat of {}.".format( vehicle.key)) return # Are we already driving this vehicle if vehicle.db.driver is self.caller: vehicle.db.driver = None self.msg("You let go of the steering wheel.") room.msg_contents("{driver} lets go of the streering wheel.", exclude=[self.caller], mapping=dict(driver=self.caller)) self.caller.cmdset.remove("commands.driving.DrivingCmdSet") return # Or someone else could be riving if vehicle.db.driver: self.msg("Someone else is at the wheel.") return # All is good, allow 'self.caller' to drive vehicle.db.driver = self.caller self.msg("You grip the steering wheel and stretch your legs " \ "to reach the pedals.") room.msg_contents("{driver} grips the steering wheel and stretches " \ "his legs to reach the pedals.", exclude=[self.caller], mapping=dict(driver=self.caller)) # If the vehicle is in a room, leave it. if vehicle.location is not None: vehicle.location = None self.msg("You start the engine and begin to drive {} " \ "on the street.".format(vehicle.key)) vehicle.msg_contents("The engine of {vehicle} starts.", exclude=[self.caller], mapping=dict(vehicle=vehicle)) self.caller.cmdset.add("commands.driving.DrivingCmdSet", permanent=True)
def at_before_say(self, message, **kwargs): """ Before the object says something. This hook is by default used by the 'say' and 'whisper' commands as used by this command it is called before the text is said/whispered and can be used to customize the outgoing text from the object. Returning `None` aborts the command. Args: message (str): The suggested say/whisper text spoken by self. Keyword Args: whisper (bool): If True, this is a whisper rather than a say. This is sent by the whisper command by default. Other verbal commands could use this hook in similar ways. receiver (Object): If set, this is a target for the say/whisper. Returns: message (str): The (possibly modified) text to be spoken. """ # First, try the location location = getattr(self, "location", None) location = (location if location and inherits_from( location, "evennia.objects.objects.DefaultRoom") else None) if location and not kwargs.get("whisper", False): allow = location.callbacks.call("can_say", self, location, message, parameters=message) message = location.callbacks.get_variable("message") if not allow or not message: return # Browse all the room's other characters for obj in location.contents: if obj is self or not inherits_from( obj, "evennia.objects.objects.DefaultCharacter"): continue allow = obj.callbacks.call("can_say", self, obj, message, parameters=message) message = obj.callbacks.get_variable("message") if not allow or not message: return return message
def isguest(accessing_obj, accessed_obj): """ a called lockstring with no args. This lock function will check if the caller is a guest or Helper+ """ if utils.inherits_from( accessing_obj, "typeclasses.characters.Character") and utils.inherits_from( accessing_obj.account, "evennia.accounts.accounts.DefaultGuest"): return True else: return False
def parse(self): """ We run the parent parser as usual, then fix the result """ super(MuxPlayerCommand, self).parse() if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): # caller is an Object/Character self.character = self.caller self.caller = self.caller.player elif utils.inherits_from(self.caller, "evennia.players.players.DefaultPlayer"): # caller was already a Player self.character = self.caller.get_puppet(self.session) else: self.character = None
def location_summary(self): """ You arrive at <location name>. <Person/Sentient> <is/are> here. You see <exit name> to the <exit direction>, and <exit name> to the <exit direction>. """ owner = self.owner location = owner.location characters = [] exits = [] char_str = '' exit_str = '' msg = '' if location is None: return "You have arrive nowhere." # Generate lists of characters and exits in the room. for i in location.contents: if inherits_from(i, 'characters.characters.Character') and i is not owner: characters.append(i) elif inherits_from(i, 'travel.exits.Exit'): exits.append(i) # Parse list of characters. if len(characters) > 1: char_list = gen_mec.objects_to_display_names(characters, owner) temp_list = [] for i in char_list: temp_list.append(f"|c{i}|n") char_str = gen_mec.comma_separated_string_list(temp_list) char_str = f"{char_str} are here. " elif len(characters) == 1: char_str = f'|c{characters[0].get_display_name(owner)}|n' char_str = f"{char_str} is here. " else: char_str = False # Parse list of exits. exit_str = self.exits_to_string(exits) # Generate final outgoing message. msg = f"You arrive at |310{location.get_display_name(owner)}|n. " msg = f"{msg}{'' if char_str == False else char_str}" # Characters string addition. if exit_str: msg = f"{msg}You see {exit_str}." else: msg = f"{msg}{self.no_exit_str}" return msg
def func(self): "implement the door functionality" if not self.args: self.caller.msg("Usage: open||close <door>") return door = self.caller.search(self.args) if not door: return if not inherits_from(door, SimpleDoor): self.caller.msg("This is not a door.") return if self.cmdstring == "open": if door.locks.check(self.caller, "traverse"): self.caller.msg("%s is already open." % door.key) else: door.setlock("traverse:true()") self.caller.msg("You open %s." % door.key) else: # close if not door.locks.check(self.caller, "traverse"): self.caller.msg("%s is already closed." % door.key) else: door.setlock("traverse:false()") self.caller.msg("You close %s." % door.key)
def _to_player(accessing_obj): "Helper function. Makes sure an accessing object is a player object" if utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject"): # an object. Convert to player. accessing_obj = accessing_obj.player return accessing_obj
def parse_input(self, inp): """ Parse the input to figure out the size of the data, how many pages it consist of and pick the correct paginator mechanism. Override this if you want to support a new type of input. Each initializer should set self._paginator and optionally self._page_formatter for properly handling the input data. """ if inherits_from(inp, "evennia.utils.evtable.EvTable"): # an EvTable self.init_evtable(inp) elif isinstance(inp, QuerySet): # a queryset self.init_queryset(inp) elif isinstance(inp, Paginator): self.init_django_paginator(inp) elif not isinstance(inp, str): # anything else not a str self.init_iterable(inp) elif "\f" in inp: # string with \f line-break markers in it self.init_f_str(inp) else: # a string self.init_str(inp)
def func(self): caller = self.caller if self.args is None or not utils.dbref(self.args): caller.msg("A puzzle recipe's #dbref must be specified") return puzzle = search.search_script(self.args) if not puzzle or not inherits_from(puzzle[0], PuzzleRecipe): caller.msg("Invalid puzzle %r" % (self.args)) return puzzle = puzzle[0] caller.msg("Puzzle Recipe %s(%s) '%s' found.\nSpawning %d parts ..." % (puzzle.name, puzzle.dbref, puzzle.db.puzzle_name, len(puzzle.db.parts))) for proto_part in puzzle.db.parts: part = spawn(proto_part)[0] caller.msg( "Part %s(%s) spawned and placed at %s(%s)" % (part.name, part.dbref, part.location, part.location.dbref)) part.tags.add(puzzle.db.puzzle_name, category=_PUZZLES_TAG_CATEGORY) part.db.puzzle_name = puzzle.db.puzzle_name caller.msg("Puzzle armed |gsuccessfully|n.")
def at_traverse(self, traversing_object, target_location): """ This hook is responsible for handling the actual traversal, normally by calling `traversing_object.move_to(target_location)`. It is normally only implemented by Exit objects. If it returns False (usually because `move_to` returned False), `at_after_traverse` below should not be called and instead `at_failed_traverse` should be called. Args: traversing_object (Object): Object traversing us. target_location (Object): Where target is going. """ is_character = inherits_from(traversing_object, DefaultCharacter) if is_character: allow = self.callbacks.call("can_traverse", traversing_object, self, self.location) if not allow: return super().at_traverse(traversing_object, target_location) # After traversing if is_character: self.callbacks.call("traverse", traversing_object, self, self.location, self.destination)
def create_exit(self, exit_name, location, destination, exit_aliases=None, typeclass=None): """ Simple wrapper for the default CmdOpen.create_exit """ # create a new exit as normal new_exit = super(CmdOpen, self).create_exit(exit_name, location, destination, exit_aliases=exit_aliases, typeclass=typeclass) if hasattr(self, "return_exit_already_created"): # we don't create a return exit if it was already created (because # we created a door) del self.return_exit_already_created return new_exit if inherits_from(new_exit, SimpleDoor): # a door - create its counterpart and make sure to turn off the default # return-exit creation of CmdOpen self.caller.msg( "Note: A door-type exit was created - ignored eventual custom return-exit type." ) self.return_exit_already_created = True back_exit = self.create_exit(exit_name, destination, location, exit_aliases=exit_aliases, typeclass=typeclass) new_exit.db.return_exit = back_exit back_exit.db.return_exit = new_exit return new_exit
def change_vital(self, attr_type, by=0, update=True): """ helper function to change current vital value based on max and current value. attr_type supplied must be valid key that points to attributes on AttributeHandler, and must be a valid VitalAttribute object """ attr = self.__getattr__(attr_type) if not inherits_from(attr, 'world.attributes.VitalAttribute'): raise NotImplementedError( 'change_vital function doesnt support changing attribute that are NOT VitalAttributes' ) if attr.cur == attr.max: return cur = attr.cur cur += int((by * attr.rate)) # apply rate if cur < 0: cur = 0 if cur > attr.max: cur = attr.max attr.cur = cur self.__setattr__(attr_type, attr) if update: self.update()
def check_target(self, target, caller): """ Determines if a target is valid. """ from evennia.utils.utils import inherits_from return inherits_from(target, self.valid_typeclass_path)
def delete_contents(obj, exclude=[], do_not_delete_chars=True): """ DANGEROUS OPERATION recursively delete objs contained within itself make sure to put appropriate typeclasses in exclude, you can delete yourself if you are not careful.. for example. By default this skips over character typeclasses, but this can be overwritten. # bad!! delete_contents(self.caller.location, do_not_delete_chars=False) # whoopsie.. doooh # good!! delete_contents(self.caller.location) """ for o in obj.contents: if o in exclude or (do_not_delete_chars and inherits_from( o, 'typeclasses.characters.Character')): continue if o.contents: delete_contents(obj=o) o.delete()
def _to_account(accessing_obj): "Helper function. Makes sure an accessing object is an account object" if utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject"): # an object. Convert to account. accessing_obj = accessing_obj.account return accessing_obj
def func(self): try: item_name, value = self.args.split() except: self.caller.msg("Syntax: price <item> <value>") return sellable_items = [ware for ware in self.caller.location.contents if inherits_from(ware, Item)] item = self.caller.search(item_name, location=self.caller.location, candidates=sellable_items, quiet=True) if len(item) > 1: self.caller.msg("Which '{}' did you mean?".format(item_name)) return elif len(item): item = item[0] try: value = int(value) except: self.caller.msg("Your price can only be an integer.") return item.db.value = value self.caller.msg("You set the price of {} to {}.".format( item, as_price(value) )) else: self.caller.msg("There is no '{}' available here to sell.".format(item_name))
def at_traverse(self, traversing_object, target_location): """ This hook is responsible for handling the actual traversal, normally by calling `traversing_object.move_to(target_location)`. It is normally only implemented by Exit objects. If it returns False (usually because `move_to` returned False), `at_after_traverse` below should not be called and instead `at_failed_traverse` should be called. Args: traversing_object (Object): Object traversing us. target_location (Object): Where target is going. """ is_character = inherits_from(traversing_object, DefaultCharacter) if is_character: allow = self.callbacks.call("can_traverse", traversing_object, self, self.location) if not allow: return super(EventExit, self).at_traverse(traversing_object, target_location) # After traversing if is_character: self.callbacks.call("traverse", traversing_object, self, self.location, self.destination)
def func(self): try: item_name, value = self.args.split() except: self.caller.msg("Syntax: price <item> <value>") return sellable_items = [ ware for ware in self.caller.location.contents if inherits_from(ware, Item) ] item = self.caller.search(item_name, location=self.caller.location, candidates=sellable_items, quiet=True) if len(item) > 1: self.caller.msg("Which '{}' did you mean?".format(item_name)) return elif len(item): item = item[0] try: value = int(value) except: self.caller.msg("Your price can only be an integer.") return item.db.value = value self.caller.msg("You set the price of {} to {}.".format( item, as_price(value))) else: self.caller.msg( "There is no '{}' available here to sell.".format(item_name))
def add(self, cmd): """ Add a command, a list of commands or a cmdset to this cmdset. Note that if cmd already exists in set, it will replace the old one (no priority checking etc at this point; this is often used to overload default commands). If cmd is another cmdset class or -instance, the commands of that command set is added to this one, as if they were part of the original cmdset definition. No merging or priority checks are made, rather later added commands will simply replace existing ones to make a unique set. """ if inherits_from(cmd, "evennia.commands.cmdset.CmdSet"): # cmd is a command set so merge all commands in that set # to this one. We raise a visible error if we created # an infinite loop (adding cmdset to itself somehow) try: cmd = self._instantiate(cmd) except RuntimeError: string = "Adding cmdset %(cmd)s to %(class)s lead to an " string += "infinite loop. When adding a cmdset to another, " string += "make sure they are not themself cyclically added to " string += "the new cmdset somewhere in the chain." raise RuntimeError( _(string) % { "cmd": cmd, "class": self.__class__ }) cmds = cmd.commands elif is_iter(cmd): cmds = [self._instantiate(c) for c in cmd] else: cmds = [self._instantiate(cmd)] commands = self.commands system_commands = self.system_commands for cmd in cmds: # add all commands if not hasattr(cmd, 'obj'): cmd.obj = self.cmdsetobj try: ic = commands.index(cmd) commands[ic] = cmd # replace except ValueError: commands.append(cmd) # extra run to make sure to avoid doublets self.commands = list(set(commands)) #print "In cmdset.add(cmd):", self.key, cmd # add system_command to separate list as well, # for quick look-up if cmd.key.startswith("__"): try: ic = system_commands.index(cmd) system_commands[ic] = cmd # replace except ValueError: system_commands.append(cmd)
def parse(self): if not self.args: self.msg("Who would you like to sniff? Usage: 'sniff <character>'") else: self.target = self.caller.search(self.args.strip()) if not inherits_from(self.target, Character): self.target = None self.msg("You can only sniff characters!")
def parse(self): if not self.args: self.msg("What would you like to bury? Usage: 'bury <bones>'") else: self.target = self.caller.search(self.args.strip()) if not inherits_from(self.target, Bones): self.target = None self.msg("You can only bury bones!")
def get_wares(caller): """ Gets items located in the designated storeroom of the caller's location with a price assigned Only descendants of the Item typeclass are eligible for sale """ return [ ware for ware in caller.location.db.storeroom.contents if inherits_from(ware, Item) and ware.db.value ]
def add(self, cmd): """ Add a command, a list of commands or a cmdset to this cmdset. Note that if cmd already exists in set, it will replace the old one (no priority checking etc at this point; this is often used to overload default commands). If cmd is another cmdset class or -instance, the commands of that command set is added to this one, as if they were part of the original cmdset definition. No merging or priority checks are made, rather later added commands will simply replace existing ones to make a unique set. """ if inherits_from(cmd, "evennia.commands.cmdset.CmdSet"): # cmd is a command set so merge all commands in that set # to this one. We raise a visible error if we created # an infinite loop (adding cmdset to itself somehow) try: cmd = self._instantiate(cmd) except RuntimeError: string = "Adding cmdset %(cmd)s to %(class)s lead to an " string += "infinite loop. When adding a cmdset to another, " string += "make sure they are not themself cyclically added to " string += "the new cmdset somewhere in the chain." raise RuntimeError(_(string) % {"cmd": cmd, "class": self.__class__}) cmds = cmd.commands elif is_iter(cmd): cmds = [self._instantiate(c) for c in cmd] else: cmds = [self._instantiate(cmd)] commands = self.commands system_commands = self.system_commands for cmd in cmds: # add all commands if not hasattr(cmd, 'obj'): cmd.obj = self.cmdsetobj try: ic = commands.index(cmd) commands[ic] = cmd # replace except ValueError: commands.append(cmd) # extra run to make sure to avoid doublets self.commands = list(set(commands)) #print "In cmdset.add(cmd):", self.key, cmd # add system_command to separate list as well, # for quick look-up if cmd.key.startswith("__"): try: ic = system_commands.index(cmd) system_commands[ic] = cmd # replace except ValueError: system_commands.append(cmd)
def create_obj(self, args): """ Create an object, given its new key. When using the |w@new obj|n command, you have to specify the name of the object to create. This is a mandatory argument. Other options: |y-p|n (|y--prototype|n): the object prototype. |y-l|n (|y--location|n): the object's location. Examples: |w@new obj an apple|n |w@new obj an apple -p red_apple|n |w@new obj a lovely apple -t red_apple -l #2|n Objects don't necessarily have a prototype, even though it is very common. You can set their prototype by the |y-p|n option. Using the optional location, you can easily spawn an object in a container (owned by a player, for instance) or on the ground of a distant room. If you don't specify this option, the object will be spawned at your feet. """ key = " ".join(args.key).strip().lower() if not key: self.msg("|rSpecify at least a name for this object prototype.|n") return # Search for the optional prototype prototype = None if args.prototype: prototype = self.caller.search(args.prototype, global_search=True, use_dbref=True) if not prototype: return elif not inherits_from(prototype, "typeclasses.prototypes.PObj"): self.msg("{} isn't a valid object prototype.".format(prototype.get_display_name(self.caller))) return # Search for the optional location location = self.caller.location if args.location: location = self.caller.search(args.location, global_search=True, use_dbref=True) if not location: return # Create the new Obj if prototype: obj = prototype.create(key=key, location=location) self.msg("The object {} was created on prototype {}.".format(obj.get_display_name(self.caller), prototype.key)) else: obj = create_object("typeclasses.objects.Object", key=key, location=location) self.msg("The object {} was successfully created without a prototype.".format(obj.get_display_name(self.caller))) self.msg("It was spawned in: {}.".format(location.get_display_name(self.caller)))
def sheet(request, object_id): object_id = '#' + object_id try: character = object_search(object_id)[0] except IndexError: raise Http404("I couldn't find a character with that ID.") if not inherits_from(character, settings.BASE_CHARACTER_TYPECLASS): raise Http404("I couldn't find a character with that ID. " "Found something else instead.") return render(request, 'character/sheet.html', {'character': character})
def sheet(request, char_name): name = char_name try: character = search_object(name)[0] except IndexError: raise Http404("I couldn't find a character with that name.") if not inherits_from(character, settings.BASE_CHARACTER_TYPECLASS): raise Http404("I couldn't find a character with that name." "Found something else instead.") return render(request, 'charinfo/sheet.html', {'character': character})
def func(self): """Execute the command.""" room = self.caller.location if not inherits_from(room, "typeclasses.rooms.VehicleRoom"): self.msg("|rIt seems you are not in a vehicle.|n") return vehicle = room.location # A VehicleRoom should be contained in a vehicle... but let's check if not inherits_from(vehicle, "typeclasses.vehicles.Vehicle"): self.msg("|rAre you, or are you not, in a vehicle? Hard to say.|n..") return # Make sure we are currently driving if vehicle.db.driver is not self.caller: self.msg("|gYou aren't driving {}|n.".format(vehicle.key)) return # If the vehicle is parked, un-park it if vehicle.location is not None: vehicle.location = None # Change the speed current = vehicle.db.speed desired = self.args.strip() try: desired = int(desired) assert desired >= 0 except (ValueError, AssertionError): self.msg("|rSorry, this is not a valid speed.|n") else: vehicle.db.desired_speed = desired self.msg("You're now planning to drive at {} MPH.".format(desired)) # Display a message to the vehicle if the speed changes if current < desired: vehicle.msg_contents("{} begins to speed up.".format( vehicle.key)) elif current > desired: vehicle.msg_contents("{} begins to slow down.".format( vehicle.key))
def add(self, cmdset, emit_to_obj=None, permanent=False, default_cmdset=False): """ Add a cmdset to the handler, on top of the old ones, unless it is set as the default one (it will then end up at the bottom of the stack) Args: cmdset (CmdSet or str): Can be a cmdset object or the python path to such an object. emit_to_obj (Object, optional): An object to receive error messages. permanent (bool, optional): This cmdset will remain across a server reboot. default_cmdset (Cmdset, optional): Insert this to replace the default cmdset position (there is only one such position, always at the bottom of the stack). Notes: An interesting feature of this method is if you were to send it an *already instantiated cmdset* (i.e. not a class), the current cmdsethandler's obj attribute will then *not* be transferred over to this already instantiated set (this is because it might be used elsewhere and can cause strange effects). This means you could in principle have the handler launch command sets tied to a *different* object than the handler. Not sure when this would be useful, but it's a 'quirk' that has to be documented. """ if not (isinstance(cmdset, str) or utils.inherits_from(cmdset, CmdSet)): string = _("Only CmdSets can be added to the cmdsethandler!") raise Exception(string) if callable(cmdset): cmdset = cmdset(self.obj) elif isinstance(cmdset, str): # this is (maybe) a python path. Try to import from cache. cmdset = self._import_cmdset(cmdset) if cmdset and cmdset.key != "_CMDSET_ERROR": cmdset.permanent = permanent if permanent and cmdset.key != "_CMDSET_ERROR": # store the path permanently storage = self.obj.cmdset_storage or [""] if default_cmdset: storage[0] = cmdset.path else: storage.append(cmdset.path) self.obj.cmdset_storage = storage if default_cmdset: self.cmdset_stack[0] = cmdset else: self.cmdset_stack.append(cmdset) self.update()
def at_before_say(self, message, **kwargs): """ Before the object says something. This hook is by default used by the 'say' and 'whisper' commands as used by this command it is called before the text is said/whispered and can be used to customize the outgoing text from the object. Returning `None` aborts the command. Args: message (str): The suggested say/whisper text spoken by self. Kwargs: whisper (bool): If True, this is a whisper rather than a say. This is sent by the whisper command by default. Other verbal commands could use this hook in similar ways. receiver (Object): If set, this is a target for the say/whisper. Returns: message (str): The (possibly modified) text to be spoken. """ # First, try the location location = getattr(self, "location", None) location = location if location and inherits_from(location, "evennia.objects.objects.DefaultRoom") else None if location and not kwargs.get("whisper", False): allow = location.callbacks.call("can_say", self, location, message, parameters=message) message = location.callbacks.get_variable("message") if not allow or not message: return # Browse all the room's other characters for obj in location.contents: if obj is self or not inherits_from(obj, "evennia.objects.objects.DefaultCharacter"): continue allow = obj.callbacks.call("can_say", self, obj, message, parameters=message) message = obj.callbacks.get_variable("message") if not allow or not message: return return message
def modify_vital(self, attr_type, by=0): """ same as change_vital except adds it to modifier list attr_type must be a vald key on AttributeHandler """ attr = self.__getattr__(attr_type) if not inherits_from(attr, 'world.attributes.VitalAttribute'): raise NotImplementedError( 'modify_vital function doesnt support changing attribute that are NOT VitalAttributes' ) # # Nifty piece of code here, instead of tracking individual # objects that modify vitals, instead I turned it into a math # game. If your mod list looks like this: # # mod = [1,1,-2,5], your a cum modifier of 5 # # when you add a modifer to the list, instead of it growing # forever, it will first try to find the oppositve value # and attempt to remove that, if it can't find it, it will a # add it. For example # # health_mod = [2,2,-3,5,10] (16) # modify_vital('health', by=3) # adding three to modifer # # results would be: # # health_mod = [2,2,5,10] (19) # it found a opposite of 3 (-3) # and removed that. # modify_vital('health', by=-10) # deduct 10 from mod # health_mod = [2,2,5] (9) # doing it again though: modify_vital('health', by=-10) # health_mod = [2,2,5,-10] (-1) # # if by != 0: # attempt to remove a positive equiv # mod, if it doesn't exist add it tmp = -by try: idx = attr.mod.index(tmp) attr.mod.remove(attr.mod[idx]) except ValueError: # it doesn't exist, just add it to list attr.mod.append(by) self.__setattr__(attr_type, attr) self.update()
def func(self): caller = self.caller if not self.args: caller.msg("Usage: attack <target>") return target = caller.search(self.args) if not target: return # for combat against yourself if target.id == caller.id: caller.msg("Combat against yourself is not supported.") return if not inherits_from(target, 'typeclasses.characters.Character'): caller.msg("Combat against {target} is not supported.".format( target=target.get_display_name(caller))) return if target.ndb.no_attack: caller.msg("You cannot attack {target} at this time.".format( target=target.get_display_name(caller) )) return if caller.location.tags.get('no_attack', None, category='flags'): caller.msg("Combat is not allowed in this location.") return # set up combat if target.ndb.combat_handler: # target is already in combat - join it target.ndb.combat_handler.add_character(caller) target.ndb.combat_handler.combat_msg( "{actor} joins combat!" , actor=caller ) else: # create a new combat handler chandler = create_script("typeclasses.combat_handler.CombatHandler") chandler.add_character(caller) chandler.add_character(target) caller.msg("You attack {}!".format( target.get_display_name(caller))) target.msg("{} attacks you!".format( caller.get_display_name(target))) for char in chandler.db.characters.values(): char.execute_cmd("look") chandler.msg_all("The turn begins. Declare your actions!")
def func(self): "implement the ooc look command" if MULTISESSION_MODE < 2: # only one character allowed string = "You are out-of-character (OOC).\nUse {w@ic{n to get back into the game." self.msg(string) return if utils.inherits_from(self.caller, "evennia.objects.objects.Object"): # An object of some type is calling. Use default look instead. super(CmdOOCLook, self).func() elif self.args: self.look_target() else: self.no_look_target()
def add(self, cmdset, emit_to_obj=None, permanent=False, default_cmdset=False): """ Add a cmdset to the handler, on top of the old ones, unless it is set as the default one (it will then end up at the bottom of the stack) Args: cmdset (CmdSet or str): Can be a cmdset object or the python path to such an object. emit_to_obj (Object, optional): An object to receive error messages. permanent (bool, optional): This cmdset will remain across a server reboot. default_cmdset (Cmdset, optional): Insert this to replace the default cmdset position (there is only one such position, always at the bottom of the stack). Notes: An interesting feature of this method is if you were to send it an *already instantiated cmdset* (i.e. not a class), the current cmdsethandler's obj attribute will then *not* be transferred over to this already instantiated set (this is because it might be used elsewhere and can cause strange effects). This means you could in principle have the handler launch command sets tied to a *different* object than the handler. Not sure when this would be useful, but it's a 'quirk' that has to be documented. """ if not (isinstance(cmdset, basestring) or utils.inherits_from(cmdset, CmdSet)): string = _("Only CmdSets can be added to the cmdsethandler!") raise Exception(string) if callable(cmdset): cmdset = cmdset(self.obj) elif isinstance(cmdset, basestring): # this is (maybe) a python path. Try to import from cache. cmdset = self._import_cmdset(cmdset) if cmdset and cmdset.key != '_CMDSET_ERROR': cmdset.permanent = permanent if permanent and cmdset.key != '_CMDSET_ERROR': # store the path permanently storage = self.obj.cmdset_storage or [""] if default_cmdset: storage[0] = cmdset.path else: storage.append(cmdset.path) self.obj.cmdset_storage = storage if default_cmdset: self.cmdset_stack[0] = cmdset else: self.cmdset_stack.append(cmdset) self.update()
def find_by_name(search): search = search.strip().split(';', 1)[0] keyquery = Q(db_key__istartswith=search) aliasquery = Q(db_tags__db_key__istartswith=search, db_tags__db_tagtype__iexact='alias') results = ObjectDB.objects.filter(keyquery | aliasquery).distinct() nresults = results.count() if nresults: # convert multiple results to typeclasses. results = [result for result in results] room_typeclass = settings.BASE_ROOM_TYPECLASS # Narrow results to only room types. results = [obj for obj in results if inherits_from(obj, room_typeclass)] return results
def _searcher(self, room, depth): """Searches surrounding rooms recursively for an object""" if not room or len(self.visited) >= self.maxrooms: # Stop search if the search has ... return False # ... visited too many rooms or has come across a bad room. loc = self.caller.location this = self.obj.get_display_name(self.caller) target = self.target.get_display_name(self.caller) # first, record searching here self.visited.append(room) # End search either when the item is found... if self.target in ([room] + room.contents + sum([each.contents for each in room.contents], [])): if depth == 0: here = loc.get_display_name(self.caller) loc.msg_contents("{} finds {} right here in {}!".format(this, target, here)) else: way = self.direction.get_display_name(self.caller, mxp=self.direction.key) plural = 's' if depth != 1 else '' if inherits_from(self.direction, 'typeclasses.rooms.Room'): dir_convert = {'n': 'north', 's': 'south', 'e': 'east', 'w': 'west', 'nw': 'northwest', 'se': 'southeast', 'ne': 'northeast', 'sw': 'southwest', 'u': 'up', 'd': 'down'} directions = {k: v for k, v in loc.db.exits.items() if v == self.direction} way = '|lc{0}|lt|530{1}|n|le'.format(directions.keys()[0], dir_convert[directions.keys()[0]]) loc.msg_contents("{} detects {} {} step{} away. Go {}".format(this, target, depth, plural, way)) print('Depth and Number of rooms searched: ', depth, len(self.visited)) return True # or searched to `maxdepth` distance if depth >= self.maxdepth: return False # Target not in the current room, scan through the exits and check them, # skipping rooms we've already visited exits = [exit for exit in room.exits if exit.destination not in self.visited] orange_destinations = [room.db.exits[exit] for exit in room.db.exits.keys() if room.db.exits[exit] not in self.visited] if room.db.exits else [] for next in exits: if depth == 0: # we only want to return the exit out of the current room self.direction = next if self._searcher(next.destination, depth + 1): # if we found the object, stop searching return True for next in orange_destinations: if depth == 0: # we only want to return the orange exit out of the current room self.direction = next if self._searcher(next, depth + 1): # if we found the object, stop searching return True # Check all the exits, no result return False
def add(self, cmdset, emit_to_obj=None, permanent=False): """ Add a cmdset to the handler, on top of the old ones. Default is to not make this permanent, i.e. the set will not survive a server reset. cmdset - can be a cmdset object or the python path to such an object. emit_to_obj - an object to receive error messages. permanent - this cmdset will remain across a server reboot Note: An interesting feature of this method is if you were to send it an *already instantiated cmdset* (i.e. not a class), the current cmdsethandler's obj attribute will then *not* be transferred over to this already instantiated set (this is because it might be used elsewhere and can cause strange effects). This means you could in principle have the handler launch command sets tied to a *different* object than the handler. Not sure when this would be useful, but it's a 'quirk' that has to be documented. """ if not (isinstance(cmdset, basestring) or utils.inherits_from(cmdset, CmdSet)): string = _("Only CmdSets can be added to the cmdsethandler!") raise Exception(string) if callable(cmdset): cmdset = cmdset(self.obj) elif isinstance(cmdset, basestring): # this is (maybe) a python path. Try to import from cache. cmdset = self._import_cmdset(cmdset) if cmdset and cmdset.key != "_CMDSET_ERROR": if permanent and cmdset.key != "_CMDSET_ERROR": # store the path permanently cmdset.permanent = True storage = self.obj.cmdset_storage if not storage: storage = ["", cmdset.path] else: storage.append(cmdset.path) self.obj.cmdset_storage = storage else: cmdset.permanent = False self.cmdset_stack.append(cmdset) self.update()
def func(self): caller = self.caller args = self.args if (not len(args)): caller.msg(u"Не указано какое вещество употребить.") return False substance = caller.search(args, location=caller, global_search = False, nofound_string = u"Такого вещества у тебя нет") if (not substance): return False if (utils.inherits_from(substance, 'typeclasses.substance.Substance')): caller.msg(u"Ты употребил %s" % substance.name) substance.use(caller) else: # TODO переработать, использовать можно не только вещества caller.msg(u"Ты не можешь употребить %s" % substance.name) return True
def create_exit(self, exit_name, location, destination, exit_aliases=None, typeclass=None): """ Simple wrapper for the default CmdOpen.create_exit """ # create a new exit as normal new_exit = super(CmdOpen, self).create_exit(exit_name, location, destination, exit_aliases=exit_aliases, typeclass=typeclass) if hasattr(self, "return_exit_already_created"): # we don't create a return exit if it was already created (because # we created a door) del self.return_exit_already_created return new_exit if inherits_from(new_exit, SimpleDoor): # a door - create its counterpart and make sure to turn off the default # return-exit creation of CmdOpen self.caller.msg("Note: A door-type exit was created - ignored eventual custom return-exit type.") self.return_exit_already_created = True back_exit = self.create_exit(exit_name, destination, location, exit_aliases=exit_aliases, typeclass=typeclass) new_exit.db.return_exit = back_exit back_exit.db.return_exit = new_exit return new_exit
def add_default(self, cmdset, emit_to_obj=None, permanent=True): """ Add a new default cmdset. If an old default existed, it is replaced. If permanent is set, the set will survive a reboot. cmdset - can be a cmdset object or the python path to an instance of such an object. emit_to_obj - an object to receive error messages. permanent - save cmdset across reboots See also the notes for self.add(), which applies here too. """ if callable(cmdset): if not utils.inherits_from(cmdset, CmdSet): string = _("Only CmdSets can be added to the cmdsethandler!") raise Exception(string) cmdset = cmdset(self.obj) elif isinstance(cmdset, basestring): # this is (maybe) a python path. Try to import from cache. cmdset = self._import_cmdset(cmdset) if cmdset and cmdset.key != "_CMDSET_ERROR": if self.cmdset_stack: self.cmdset_stack[0] = cmdset self.mergetype_stack[0] = cmdset.mergetype else: self.cmdset_stack = [cmdset] self.mergetype_stack = [cmdset.mergetype] if permanent and cmdset.key != "_CMDSET_ERROR": cmdset.permanent = True storage = self.obj.cmdset_storage if storage: storage[0] = cmdset.path else: storage = [cmdset.path] self.obj.cmdset_storage = storage else: cmdset.permanent = False self.update()
def perm(accessing_obj, accessed_obj, *args, **kwargs): """ The basic permission-checker. Ignores case. Usage: perm(<permission>) where <permission> is the permission accessing_obj must have in order to pass the lock. If the given permission is part of settings.PERMISSION_HIERARCHY, permission is also granted to all ranks higher up in the hierarchy. If accessing_object is an Object controlled by an Account, the permissions of the Account is used unless the Attribute _quell is set to True on the Object. In this case however, the LOWEST hieararcy-permission of the Account/Object-pair will be used (this is order to avoid Accounts potentially escalating their own permissions by use of a higher-level Object) """ # this allows the perm_above lockfunc to make use of this function too gtmode = kwargs.pop("_greater_than", False) try: permission = args[0].lower() perms_object = [p.lower() for p in accessing_obj.permissions.all()] except (AttributeError, IndexError): return False if utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject") and accessing_obj.account: account = accessing_obj.account # we strip eventual plural forms, so Builders == Builder perms_account = [p.lower().rstrip("s") for p in account.permissions.all()] is_quell = account.attributes.get("_quell") if permission in _PERMISSION_HIERARCHY: # check hierarchy without allowing escalation obj->account hpos_target = _PERMISSION_HIERARCHY.index(permission) hpos_account = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_account] hpos_account = hpos_account and hpos_account[-1] or -1 if is_quell: hpos_object = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_object] hpos_object = hpos_object and hpos_object[-1] or -1 if gtmode: return hpos_target < min(hpos_account, hpos_object) else: return hpos_target <= min(hpos_account, hpos_object) elif gtmode: return hpos_target < hpos_account else: return hpos_target <= hpos_account elif not is_quell and permission in perms_account: # if we get here, check account perms first, otherwise # continue as normal return True if permission in perms_object: # simplest case - we have direct match return True if permission in _PERMISSION_HIERARCHY: # check if we have a higher hierarchy position hpos_target = _PERMISSION_HIERARCHY.index(permission) return any(1 for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_object and hpos_target < hpos) return False
def perm(accessing_obj, accessed_obj, *args, **kwargs): """ The basic permission-checker. Ignores case. Usage: perm(<permission>) where <permission> is the permission accessing_obj must have in order to pass the lock. If the given permission is part of settings.PERMISSION_HIERARCHY, permission is also granted to all ranks higher up in the hierarchy. If accessing_object is an Object controlled by an Account, the permissions of the Account is used unless the Attribute _quell is set to True on the Object. In this case however, the LOWEST hieararcy-permission of the Account/Object-pair will be used (this is order to avoid Accounts potentially escalating their own permissions by use of a higher-level Object) """ # this allows the perm_above lockfunc to make use of this function too try: permission = args[0].lower() perms_object = accessing_obj.permissions.all() except (AttributeError, IndexError): return False gtmode = kwargs.pop("_greater_than", False) is_quell = False account = (utils.inherits_from(accessing_obj, "evennia.objects.objects.DefaultObject") and accessing_obj.account) # check object perms (note that accessing_obj could be an Account too) perms_account = [] if account: perms_account = account.permissions.all() is_quell = account.attributes.get("_quell") # Check hirarchy matches; handle both singular/plural forms in hierarchy hpos_target = None if permission in _PERMISSION_HIERARCHY: hpos_target = _PERMISSION_HIERARCHY.index(permission) if permission.endswith('s') and permission[:-1] in _PERMISSION_HIERARCHY: hpos_target = _PERMISSION_HIERARCHY.index(permission[:-1]) if hpos_target is not None: # hieratchy match hpos_account = -1 hpos_object = -1 if account: # we have an account puppeting this object. We must check what perms it has perms_account_single = [p[:-1] if p.endswith('s') else p for p in perms_account] hpos_account = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_account_single] hpos_account = hpos_account and hpos_account[-1] or -1 if not account or is_quell: # only get the object-level perms if there is no account or quelling perms_object_single = [p[:-1] if p.endswith('s') else p for p in perms_object] hpos_object = [hpos for hpos, hperm in enumerate(_PERMISSION_HIERARCHY) if hperm in perms_object_single] hpos_object = hpos_object and hpos_object[-1] or -1 if account and is_quell: # quell mode: use smallest perm from account and object if gtmode: return hpos_target < min(hpos_account, hpos_object) else: return hpos_target <= min(hpos_account, hpos_object) elif account: # use account perm if gtmode: return hpos_target < hpos_account else: return hpos_target <= hpos_account else: # use object perm if gtmode: return hpos_target < hpos_object else: return hpos_target <= hpos_object else: # no hierarchy match - check direct matches if account: # account exists, check it first unless quelled if is_quell and permission in perms_object: return True elif permission in perms_account: return True elif permission in perms_object: return True return False
def func(self): caller = self.caller if not self.args: self._usage() return # split the target off from the first list argument targs = tuple(self.lhslist[0].rsplit(' ', 1)) if len(targs) == 1 or \ (len(targs) > 1 and targs[1].lower() not in ALL_SKILLS): target, self.lhslist[0] = self.lhslist[0], '' else: target, self.lhslist[0] = targs char = caller.search(target, location=caller.location, typeclass='typeclasses.characters.NPC') if not char: return # the inherits_from check below is necessary due to an issue # with search() on the ContripRPObject class if not inherits_from(char, 'typeclasses.characters.NPC'): caller.msg("Could not find NPC: '{}'".format(target)) return if self.rhs: # handle assignments if not all((x.isdigit() for x in self.rhslist)): caller.msg('Assignment values must be numeric.') return if len(self.lhslist) != len(self.rhslist): caller.msg('Incorrect number of assignment values.') return for i in xrange(len(self.lhslist)): if self.lhslist[i].lower() in char.skills.all: char.skills[self.lhslist[i].lower()].base = \ min(max(int(self.rhslist[i]), 0), 10) # enforce {0, 10} bounds caller.msg('Skill "{}" set to {} for {}'.format( self.lhslist[i].lower(), min(max(int(self.rhslist[i]), 0), 10), char.sdesc.get())) else: caller.msg('Invalid skill: "{}"'.format(self.lhslist[i])) # display traits data = [] if any(self.lhslist): skills = [s.lower() for s in self.lhslist if s.lower() in char.skills.all] else: skills = char.skills.all if len(skills) < 3: [data.append([self._format_skill_3col(char.skills[s])]) for s in skills] else: [data.append([self._format_skill_3col(char.skills[s]) for s in skills[i::3]]) for i in xrange(3)] table = EvTable(header=False, table=data) caller.msg(unicode(table))
def func(self): caller = self.caller if not self.args: self._usage() return # split off the target from the first item of lhslist targs = tuple(self.lhslist[0].rsplit(' ', 1)) if len(targs) == 1 or \ (len(targs) > 1 and targs[1].upper() not in ALL_TRAITS): target, self.lhslist[0] = self.lhslist[0], '' else: target, self.lhslist[0] = targs # search for the target NPC char = caller.search(target, location=caller.location, typeclass='typeclasses.characters.NPC') if not char: return # the inherits_from check below is necessary due to an issue # with search() on the ContripRPObject class if not inherits_from(char, 'typeclasses.characters.NPC'): caller.msg("Could not find NPC: '{}'".format(target)) return if self.rhs: if not all((x.isdigit() for x in self.rhslist)): caller.msg('Assignment values must be numeric.') return if len(self.lhslist) != len(self.rhslist): caller.msg('Incorrect number of assignment values.') return for i in xrange(len(self.lhslist)): if self.lhslist[i].upper() in char.traits.all: char.traits[self.lhslist[i].upper()].base = \ min(max(int(self.rhslist[i]), 0), 10) caller.msg('Trait "{}" set to {} for {}'.format( self.lhslist[i].upper(), min(max(int(self.rhslist[i]), 0), 10), char.sdesc.get())) else: caller.msg('Invalid trait: "{}"'.format(self.lhslist[i])) # display traits data = [] if any(self.lhslist): traits = [t.upper() for t in self.lhslist if t.upper() in char.traits.all] else: traits = char.traits.all if len(traits) == 0: return elif 0 < len(traits) < 3: [data.append([self._format_trait_3col(char.traits[t])]) for t in traits] else: [data.append([self._format_trait_3col(char.traits[t]) for t in traits[i::3]]) for i in xrange(3)] table = EvTable(header=False, table=data) caller.msg(unicode(table))
def get_wares(caller): """ Gets items located in the designated storeroom of the caller's location with a price assigned Only descendants of the Item typeclass are eligible for sale """ return [ware for ware in caller.location.db.storeroom.contents if inherits_from(ware, Item) and ware.db.value]
def parse(self): """ This method is called by the cmdhandler once the command name has been identified. It creates a new set of member variables that can be later accessed from self.func() (see below) The following variables are available for our use when entering this method (from the command definition, and assigned on the fly by the cmdhandler): self.key - the name of this command ('look') self.aliases - the aliases of this cmd ('l') self.permissions - permission string for this command self.help_category - overall category of command self.caller - the object calling this command self.cmdstring - the actual command name used to call this (this allows you to know which alias was used, for example) self.args - the raw input; everything following self.cmdstring. self.cmdset - the cmdset from which this command was picked. Not often used (useful for commands like 'help' or to list all available commands etc) self.obj - the object on which this command was defined. It is often the same as self.caller. A MUX command has the following possible syntax: name[ with several words][/switch[/switch..]] arg1[,arg2,...] [[=|,] arg[,..]] The 'name[ with several words]' part is already dealt with by the cmdhandler at this point, and stored in self.cmdname (we don't use it here). The rest of the command is stored in self.args, which can start with the switch indicator /. This parser breaks self.args into its constituents and stores them in the following variables: self.switches = [list of /switches (without the /)] self.raw = This is the raw argument input, including switches self.args = This is re-defined to be everything *except* the switches self.lhs = Everything to the left of = (lhs:'left-hand side'). If no = is found, this is identical to self.args. self.rhs: Everything to the right of = (rhs:'right-hand side'). If no '=' is found, this is None. self.lhslist - [self.lhs split into a list by comma] self.rhslist - [list of self.rhs split into a list by comma] self.arglist = [list of space-separated args (stripped, including '=' if it exists)] All args and list members are stripped of excess whitespace around the strings, but case is preserved. """ raw = self.args args = raw.strip() # split out switches switches = [] if args and len(args) > 1 and raw[0] == "/": # we have a switch, or a set of switches. These end with a space. switches = args[1:].split(None, 1) if len(switches) > 1: switches, args = switches switches = switches.split('/') else: args = "" switches = switches[0].split('/') arglist = [arg.strip() for arg in args.split()] # check for arg1, arg2, ... = argA, argB, ... constructs lhs, rhs = args, None lhslist, rhslist = [arg.strip() for arg in args.split(',')], [] if args and '=' in args: lhs, rhs = [arg.strip() for arg in args.split('=', 1)] lhslist = [arg.strip() for arg in lhs.split(',')] rhslist = [arg.strip() for arg in rhs.split(',')] # save to object properties: self.raw = raw self.switches = switches self.args = args.strip() self.arglist = arglist self.lhs = lhs self.lhslist = lhslist self.rhs = rhs self.rhslist = rhslist # if the class has the player_caller property set on itself, we make # sure that self.caller is always the player if possible. We also create # a special property "character" for the puppeted object, if any. This # is convenient for commands defined on the Player only. if hasattr(self, "player_caller") and self.player_caller: if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): # caller is an Object/Character self.character = self.caller self.caller = self.caller.player elif utils.inherits_from(self.caller, "evennia.players.players.DefaultPlayer"): # caller was already a Player self.character = self.caller.get_puppet(self.session) else: self.character = None
def swap_typeclass(self, new_typeclass, clean_attributes=False, run_start_hooks=True, no_default=True): """ This performs an in-situ swap of the typeclass. This means that in-game, this object will suddenly be something else. Player will not be affected. To 'move' a player to a different object entirely (while retaining this object's type), use self.player.swap_object(). Note that this might be an error prone operation if the old/new typeclass was heavily customized - your code might expect one and not the other, so be careful to bug test your code if using this feature! Often its easiest to create a new object and just swap the player over to that one instead. Args: new_typeclass (str or classobj): Type to switch to. clean_attributes (bool or list, optional): Will delete all attributes stored on this object (but not any of the database fields such as name or location). You can't get attributes back, but this is often the safest bet to make sure nothing in the new typeclass clashes with the old one. If you supply a list, only those named attributes will be cleared. run_start_hooks (bool, optional): Trigger the start hooks of the object, as if it was created for the first time. no_default (bool, optiona): If set, the swapper will not allow for swapping to a default typeclass in case the given one fails for some reason. Instead the old one will be preserved. Returns: result (bool): True/False depending on if the swap worked or not. """ if not callable(new_typeclass): # this is an actual class object - build the path new_typeclass = class_from_module(new_typeclass, defaultpaths=settings.TYPECLASS_PATHS) # if we get to this point, the class is ok. if inherits_from(self, "evennia.scripts.models.ScriptDB"): if self.interval > 0: raise RuntimeError("Cannot use swap_typeclass on time-dependent " \ "Script '%s'.\nStop and start a new Script of the " \ "right type instead." % self.key) self.typeclass_path = new_typeclass.path self.__class__ = new_typeclass if clean_attributes: # Clean out old attributes if is_iter(clean_attributes): for attr in clean_attributes: self.attributes.remove(attr) for nattr in clean_attributes: if hasattr(self.ndb, nattr): self.nattributes.remove(nattr) else: self.attributes.clear() self.nattributes.clear() if run_start_hooks: # fake this call to mimic the first save self.at_first_save()
def parse(self): """ This method is called by the cmdhandler once the command name has been identified. It creates a new set of member variables that can be later accessed from self.func() (see below) The following variables are available for our use when entering this method (from the command definition, and assigned on the fly by the cmdhandler): self.key - the name of this command ('look') self.aliases - the aliases of this cmd ('l') self.permissions - permission string for this command self.help_category - overall category of command self.caller - the object calling this command self.cmdstring - the actual command name used to call this (this allows you to know which alias was used, for example) self.args - the raw input; everything following self.cmdstring. self.cmdset - the cmdset from which this command was picked. Not often used (useful for commands like 'help' or to list all available commands etc) self.obj - the object on which this command was defined. It is often the same as self.caller. A MUX command has the following possible syntax: name[ with several words][/switch[/switch..]] arg1[,arg2,...] [[=|,] arg[,..]] The 'name[ with several words]' part is already dealt with by the cmdhandler at this point, and stored in self.cmdname (we don't use it here). The rest of the command is stored in self.args, which can start with the switch indicator /. Optional variables to aid in parsing, if set: self.switch_options - (tuple of valid /switches expected by this command (without the /)) self.rhs_split - Alternate string delimiter or tuple of strings to separate left/right hand sides. tuple form gives priority split to first string delimiter. This parser breaks self.args into its constituents and stores them in the following variables: self.switches = [list of /switches (without the /)] self.raw = This is the raw argument input, including switches self.args = This is re-defined to be everything *except* the switches self.lhs = Everything to the left of = (lhs:'left-hand side'). If no = is found, this is identical to self.args. self.rhs: Everything to the right of = (rhs:'right-hand side'). If no '=' is found, this is None. self.lhslist - [self.lhs split into a list by comma] self.rhslist - [list of self.rhs split into a list by comma] self.arglist = [list of space-separated args (stripped, including '=' if it exists)] All args and list members are stripped of excess whitespace around the strings, but case is preserved. """ raw = self.args args = raw.strip() # Without explicitly setting these attributes, they assume default values: if not hasattr(self, "switch_options"): self.switch_options = None if not hasattr(self, "rhs_split"): self.rhs_split = "=" if not hasattr(self, "account_caller"): self.account_caller = False # split out switches switches, delimiters = [], self.rhs_split if self.switch_options: self.switch_options = [opt.lower() for opt in self.switch_options] if args and len(args) > 1 and raw[0] == "/": # we have a switch, or a set of switches. These end with a space. switches = args[1:].split(None, 1) if len(switches) > 1: switches, args = switches switches = switches.split('/') else: args = "" switches = switches[0].split('/') # If user-provides switches, parse them with parser switch options. if switches and self.switch_options: valid_switches, unused_switches, extra_switches = [], [], [] for element in switches: option_check = [opt for opt in self.switch_options if opt == element] if not option_check: option_check = [opt for opt in self.switch_options if opt.startswith(element)] match_count = len(option_check) if match_count > 1: extra_switches.extend(option_check) # Either the option provided is ambiguous, elif match_count == 1: valid_switches.extend(option_check) # or it is a valid option abbreviation, elif match_count == 0: unused_switches.append(element) # or an extraneous option to be ignored. if extra_switches: # User provided switches self.msg('|g%s|n: |wAmbiguous switch supplied: Did you mean /|C%s|w?' % (self.cmdstring, ' |nor /|C'.join(extra_switches))) if unused_switches: plural = '' if len(unused_switches) == 1 else 'es' self.msg('|g%s|n: |wExtra switch%s "/|C%s|w" ignored.' % (self.cmdstring, plural, '|n, /|C'.join(unused_switches))) switches = valid_switches # Only include valid_switches in command function call arglist = [arg.strip() for arg in args.split()] # check for arg1, arg2, ... = argA, argB, ... constructs lhs, rhs = args.strip(), None if lhs: if delimiters and hasattr(delimiters, '__iter__'): # If delimiter is iterable, best_split = delimiters[0] # (default to first delimiter) for this_split in delimiters: # try each delimiter if this_split in lhs: # to find first successful split best_split = this_split # to be the best split. break else: best_split = delimiters # Parse to separate left into left/right sides using best_split delimiter string if best_split in lhs: lhs, rhs = lhs.split(best_split, 1) # Trim user-injected whitespace rhs = rhs.strip() if rhs is not None else None lhs = lhs.strip() # Further split left/right sides by comma delimiter lhslist = [arg.strip() for arg in lhs.split(',')] if lhs is not None else "" rhslist = [arg.strip() for arg in rhs.split(',')] if rhs is not None else "" # save to object properties: self.raw = raw self.switches = switches self.args = args.strip() self.arglist = arglist self.lhs = lhs self.lhslist = lhslist self.rhs = rhs self.rhslist = rhslist # if the class has the account_caller property set on itself, we make # sure that self.caller is always the account if possible. We also create # a special property "character" for the puppeted object, if any. This # is convenient for commands defined on the Account only. if self.account_caller: if utils.inherits_from(self.caller, "evennia.objects.objects.DefaultObject"): # caller is an Object/Character self.character = self.caller self.caller = self.caller.account elif utils.inherits_from(self.caller, "evennia.accounts.accounts.DefaultAccount"): # caller was already an Account self.character = self.caller.get_puppet(self.session) else: self.character = None