From 7746ff16631f9da7ec065c73710f72b75a3dda58 Mon Sep 17 00:00:00 2001 From: Andrew Bastien Date: Sat, 6 May 2023 03:21:48 -0400 Subject: [PATCH] Added DefaultObject.can_build_object() and DefaultObject.at_object_constructed(builder) hooks and inserted them into all building commands for improved building flexibility. --- evennia/commands/default/building.py | 21 +++++++++++++++++- evennia/objects/objects.py | 33 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 9f7c5fa627..b0ddb00e5f 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -605,6 +605,9 @@ class CmdCreate(ObjManipCommand): # create object (if not a valid typeclass, the default # object typeclass will automatically be used) lockstring = self.new_obj_lockstring.format(id=caller.id) + if (err := caller.can_build_object()): + caller.msg(err) + return obj = create.create_object( typeclass, name, @@ -616,6 +619,7 @@ class CmdCreate(ObjManipCommand): ) if not obj: continue + obj.at_object_constructed(caller) if aliases: string = ( f"You create a new {obj.typename}: {obj.name} (aliases: {', '.join(aliases)})." @@ -929,6 +933,9 @@ class CmdDig(ObjManipCommand): typeclass = settings.BASE_ROOM_TYPECLASS # create room + if (err := caller.can_build_object()): + caller.msg(err) + return new_room = create.create_object( typeclass, room["name"], aliases=room["aliases"], report_to=caller ) @@ -937,6 +944,7 @@ class CmdDig(ObjManipCommand): alias_string = "" if new_room.aliases.all(): alias_string = " (%s)" % ", ".join(new_room.aliases.all()) + new_room.at_object_constructed(caller) room_string = ( f"Created room {new_room}({new_room.dbref}){alias_string} of type {typeclass}." ) @@ -957,7 +965,9 @@ class CmdDig(ObjManipCommand): typeclass = to_exit["option"] if not typeclass: typeclass = settings.BASE_EXIT_TYPECLASS - + if (err := caller.can_build_object()): + caller.msg(err) + return new_to_exit = create.create_object( typeclass, to_exit["name"], @@ -974,6 +984,7 @@ class CmdDig(ObjManipCommand): f"\nCreated Exit from {location.name} to {new_room.name}:" f" {new_to_exit}({new_to_exit.dbref}){alias_string}." ) + new_to_exit.at_object_constructed(caller) # Create exit back from new room @@ -988,6 +999,9 @@ class CmdDig(ObjManipCommand): typeclass = back_exit["option"] if not typeclass: typeclass = settings.BASE_EXIT_TYPECLASS + if (err := caller.can_build_object()): + caller.msg(err) + return new_back_exit = create.create_object( typeclass, back_exit["name"], @@ -1004,6 +1018,7 @@ class CmdDig(ObjManipCommand): f"\nCreated Exit back from {new_room.name} to {location.name}:" f" {new_back_exit}({new_back_exit.dbref}){alias_string}." ) + new_back_exit.at_object_constructed(caller) caller.msg(f"{room_string}{exit_to_string}{exit_back_string}") if new_room and "teleport" in self.switches: caller.move_to(new_room, move_type="teleport") @@ -1477,6 +1492,9 @@ class CmdOpen(ObjManipCommand): lockstring = self.new_obj_lockstring.format(id=caller.id) if not typeclass: typeclass = settings.BASE_EXIT_TYPECLASS + if (err := caller.can_build_object()): + caller.msg(err) + return exit_obj = create.create_object( typeclass, key=exit_name, @@ -1497,6 +1515,7 @@ class CmdOpen(ObjManipCommand): f"Created new Exit '{exit_name}' from {location.name} to" f" {destination.name}{string}." ) + exit_obj.at_object_constructed(caller) else: string = f"Error: Exit '{exit.name}' not created." # emit results diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index c710e96498..b222ac53d3 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1598,6 +1598,38 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): """ pass + def can_build_object(self): + """ + This hook is called by the build command to determine if + self can build a new object. This is called before the + object is created. As it receives no arguments, it + can only be used to determine if self is allowed to build + anything in the current context. + + For instance, a room may want to limit its number of exits, + or the builder might have an enforced quota limit for rooms + they can add to their custom dungeon. + + Returns: + can_build (str or None): If self is allowed to build objects. + return a string as the error if there is a problem. If + this returns True, the build will abort. + """ + pass + + def at_object_constructed(self, builder): + """ + Called when the object is constructed by a builder. + This is used to implement custom logic for building, + such as quota tracking systems, auto-tagging of rooms, + creation of logical groups of rooms like Zones or + dungeons, etc. + + Args: + builder (Object): The object that constructed this object. + """ + pass + def at_pre_puppet(self, account, session=None, **kwargs): """ Called just before an Account connects to this object to puppet @@ -1664,6 +1696,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): """ pass + def at_server_reload(self): """ This hook is called whenever the server is shutting down for