objects based on the visible key and desc. Useful for inventory listings (Griatch)
Feature: Add DefaultObject.get_numbered_namereturn_string bool kwarg, for only
returning singular/plural based on count instead of a tuple with both (Griatch)
+
Fix Removed the @reboot alias to @reset to not mislead people
+into thinking you can do a portal+server reboot from in-game (you cannot) (Griatch)
Fix: DefaultObject.get_numbered_name used .name instead of
.get_display_name which broke recog systems. May lead to object’s #dbref
will show for admins in some more places (Griatch)
Fix: Refactor Clothing contrib’s inventory command align with
Evennia core’s version (michaelfaith84, Griatch)
+
Fix: Limiting search by tag didn’t take search-string into
+account (Griatch)
+
[Fix][issue4311]: SSH connection caused a traceback in protocol (Griatch)
Fix: Resolve a bug when loading on-demand-handler data from database (Griatch)
diff --git a/docs/latest/_modules/evennia/commands/default/system.html b/docs/latest/_modules/evennia/commands/default/system.html
index 83dceaaa20..d950acd178 100644
--- a/docs/latest/_modules/evennia/commands/default/system.html
+++ b/docs/latest/_modules/evennia/commands/default/system.html
@@ -190,7 +190,6 @@
"""key="@reset"
- aliases=["@reboot"]locks="cmd:perm(reload) or perm(Developer)"help_category="System"
diff --git a/docs/latest/_modules/evennia/contrib/tutorials/evadventure/ai.html b/docs/latest/_modules/evennia/contrib/tutorials/evadventure/ai.html
index bd6b41893e..4262e55336 100644
--- a/docs/latest/_modules/evennia/contrib/tutorials/evadventure/ai.html
+++ b/docs/latest/_modules/evennia/contrib/tutorials/evadventure/ai.html
@@ -91,375 +91,235 @@
"""NPC AI module for EvAdventure (WIP)
-This implements a state machine for the NPCs, where it uses inputs from the game to determine what
-to do next. The AI works on the concept of being 'ticks', at which point, the AI will decide to move
-between different 'states', performing different 'actions' within each state until changing to
-another state. The odds of changing between states and performing actions are weighted, allowing for
-an AI agent to be more or less likely to perform certain actions.
+This implements a simple state machine for NPCs to follow.
-The state machine is fed a dictionary of states and their transitions, and a dictionary of available
-actions to choose between.
-::
+The AIHandler class is stored on the NPC object and is queried by the game loop to determine what
+the NPC does next. This leads to the calling of one of the relevant state methods on the NPC, which
+is where the actual logic for the NPC's behaviour is implemented. Each state is responsible for
+switching to the next state when the conditions are met.
- {
- "states": {
- "state1": {"action1": odds, "action2": odds, ...},
- "state2": {"action1": odds, "action2": odds, ...}, ...
- }
- "transition": {
- "state1": {"state2": "odds, "state3": odds, ...},
- "state2": {"state1": "odds, "state3": odds, ...}, ...
- }
- }
+The AIMixin class is a mixin that can be added to any object that needs AI. It provides the `.ai`
+reference to the AIHandler and a few basic `ai_*` methods for basic AI behaviour.
-The NPC class needs to look like this:
-::
- class NPC(DefaultCharacter):
+Example usage:
- # ...
+```python
+from evennia import create_object
+from .npc import EvadventureNPC
+from .ai import AIMixin
- @lazy_property
- def ai(self):
- return AIHandler(self)
+class MyMob(AIMixin, EvadventureNPC):
+ pass
- def ai_roam(self, action):
- # perform the action within the current state ai.state
+mob = create_object(MyMob, key="Goblin", location=room)
- def ai_hunt(self, action):
- # etc
+mob.ai.set_state("patrol")
+
+# tick the ai whenever needed
+mob.ai.run()
+
+```"""importrandom
-fromevennia.utilsimportlogger
-fromevennia.utils.dbserializeimportdeserialize
+fromevennia.utils.loggerimportlog_trace
+fromevennia.utils.utilsimportlazy_property
-# Some example AI structures
-
-EMOTIONAL_AI={
- # Non-combat AI that has different moods for conversations
- "states":{
- "neutral":{"talk_neutral":0.9,"change_state":0.1},
- "happy":{"talk_happy":0.9,"change_state":0.1},
- "sad":{"talk_sad":0.9,"change_state":0.1},
- "angry":{"talk_angry":0.9,"change_state":0.1},
- }
-}
-
-STATIC_AI={
- # AI that just hangs around until attacked
- "states":{
- "idle":{"do_nothing":1.0},
- "combat":{"attack":0.9,"stunt":0.1},
- }
-}
-
-ROAM_AI={
- # AI that roams around randomly, now and then stopping.
- "states":{
- "idle":{"do_nothing":0.9,"change_state":0.1},
- "roam":{
- "move_north":0.1,
- "move_south":0.1,
- "move_east":0.1,
- "move_west":0.1,
- "wait":0.4,
- "change_state":0.2,
- },
- "combat":{"attack":0.9,"stunt":0.05,"flee":0.05},
- },
- "transitions":{
- "idle":{"roam":0.5,"idle":0.5},
- "roam":{"idle":0.1,"roam":0.9},
- },
-}
-
-HUNTER_AI={
- "states":{
- "hunt_roam":{
- "move_north":0.2,
- "move_south":0.2,
- "move_east":0.2,
- "move_west":0.2,
- },
- "hunt_track":{
- "track_and_move":0.9,
- "change_state":0.1,
- },
- "combat":{"attack":0.8,"stunt":0.1,"other":0.1},
- },
- "transitions":{
- # add a chance of the hunter losing its trail
- "hunt_track":{"hunt_roam":1.0},
- },
-}
+from.enumsimportAbility
[docs]defget_targets(self):
+"""
+ Get a list of potential targets for the NPC to attack
+ """
+ return[objforobjinself.obj.location.contentsifhasattr(obj,"is_pc")andobj.is_pc]
[docs]classAIMixin:"""
- AIHandler class. This should be placed on the NPC object, and will handle the state machine,
- including transitions and actions.
-
- Add to typeclass with @lazyproperty:
-
- class NPC(DefaultCharacter):
-
- ai_states = {...}
-
- # ...
-
- @lazyproperty
- def ai(self):
- return AIHandler(self)
+ Mixin for adding AI to an Object. This is a simple state machine. Just add more `ai_*` methods
+ to the object to make it do more things. """
-
[docs]def__init__(self,obj):
- self.obj=obj
+ # combat probabilities should add up to 1.0
+ combat_probabilities={
+ "hold":0.1,
+ "attack":0.9,
+ "stunt":0.0,
+ "item":0.0,
+ "flee":0.0,
+ }
- ifhasattr(self,"ai_states"):
- # since we're not setting `force=True` here, we won't overwrite any existing /
- # customized dicts.
- self.add_aidict(self.ai_states)
[docs]classAggressiveMobMixin(AIMixin):
+"""
+ A simple aggressive mob that can roam, attack and flee.
+
+ """
+
+ combat_probabilities={
+ "hold":0.0,
+ "attack":0.85,
+ "stunt":0.05,
+ "item":0.0,
+ "flee":0.05,
+ }
+
+
[docs]defai_idle(self):"""
- Normalize odds to 1.0.
-
- Args:
- odds (list): List of odds to normalize.
- Returns:
- list: Normalized list of odds.
+ Do nothing, but switch to attack state if a target is found. """
- return[float(i)/sum(odds)foriinodds]
+ ifself.ai.get_targets():
+ self.ai.set_state("attack")
[docs]defai_attack(self):"""
- Choose a random element from a list of choices, with odds.
-
- Args:
- choices (list): List of choices to choose from. Unordered.
- odds (list): List of odds to choose from, matching the choices list. This
- can be a list of integers or floats, indicating priority. Have odds sum
- up to 100 or 1.0 to properly represent predictable odds.
- Returns:
- object: Randomly chosen element from choices.
+ Manage the attack/combat state of the mob. """
- ifchoices:
- returnrandom.choices(choices,odds)[0]
+ ifcombathandler:=self.nbd.combathandler:
+ # already in combat
+ allies,enemies=combathandler.get_sides(self)
+ action=self.ai.random_probability(self.combat_probabilities)
- @staticmethod
- def_weighted_choice_dict(choices):
+ matchaction:
+ case"hold":
+ combathandler.queue_action({"key":"hold"})
+ case"attack":
+ combathandler.queue_action({"key":"attack","target":random.choice(enemies)})
+ case"stunt":
+ # choose a random ally to help
+ combathandler.queue_action(
+ {
+ "key":"stunt",
+ "recipient":random.choice(allies),
+ "advantage":True,
+ "stunt":Ability.STR,
+ "defense":Ability.DEX,
+ }
+ )
+ case"item":
+ # use a random item on a random ally
+ target=random.choice(allies)
+ valid_items=[itemforiteminself.contentsifitem.at_pre_use(self,target)]
+ combathandler.queue_action(
+ {"key":"item","item":random.choice(valid_items),"target":target}
+ )
+ case"flee":
+ self.ai.set_state("flee")
+
+ ifnot(targets:=self.ai.get_targets()):
+ self.ai.set_state("patrol")
+ else:
+ target=random.choice(targets)
+ self.execute_cmd(f"attack {target.key}")
+
+
[docs]defai_patrol(self):"""
- Choose a random element from a dictionary of choices, with odds.
-
- Args:
- choices (dict): Dictionary of choices to choose from, with odds as values.
- Returns:
- object: Randomly chosen element from choices.
+ Patrol, moving randomly to a new room. If a target is found, switch to attack state. """
- returnAIHandler._weighted_choice(list(choices.keys()),list(choices.values()))
+ iftargets:=self.ai.get_targets():
+ self.ai.set_state("attack")
+ self.execute_cmd(f"attack {random.choice(targets).key}")
+ else:
+ exits=self.ai.get_traversable_exits()
+ ifexits:
+ exi=random.choice(exits)
+ self.execute_cmd(f"{exi.key}")
- @staticmethod
- def_validate_ai_dict(aidict):
+
[docs]defai_flee(self):"""
- Validate and normalize an AI dictionary.
-
- Args:
- aidict (dict): AI dictionary to normalize.
- Returns:
- dict: Normalized AI dictionary.
+ Flee from the current room, avoiding going back to the room from which we came. If no exits
+ are found, switch to patrol state. """
- if"states"notinaidict:
- raiseValueError("AI dictionary must contain a 'states' key.")
-
- if"transitions"notinaidict:
- aidict["transitions"]={}
-
- # if we have no transitions, make sure we have a transition for each state set to 0
- forstateinaidict["states"]:
- ifstatenotinaidict["transitions"]:
- aidict["transitions"][state]={}
- forstate2inaidict["states"]:
- ifstate2notinaidict["transitions"][state]:
- aidict["transitions"][state][state2]=0.0
-
- # normalize odds
- forstate,actionsinaidict["states"].items():
- aidict["states"][state]=AIHandler._normalize_odds(list(actions.values()))
- forstate,transitionsinaidict["transitions"].items():
- aidict["transitions"][state]=AIHandler._normalize_odds(list(transitions.values()))
-
- returnaidict
-
- @property
- defstate(self):
-"""
- Return the current state of the AI.
-
- Returns:
- str: Current state of the AI.
-
- """
- returnself.obj.attributes.get("ai_state",category="ai",default="idle")
-
- @state.setter
- defstate(self,value):
-"""
- Set the current state of the AI. This allows to force a state change, e.g. when starting
- combat.
-
- Args:
- value (str): New state of the AI.
-
- """
- returnself.obj.attributes.add("ai_state",category="ai")
-
- @property
- defstates(self):
-"""
- Return the states dictionary for the AI.
-
- Returns:
- dict: States dictionary for the AI.
-
- """
- returnself.obj.attributes.get("ai_states",category="ai",default={"idle":{}})
-
- @states.setter
- defstates(self,value):
-"""
- Set the states dictionary for the AI.
-
- Args:
- value (dict): New states dictionary for the AI.
-
- """
- returnself.obj.attributes.add("ai_states",value,category="ai")
-
- @property
- deftransitions(self):
-"""
- Return the transitions dictionary for the AI.
-
- Returns:
- dict: Transitions dictionary for the AI.
-
- """
- returnself.obj.attributes.get("ai_transitions",category="ai",default={"idle":[]})
-
- @transitions.setter
- deftransitions(self,value):
-"""
- Set the transitions dictionary for the AI.
-
- Args:
- value (dict): New transitions dictionary for the AI. This will be automatically
- normalized.
-
- """
- forstateinvalue.keys():
- value[state]=dict(
- zip(value[state].keys(),self._normalize_odds(value[state].values()))
- )
- returnself.obj.attributes.add("ai_transitions",value,category="ai")
-
-
[docs]defadd_aidict(self,aidict,force=False):
-"""
- Add an AI dictionary to the AI handler, if one doesn't already exist.
-
- Args:
- aidict (dict): AI dictionary to add.
- force (bool, optional): Force adding the AI dictionary, even if one already exists on
- this handler.
-
- """
- ifnotforceandself.statesandself.transitions:
- return
-
- aidict=self._validate_ai_dict(aidict)
- self.states=aidict["states"]
- self.transitions=aidict["transitions"]
-
-
[docs]defadjust_transition_probability(self,state_start,state_end,odds):
-"""
- Adjust the transition probability between two states.
-
- Args:
- state_start (str): State to start from.
- state_end (str): State to end at.
- odds (int): New odds for the transition.
-
- Note:
- This will normalize the odds across the other transitions from the starting state.
-
- """
- transitions=deserialize(self.transitions)
- transitions[state_start][state_end]=odds
- transitions[state_start]=dict(
- zip(
- transitions[state_start].keys(),
- self._normalize_odds(transitions[state_start].values()),
- )
- )
- self.transitions=transitions
-
-
[docs]defget_next_state(self):
-"""
- Get the next state for the AI.
-
- Returns:
- str: Next state for the AI.
-
- """
- returnself._weighted_choice_dict(self.transitions[self.state])
-
-
[docs]defget_next_action(self):
-"""
- Get the next action for the AI within the current state.
-
- Returns:
- str: Next action for the AI.
-
- """
- returnself._weighted_choice_dict(self.states[self.state])
-
-
[docs]defexecute_ai(self):
-"""
- Execute the next ai action in the current state.
-
- This assumes that each available state exists as a method on the object, named
- ai_<state_name>, taking an optional argument of the next action to perform. The method
- will itself update the state or transition weights through this handler.
-
- Some states have in-built state transitions, via the special "change_state" action.
-
- """
- next_action=self.get_next_action()
- statechange=0
- whilenext_action=="change_state":
- self.state=self.get_next_state()
- next_action=self.get_next_action()
- ifstatechange>5:
- logger.log_err(f"AIHandler: {self.obj} got stuck in a state-change loop.")
- return
-
- # perform the action
- try:
- getattr(self.obj,f"ai_{self.state}")(next_action)
- exceptAttributeError:
- logger.log_err(f"AIHandler: {self.obj} has no ai_{self.state} method.")
+ current_room=self.location
+ past_room=self.attributes.get("past_room",category="ai_state",default=None)
+ exits=self.ai.get_traversable_exits(exclude_destination=past_room)
+ ifexits:
+ self.attributes.set("past_room",current_room,category="ai_state")
+ exi=random.choice(exits)
+ self.execute_cmd(f"{exi.key}")
+ else:
+ # if in a dead end, patrol will allow for backing out
+ self.ai.set_state("patrol")
[docs]classEvAdventureMob(AggressiveMobMixin,EvAdventureNPC):""" Mob (mobile) NPC; this is usually an enemy.
@@ -346,36 +347,6 @@
# chance (%) that this enemy will loot you when defeating youloot_chance=AttributeProperty(75,autocreate=False)
-
[docs]defai_next_action(self,**kwargs):
-"""
- Called to get the next action in combat.
-
- Args:
- combathandler (EvAdventureCombatHandler): The currently active combathandler.
-
- Returns:
- tuple: A tuple `(str, tuple, dict)`, being the `action_key`, and the `*args` and
- `**kwargs` for that action. The action-key is that of a CombatAction available to the
- combatant in the current combat handler.
-
- """
- from.combatimportCombatActionAttack,CombatActionDoNothing
-
- ifself.is_idle:
- # mob just stands around
- returnCombatActionDoNothing.key,(),{}
-
- target=choice(combathandler.get_enemy_targets(self))
-
- # simply randomly decide what action to take
- action=choice(
- (
- CombatActionAttack,
- CombatActionDoNothing,
- )
- )
- returnaction.key,(target,),{}
-
[docs]defat_defeat(self):""" Mobs die right away when defeated, no death-table rolls.
diff --git a/docs/latest/_modules/evennia/contrib/tutorials/evadventure/tests/test_ai.html b/docs/latest/_modules/evennia/contrib/tutorials/evadventure/tests/test_ai.html
new file mode 100644
index 0000000000..a5f4c9b83b
--- /dev/null
+++ b/docs/latest/_modules/evennia/contrib/tutorials/evadventure/tests/test_ai.html
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+ evennia.contrib.tutorials.evadventure.tests.test_ai — Evennia latest documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/latest/api/evennia.commands.default.account.html b/docs/latest/api/evennia.commands.default.account.html
index 375f6c988e..e86f477900 100644
--- a/docs/latest/api/evennia.commands.default.account.html
+++ b/docs/latest/api/evennia.commands.default.account.html
@@ -145,7 +145,7 @@ method. Otherwise all text will be returned to all connected sessions.
-search_index_entry = {'aliases': 'remit pemit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' remit pemit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}¶
+search_index_entry = {'aliases': 'pemit remit', 'category': 'admin', 'key': 'emit', 'no_prefix': ' pemit remit', 'tags': '', 'text': '\n admin command for emitting message to multiple objects\n\n Usage:\n emit[/switches] [<obj>, <obj>, ... =] <message>\n remit [<obj>, <obj>, ... =] <message>\n pemit [<obj>, <obj>, ... =] <message>\n\n Switches:\n room - limit emits to rooms only (default)\n accounts - limit emits to accounts only\n contents - send to the contents of matched objects too\n\n Emits a message to the selected objects or to\n your immediate surroundings. If the object is a room,\n send to its contents. remit and pemit are just\n limited forms of emit, for sending to rooms and\n to accounts respectively.\n '}¶
diff --git a/docs/latest/api/evennia.commands.default.building.html b/docs/latest/api/evennia.commands.default.building.html
index 675e9c320b..9c35c3fdeb 100644
--- a/docs/latest/api/evennia.commands.default.building.html
+++ b/docs/latest/api/evennia.commands.default.building.html
@@ -641,7 +641,7 @@ You can specify the /force switch to bypass this confirmation.
@@ -682,7 +682,7 @@ You can specify the /force switch to bypass this confirmation.
-search_index_entry = {'aliases': '@delete @del', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy delete del', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}¶
+search_index_entry = {'aliases': '@del @delete', 'category': 'building', 'key': '@destroy', 'no_prefix': 'destroy del delete', 'tags': '', 'text': '\n permanently delete objects\n\n Usage:\n destroy[/switches] [obj, obj2, obj3, [dbref-dbref], ...]\n\n Switches:\n override - The destroy command will usually avoid accidentally\n destroying account objects. This switch overrides this safety.\n force - destroy without confirmation.\n Examples:\n destroy house, roof, door, 44-78\n destroy 5-10, flower, 45\n destroy/force north\n\n Destroys one or many objects. If dbrefs are used, a range to delete can be\n given, e.g. 4-10. Also the end points will be deleted. This command\n displays a confirmation before destroying, to make sure of your choice.\n You can specify the /force switch to bypass this confirmation.\n '}¶
-search_index_entry = {'aliases': '@typeclasses @update @parent @swap @type', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass typeclasses update parent swap type', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
+search_index_entry = {'aliases': '@typeclasses @type @update @parent @swap', 'category': 'building', 'key': '@typeclass', 'no_prefix': 'typeclass typeclasses type update parent swap', 'tags': '', 'text': "\n set or change an object's typeclass\n\n Usage:\n typeclass[/switch] <object> [= typeclass.path]\n typeclass/prototype <object> = prototype_key\n\n typeclasses or typeclass/list/show [typeclass.path]\n swap - this is a shorthand for using /force/reset flags.\n update - this is a shorthand for using the /force/reload flag.\n\n Switch:\n show, examine - display the current typeclass of object (default) or, if\n given a typeclass path, show the docstring of that typeclass.\n update - *only* re-run at_object_creation on this object\n meaning locks or other properties set later may remain.\n reset - clean out *all* the attributes and properties on the\n object - basically making this a new clean object. This will also\n reset cmdsets!\n force - change to the typeclass also if the object\n already has a typeclass of the same name.\n list - show available typeclasses. Only typeclasses in modules actually\n imported or used from somewhere in the code will show up here\n (those typeclasses are still available if you know the path)\n prototype - clean and overwrite the object with the specified\n prototype key - effectively making a whole new object.\n\n Example:\n type button = examples.red_button.RedButton\n type/prototype button=a red button\n\n If the typeclass_path is not given, the current object's typeclass is\n assumed.\n\n View or set an object's typeclass. If setting, the creation hooks of the\n new typeclass will be run on the object. If you have clashing properties on\n the old class, use /reset. By default you are protected from changing to a\n typeclass of the same name as the one you already have - use /force to\n override this protection.\n\n The given typeclass must be identified by its location using python\n dot-notation pointing to the correct module and class. If no typeclass is\n given (or a wrong typeclass is given). Errors in the path or new typeclass\n will lead to the old typeclass being kept. The location of the typeclass\n module is searched from the default typeclass directory, as defined in the\n server settings.\n\n "}¶
-search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}¶
@@ -280,7 +280,7 @@ for everyone to use, you need build privileges and the alias command.
@@ -312,7 +312,7 @@ for everyone to use, you need build privileges and the alias command.
-search_index_entry = {'aliases': 'nicks nickname', 'category': 'general', 'key': 'nick', 'no_prefix': ' nicks nickname', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
+search_index_entry = {'aliases': 'nickname nicks', 'category': 'general', 'key': 'nick', 'no_prefix': ' nickname nicks', 'tags': '', 'text': '\n define a personal alias/nick by defining a string to\n match and replace it with another on the fly\n\n Usage:\n nick[/switches] <string> [= [replacement_string]]\n nick[/switches] <template> = <replacement_template>\n nick/delete <string> or number\n nicks\n\n Switches:\n inputline - replace on the inputline (default)\n object - replace on object-lookup\n account - replace on account-lookup\n list - show all defined aliases (also "nicks" works)\n delete - remove nick by index in /list\n clearall - clear all nicks\n\n Examples:\n nick hi = say Hello, I\'m Sarah!\n nick/object tom = the tall man\n nick build $1 $2 = create/drop $1;$2\n nick tell $1 $2=page $1=$2\n nick tm?$1=page tallman=$1\n nick tm\\=$1=page tallman=$1\n\n A \'nick\' is a personal string replacement. Use $1, $2, ... to catch arguments.\n Put the last $-marker without an ending space to catch all remaining text. You\n can also use unix-glob matching for the left-hand side <string>:\n\n * - matches everything\n ? - matches 0 or 1 single characters\n [abcd] - matches these chars in any order\n [!abcd] - matches everything not among these chars\n \\= - escape literal \'=\' you want in your <string>\n\n Note that no objects are actually renamed or changed by this command - your nicks\n are only available to you. If you want to permanently add keywords to an object\n for everyone to use, you need build privileges and the alias command.\n\n '}¶
@@ -610,7 +610,7 @@ placing it in their inventory.
@@ -641,7 +641,7 @@ placing it in their inventory.
-search_index_entry = {'aliases': '\' "', 'category': 'general', 'key': 'say', 'no_prefix': ' \' "', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
+search_index_entry = {'aliases': '" \'', 'category': 'general', 'key': 'say', 'no_prefix': ' " \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
diff --git a/docs/latest/api/evennia.commands.default.system.html b/docs/latest/api/evennia.commands.default.system.html
index 537be7e371..28822a2edf 100644
--- a/docs/latest/api/evennia.commands.default.system.html
+++ b/docs/latest/api/evennia.commands.default.system.html
@@ -247,11 +247,6 @@ cmdsets etc will be wiped.
key = '@reset'¶
-
lock_storage = 'cmd:perm(reload) or perm(Developer)'¶
@@ -275,7 +275,7 @@ cmdsets etc will be wiped.
-search_index_entry = {'aliases': '@reboot', 'category': 'system', 'key': '@reset', 'no_prefix': 'reset reboot', 'tags': '', 'text': '\n reset and reboot the server\n\n Usage:\n reset\n\n Notes:\n For normal updating you are recommended to use reload rather\n than this command. Use shutdown for a complete stop of\n everything.\n\n This emulates a cold reboot of the Server component of Evennia.\n The difference to shutdown is that the Server will auto-reboot\n and that it does not affect the Portal, so no users will be\n disconnected. Contrary to reload however, all shutdown hooks will\n be called and any non-database saved scripts, ndb-attributes,\n cmdsets etc will be wiped.\n\n '}¶
+search_index_entry = {'aliases': '', 'category': 'system', 'key': '@reset', 'no_prefix': 'reset ', 'tags': '', 'text': '\n reset and reboot the server\n\n Usage:\n reset\n\n Notes:\n For normal updating you are recommended to use reload rather\n than this command. Use shutdown for a complete stop of\n everything.\n\n This emulates a cold reboot of the Server component of Evennia.\n The difference to shutdown is that the Server will auto-reboot\n and that it does not affect the Portal, so no users will be\n disconnected. Contrary to reload however, all shutdown hooks will\n be called and any non-database saved scripts, ndb-attributes,\n cmdsets etc will be wiped.\n\n '}¶
@@ -741,7 +741,7 @@ to all the variables defined therein.
-search_index_entry = {'aliases': '@task @delays', 'category': 'system', 'key': '@tasks', 'no_prefix': 'tasks task delays', 'tags': '', 'text': "\n Display or terminate active tasks (delays).\n\n Usage:\n tasks[/switch] [task_id or function_name]\n\n Switches:\n pause - Pause the callback of a task.\n unpause - Process all callbacks made since pause() was called.\n do_task - Execute the task (call its callback).\n call - Call the callback of this task.\n remove - Remove a task without executing it.\n cancel - Stop a task from automatically executing.\n\n Notes:\n A task is a single use method of delaying the call of a function. Calls are created\n in code, using `evennia.utils.delay`.\n See |luhttps://www.evennia.com/docs/latest/Command-Duration.html|ltthe docs|le for help.\n\n By default, tasks that are canceled and never called are cleaned up after one minute.\n\n Examples:\n - `tasks/cancel move_callback` - Cancels all movement delays from the slow_exit contrib.\n In this example slow exits creates it's tasks with\n `utils.delay(move_delay, move_callback)`\n - `tasks/cancel 2` - Cancel task id 2.\n\n "}¶
+search_index_entry = {'aliases': '@delays @task', 'category': 'system', 'key': '@tasks', 'no_prefix': 'tasks delays task', 'tags': '', 'text': "\n Display or terminate active tasks (delays).\n\n Usage:\n tasks[/switch] [task_id or function_name]\n\n Switches:\n pause - Pause the callback of a task.\n unpause - Process all callbacks made since pause() was called.\n do_task - Execute the task (call its callback).\n call - Call the callback of this task.\n remove - Remove a task without executing it.\n cancel - Stop a task from automatically executing.\n\n Notes:\n A task is a single use method of delaying the call of a function. Calls are created\n in code, using `evennia.utils.delay`.\n See |luhttps://www.evennia.com/docs/latest/Command-Duration.html|ltthe docs|le for help.\n\n By default, tasks that are canceled and never called are cleaned up after one minute.\n\n Examples:\n - `tasks/cancel move_callback` - Cancels all movement delays from the slow_exit contrib.\n In this example slow exits creates it's tasks with\n `utils.delay(move_delay, move_callback)`\n - `tasks/cancel 2` - Cancel task id 2.\n\n "}¶
diff --git a/docs/latest/api/evennia.commands.default.tests.html b/docs/latest/api/evennia.commands.default.tests.html
index 333e30ae82..8f29159c3e 100644
--- a/docs/latest/api/evennia.commands.default.tests.html
+++ b/docs/latest/api/evennia.commands.default.tests.html
@@ -973,7 +973,7 @@ main test suite started with
Test the batch processor.
-red_button = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmp7ygv7scj/cbe3d4c738efb7e94ff9633fc1afb2d5f24c954c/evennia/contrib/tutorials/red_button/red_button.py'>¶
+red_button = <module 'evennia.contrib.tutorials.red_button.red_button' from '/tmp/tmp66rtn6r2/6e6ab208a6b53fc30dd91a6f83124e641d851601/evennia/contrib/tutorials/red_button/red_button.py'>¶
@@ -169,7 +169,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'co conn con', 'category': 'general', 'key': 'connect', 'no_prefix': ' co conn con', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
+search_index_entry = {'aliases': 'conn con co', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn con co', 'tags': '', 'text': '\n connect to the game\n\n Usage (at login screen):\n connect accountname password\n connect "account name" "pass word"\n\n Use the create command to first create an account before logging in.\n\n If you have spaces in your name, enclose it in double quotes.\n '}¶
@@ -254,7 +254,7 @@ version is a bit more complicated.
@@ -280,7 +280,7 @@ version is a bit more complicated.
-search_index_entry = {'aliases': 'q qu', 'category': 'general', 'key': 'quit', 'no_prefix': ' q qu', 'tags': '', 'text': '\n quit when in unlogged-in state\n\n Usage:\n quit\n\n We maintain a different version of the quit command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
+search_index_entry = {'aliases': 'qu q', 'category': 'general', 'key': 'quit', 'no_prefix': ' qu q', 'tags': '', 'text': '\n quit when in unlogged-in state\n\n Usage:\n quit\n\n We maintain a different version of the quit command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
@@ -353,7 +353,7 @@ for simplicity. It shows a pane of info.
@@ -379,7 +379,7 @@ for simplicity. It shows a pane of info.
-search_index_entry = {'aliases': '? h', 'category': 'general', 'key': 'help', 'no_prefix': ' ? h', 'tags': '', 'text': '\n get help when in unconnected-in state\n\n Usage:\n help\n\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
+search_index_entry = {'aliases': 'h ?', 'category': 'general', 'key': 'help', 'no_prefix': ' h ?', 'tags': '', 'text': '\n get help when in unconnected-in state\n\n Usage:\n help\n\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
diff --git a/docs/latest/api/evennia.contrib.base_systems.email_login.email_login.html b/docs/latest/api/evennia.contrib.base_systems.email_login.email_login.html
index 6eaa011e72..714010645b 100644
--- a/docs/latest/api/evennia.contrib.base_systems.email_login.email_login.html
+++ b/docs/latest/api/evennia.contrib.base_systems.email_login.email_login.html
@@ -151,7 +151,7 @@ the module given by settings.CONNECTION_SCREEN_MODULE.
@@ -181,7 +181,7 @@ there is no object yet before the account has logged in)
-search_index_entry = {'aliases': 'co conn con', 'category': 'general', 'key': 'connect', 'no_prefix': ' co conn con', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
+search_index_entry = {'aliases': 'conn con co', 'category': 'general', 'key': 'connect', 'no_prefix': ' conn con co', 'tags': '', 'text': '\n Connect to the game.\n\n Usage (at login screen):\n connect <email> <password>\n\n Use the create command to first create an account before logging in.\n '}¶
@@ -264,7 +264,7 @@ version is a bit more complicated.
@@ -290,7 +290,7 @@ version is a bit more complicated.
-search_index_entry = {'aliases': 'q qu', 'category': 'general', 'key': 'quit', 'no_prefix': ' q qu', 'tags': '', 'text': '\n We maintain a different version of the `quit` command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
+search_index_entry = {'aliases': 'qu q', 'category': 'general', 'key': 'quit', 'no_prefix': ' qu q', 'tags': '', 'text': '\n We maintain a different version of the `quit` command\n here for unconnected accounts for the sake of simplicity. The logged in\n version is a bit more complicated.\n '}¶
@@ -353,7 +353,7 @@ for simplicity. It shows a pane of info.
@@ -379,7 +379,7 @@ for simplicity. It shows a pane of info.
-search_index_entry = {'aliases': '? h', 'category': 'general', 'key': 'help', 'no_prefix': ' ? h', 'tags': '', 'text': '\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
+search_index_entry = {'aliases': 'h ?', 'category': 'general', 'key': 'help', 'no_prefix': ' h ?', 'tags': '', 'text': '\n This is an unconnected version of the help command,\n for simplicity. It shows a pane of info.\n '}¶
diff --git a/docs/latest/api/evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.html b/docs/latest/api/evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.html
index 8f20c35672..7e3f138b69 100644
--- a/docs/latest/api/evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.html
+++ b/docs/latest/api/evennia.contrib.base_systems.mux_comms_cmds.mux_comms_cmds.html
@@ -172,7 +172,7 @@ aliases to an already joined channel.
@@ -203,7 +203,7 @@ aliases to an already joined channel.
-search_index_entry = {'aliases': 'chanalias aliaschan', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' chanalias aliaschan', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
+search_index_entry = {'aliases': 'aliaschan chanalias', 'category': 'comms', 'key': 'addcom', 'no_prefix': ' aliaschan chanalias', 'tags': '', 'text': '\n Add a channel alias and/or subscribe to a channel\n\n Usage:\n addcom [alias=] <channel>\n\n Joins a given channel. If alias is given, this will allow you to\n refer to the channel by this alias rather than the full channel\n name. Subsequent calls of this command can be used to add multiple\n aliases to an already joined channel.\n '}¶
-search_index_entry = {'aliases': 'delaliaschan delchanalias', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delaliaschan delchanalias', 'tags': '', 'text': "\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom <alias or channel>\n delcom/all <channel>\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n "}¶
+search_index_entry = {'aliases': 'delchanalias delaliaschan', 'category': 'comms', 'key': 'delcom', 'no_prefix': ' delchanalias delaliaschan', 'tags': '', 'text': "\n remove a channel alias and/or unsubscribe from channel\n\n Usage:\n delcom <alias or channel>\n delcom/all <channel>\n\n If the full channel name is given, unsubscribe from the\n channel. If an alias is given, remove the alias but don't\n unsubscribe. If the 'all' switch is used, remove all aliases\n for that channel.\n "}¶
diff --git a/docs/latest/api/evennia.contrib.full_systems.evscaperoom.commands.html b/docs/latest/api/evennia.contrib.full_systems.evscaperoom.commands.html
index d1b6490779..1d5fbf0637 100644
--- a/docs/latest/api/evennia.contrib.full_systems.evscaperoom.commands.html
+++ b/docs/latest/api/evennia.contrib.full_systems.evscaperoom.commands.html
@@ -223,7 +223,7 @@ the operation will be general or on the room.
-search_index_entry = {'aliases': 'abort chicken out q quit', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' abort chicken out q quit', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
+search_index_entry = {'aliases': 'abort chicken out quit q', 'category': 'evscaperoom', 'key': 'give up', 'no_prefix': ' abort chicken out quit q', 'tags': '', 'text': '\n Give up\n\n Usage:\n give up\n\n Abandons your attempts at escaping and of ever winning the pie-eating contest.\n\n '}¶
-search_index_entry = {'aliases': 'l ls', 'category': 'evscaperoom', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n Look at the room, an object or the currently focused object\n\n Usage:\n look [obj]\n\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'evscaperoom', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n Look at the room, an object or the currently focused object\n\n Usage:\n look [obj]\n\n '}¶
-search_index_entry = {'aliases': 'e ex examine unfocus', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' e ex examine unfocus', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
+search_index_entry = {'aliases': 'ex e examine unfocus', 'category': 'evscaperoom', 'key': 'focus', 'no_prefix': ' ex e examine unfocus', 'tags': '', 'text': '\n Focus your attention on a target.\n\n Usage:\n focus <obj>\n\n Once focusing on an object, use look to get more information about how it\n looks and what actions is available.\n\n '}¶
-search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n look\n\n Usage:\n look\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects in your vicinity.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n look\n\n Usage:\n look\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects in your vicinity.\n '}¶
diff --git a/docs/latest/api/evennia.contrib.grid.xyzgrid.commands.html b/docs/latest/api/evennia.contrib.grid.xyzgrid.commands.html
index 5be1099b5b..b3553eddc2 100644
--- a/docs/latest/api/evennia.contrib.grid.xyzgrid.commands.html
+++ b/docs/latest/api/evennia.contrib.grid.xyzgrid.commands.html
@@ -434,7 +434,7 @@ there is no room above/below you, your movement will fail.
@@ -457,7 +457,7 @@ to all the variables defined therein.
-search_index_entry = {'aliases': 'dive fly', 'category': 'general', 'key': 'fly or dive', 'no_prefix': ' dive fly', 'tags': '', 'text': '\n Fly or Dive up and down.\n\n Usage:\n fly\n dive\n\n Will fly up one room or dive down one room at your current position. If\n there is no room above/below you, your movement will fail.\n\n '}¶
+search_index_entry = {'aliases': 'fly dive', 'category': 'general', 'key': 'fly or dive', 'no_prefix': ' fly dive', 'tags': '', 'text': '\n Fly or Dive up and down.\n\n Usage:\n fly\n dive\n\n Will fly up one room or dive down one room at your current position. If\n there is no room above/below you, your movement will fail.\n\n '}¶
diff --git a/docs/latest/api/evennia.contrib.html b/docs/latest/api/evennia.contrib.html
index c14a332676..411057606c 100644
--- a/docs/latest/api/evennia.contrib.html
+++ b/docs/latest/api/evennia.contrib.html
@@ -464,6 +464,7 @@ useful but are deemed too game-specific to go into the core library.
diff --git a/docs/latest/api/evennia.contrib.rpg.dice.dice.html b/docs/latest/api/evennia.contrib.rpg.dice.dice.html
index d8ecccdc59..416482f5c4 100644
--- a/docs/latest/api/evennia.contrib.rpg.dice.dice.html
+++ b/docs/latest/api/evennia.contrib.rpg.dice.dice.html
@@ -338,7 +338,7 @@ everyone but the person rolling.
@@ -364,7 +364,7 @@ everyone but the person rolling.
-search_index_entry = {'aliases': '@dice roll', 'category': 'general', 'key': 'dice', 'no_prefix': ' dice roll', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}¶
+search_index_entry = {'aliases': 'roll @dice', 'category': 'general', 'key': 'dice', 'no_prefix': ' roll dice', 'tags': '', 'text': "\n roll dice\n\n Usage:\n dice[/switch] <nr>d<sides> [modifier] [success condition]\n\n Switch:\n hidden - tell the room the roll is being done, but don't show the result\n secret - don't inform the room about neither roll nor result\n\n Examples:\n dice 3d6 + 4\n dice 1d100 - 2 < 50\n\n This will roll the given number of dice with given sides and modifiers.\n So e.g. 2d6 + 3 means to 'roll a 6-sided die 2 times and add the result,\n then add 3 to the total'.\n Accepted modifiers are +, -, * and /.\n A success condition is given as normal Python conditionals\n (<,>,<=,>=,==,!=). So e.g. 2d6 + 3 > 10 means that the roll will succeed\n only if the final result is above 8. If a success condition is given, the\n outcome (pass/fail) will be echoed along with how much it succeeded/failed\n with. The hidden/secret switches will hide all or parts of the roll from\n everyone but the person rolling.\n "}¶
diff --git a/docs/latest/api/evennia.contrib.rpg.rpsystem.rpsystem.html b/docs/latest/api/evennia.contrib.rpg.rpsystem.rpsystem.html
index 3b96d5c5d4..9714f3d07a 100644
--- a/docs/latest/api/evennia.contrib.rpg.rpsystem.rpsystem.html
+++ b/docs/latest/api/evennia.contrib.rpg.rpsystem.rpsystem.html
@@ -734,7 +734,7 @@ commands the caller can use.
-search_index_entry = {'aliases': '\' "', 'category': 'general', 'key': 'say', 'no_prefix': ' \' "', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
+search_index_entry = {'aliases': '" \'', 'category': 'general', 'key': 'say', 'no_prefix': ' " \'', 'tags': '', 'text': '\n speak as your character\n\n Usage:\n say <message>\n\n Talk to those in your current location.\n '}¶
This implements a state machine for the NPCs, where it uses inputs from the game to determine what
-to do next. The AI works on the concept of being ‘ticks’, at which point, the AI will decide to move
-between different ‘states’, performing different ‘actions’ within each state until changing to
-another state. The odds of changing between states and performing actions are weighted, allowing for
-an AI agent to be more or less likely to perform certain actions.
-
The state machine is fed a dictionary of states and their transitions, and a dictionary of available
-actions to choose between.
This implements a simple state machine for NPCs to follow.
+
The AIHandler class is stored on the NPC object and is queried by the game loop to determine what
+the NPC does next. This leads to the calling of one of the relevant state methods on the NPC, which
+is where the actual logic for the NPC’s behaviour is implemented. Each state is responsible for
+switching to the next state when the conditions are met.
+
The AIMixin class is a mixin that can be added to any object that needs AI. It provides the .ai
+reference to the AIHandler and a few basic ai_* methods for basic AI behaviour.
+
Example usage:
+
fromevenniaimportcreate_object
+from.npcimportEvadventureNPC
+from.aiimportAIMixin
- # ...
+classMyMob(AIMixin,EvadventureNPC):
+ pass
- @lazy_property
- defai(self):
- returnAIHandler(self)
+mob=create_object(MyMob,key="Goblin",location=room)
- defai_roam(self,action):
- # perform the action within the current state ai.state
+mob.ai.set_state("patrol")
- defai_hunt(self,action):
- # etc
+# tick the ai whenever needed
+mob.ai.run()
class evennia.contrib.tutorials.evadventure.ai.AIHandler(obj)[source]¶
Bases: object
-
AIHandler class. This should be placed on the NPC object, and will handle the state machine,
-including transitions and actions.
This assumes that each available state exists as a method on the object, named
-ai_<state_name>, taking an optional argument of the next action to perform. The method
-will itself update the state or transition weights through this handler.
-
Some states have in-built state transitions, via the special “change_state” action.
-search_index_entry = {'aliases': 'l ls', 'category': 'general', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'general', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n look at location or object\n\n Usage:\n look\n look <obj>\n look *<account>\n\n Observes your location or objects in your vicinity.\n '}¶
diff --git a/docs/latest/api/evennia.contrib.tutorials.evadventure.npcs.html b/docs/latest/api/evennia.contrib.tutorials.evadventure.npcs.html
index 25d2e9c73d..c66602ab69 100644
--- a/docs/latest/api/evennia.contrib.tutorials.evadventure.npcs.html
+++ b/docs/latest/api/evennia.contrib.tutorials.evadventure.npcs.html
@@ -478,7 +478,7 @@ based on nodes named node_start_* are available in the node tre
class evennia.contrib.tutorials.evadventure.npcs.EvAdventureMob(*args, **kwargs)[source]¶
combathandler (EvAdventureCombatHandler) – The currently active combathandler.
-
-
Returns
-
tuple – A tuple (str, tuple, dict), being the action_key, and the *args and
-**kwargs for that action. The action-key is that of a CombatAction available to the
-combatant in the current combat handler.
diff --git a/docs/latest/api/evennia.contrib.tutorials.red_button.red_button.html b/docs/latest/api/evennia.contrib.tutorials.red_button.red_button.html
index 730a90b82b..2771200c5d 100644
--- a/docs/latest/api/evennia.contrib.tutorials.red_button.red_button.html
+++ b/docs/latest/api/evennia.contrib.tutorials.red_button.red_button.html
@@ -264,7 +264,7 @@ check if the lid is open or closed.
-search_index_entry = {'aliases': 'break lid smash lid smash', 'category': 'general', 'key': 'smash glass', 'no_prefix': ' break lid smash lid smash', 'tags': '', 'text': '\n Smash the protective glass.\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n\n '}¶
+search_index_entry = {'aliases': 'smash lid smash break lid', 'category': 'general', 'key': 'smash glass', 'no_prefix': ' smash lid smash break lid', 'tags': '', 'text': '\n Smash the protective glass.\n\n Usage:\n smash glass\n\n Try to smash the glass of the button.\n\n '}¶
-search_index_entry = {'aliases': 'ex examine feel listen get l', 'category': 'general', 'key': 'look', 'no_prefix': ' ex examine feel listen get l', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
+search_index_entry = {'aliases': 'get feel l listen ex examine', 'category': 'general', 'key': 'look', 'no_prefix': ' get feel l listen ex examine', 'tags': '', 'text': "\n Looking around in darkness\n\n Usage:\n look <obj>\n\n ... not that there's much to see in the dark.\n\n "}¶
diff --git a/docs/latest/api/evennia.contrib.tutorials.tutorial_world.objects.html b/docs/latest/api/evennia.contrib.tutorials.tutorial_world.objects.html
index c7f3b04d02..78e4e48921 100644
--- a/docs/latest/api/evennia.contrib.tutorials.tutorial_world.objects.html
+++ b/docs/latest/api/evennia.contrib.tutorials.tutorial_world.objects.html
@@ -437,7 +437,7 @@ of the object. We overload it with our own version.
@@ -817,7 +817,7 @@ parry - forgoes your attack but will make you harder to hit on next
-search_index_entry = {'aliases': 'slash pierce bash fight hit parry chop stab kill thrust defend', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' slash pierce bash fight hit parry chop stab kill thrust defend', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
+search_index_entry = {'aliases': 'defend pierce slash fight kill hit stab chop bash thrust parry', 'category': 'tutorialworld', 'key': 'attack', 'no_prefix': ' defend pierce slash fight kill hit stab chop bash thrust parry', 'tags': '', 'text': '\n Attack the enemy. Commands:\n\n stab <enemy>\n slash <enemy>\n parry\n\n stab - (thrust) makes a lot of damage but is harder to hit with.\n slash - is easier to land, but does not make as much damage.\n parry - forgoes your attack but will make you harder to hit on next\n enemy attack.\n\n '}¶
diff --git a/docs/latest/api/evennia.contrib.tutorials.tutorial_world.rooms.html b/docs/latest/api/evennia.contrib.tutorials.tutorial_world.rooms.html
index 125d84798c..898c69f68a 100644
--- a/docs/latest/api/evennia.contrib.tutorials.tutorial_world.rooms.html
+++ b/docs/latest/api/evennia.contrib.tutorials.tutorial_world.rooms.html
@@ -260,7 +260,7 @@ code except for adding in the details.
@@ -275,7 +275,7 @@ code except for adding in the details.
-search_index_entry = {'aliases': 'l ls', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' l ls', 'tags': '', 'text': '\n looks at the room and on details\n\n Usage:\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects\n in your vicinity.\n\n Tutorial: This is a child of the default Look command, that also\n allows us to look at "details" in the room. These details are\n things to examine and offers some extra description without\n actually having to be actual database objects. It uses the\n return_detail() hook on TutorialRooms for this.\n '}¶
+search_index_entry = {'aliases': 'ls l', 'category': 'tutorialworld', 'key': 'look', 'no_prefix': ' ls l', 'tags': '', 'text': '\n looks at the room and on details\n\n Usage:\n look <obj>\n look <room detail>\n look *<account>\n\n Observes your location, details at your location or objects\n in your vicinity.\n\n Tutorial: This is a child of the default Look command, that also\n allows us to look at "details" in the room. These details are\n things to examine and offers some extra description without\n actually having to be actual database objects. It uses the\n return_detail() hook on TutorialRooms for this.\n '}¶
diff --git a/docs/latest/api/evennia.html b/docs/latest/api/evennia.html
index a5105a1838..dd6ffba579 100644
--- a/docs/latest/api/evennia.html
+++ b/docs/latest/api/evennia.html
@@ -609,6 +609,7 @@ with ‘q’, remove the break line and restart server when finished.
-search_index_entry = {'aliases': 'no a y abort n __nomatch_command yes', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' no a y abort n __nomatch_command yes', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
+search_index_entry = {'aliases': 'abort y yes no n a __nomatch_command', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' abort y yes no n a __nomatch_command', 'tags': '', 'text': '\n Handle a prompt for yes or no. Press [return] for the default choice.\n\n '}¶
diff --git a/docs/latest/api/evennia.utils.evmore.html b/docs/latest/api/evennia.utils.evmore.html
index c6a538e321..180e4d92b1 100644
--- a/docs/latest/api/evennia.utils.evmore.html
+++ b/docs/latest/api/evennia.utils.evmore.html
@@ -149,7 +149,7 @@ the caller.msg() construct every time the page is updated.
@@ -175,7 +175,7 @@ the caller.msg() construct every time the page is updated.
-search_index_entry = {'aliases': 't a next top end abort q n p previous e quit', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' t a next top end abort q n p previous e quit', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶
+search_index_entry = {'aliases': 'abort quit e q next p previous n top end a t', 'category': 'general', 'key': '__noinput_command', 'no_prefix': ' abort quit e q next p previous n top end a t', 'tags': '', 'text': '\n Manipulate the text paging. Catch no-input with aliases.\n '}¶