From de24d2646d7b2bf31384fd2738d0717903353a3a Mon Sep 17 00:00:00 2001 From: InspectorCaracal Date: Mon, 21 Nov 2022 20:23:56 -0700 Subject: [PATCH] add test for preserve_items opt and fix --- evennia/contrib/grid/wilderness/tests.py | 27 ++++++ evennia/contrib/grid/wilderness/wilderness.py | 85 ++++++++++++------- 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/evennia/contrib/grid/wilderness/tests.py b/evennia/contrib/grid/wilderness/tests.py index af54bebd26..ec7304e100 100644 --- a/evennia/contrib/grid/wilderness/tests.py +++ b/evennia/contrib/grid/wilderness/tests.py @@ -136,3 +136,30 @@ class TestWilderness(BaseEvenniaTest): for direction, correct_loc in directions.items(): new_loc = wilderness.get_new_coordinates(loc, direction) self.assertEqual(new_loc, correct_loc, direction) + + def test_preserve_items(self): + wilderness.create_wilderness() + w = self.get_wilderness_script() + + # move char and obj to wilderness + wilderness.enter_wilderness(self.char1) + wilderness.enter_wilderness(self.obj1) + + # move to a new room + w.move_obj(self.char1, (1, 1)) + # the room should be remapped and 0,0 should not exist + self.assertTrue((0, 0) not in w.db.rooms) + self.assertEqual(1, len(w.db.rooms)) + # verify obj1 moved to None + self.assertIsNone(self.obj1.location) + + # now change to preserve items + w.preserve_items = True + wilderness.enter_wilderness(self.obj1, (1, 1)) + # move the character again + w.move_obj(self.char1, (0, 1)) + # check that the previous room was preserved + self.assertIn((1, 1), w.db.rooms) + self.assertEqual(2, len(w.db.rooms)) + # and verify that obj1 is still at 1,1 + self.assertEqual(self.obj1.location, w.db.rooms[(1, 1)]) diff --git a/evennia/contrib/grid/wilderness/wilderness.py b/evennia/contrib/grid/wilderness/wilderness.py index 9aef3958b5..977be44454 100644 --- a/evennia/contrib/grid/wilderness/wilderness.py +++ b/evennia/contrib/grid/wilderness/wilderness.py @@ -46,11 +46,11 @@ needs. ## Example - To give an example of how to customize, we will create a very simple (and - small) wilderness map that is shaped like a pyramid. The map will be - provided as a string: a "." symbol is a location we can walk on. +To give an example of how to customize, we will create a very simple (and +small) wilderness map that is shaped like a pyramid. The map will be +provided as a string: a "." symbol is a location we can walk on. - Let's create a file world/pyramid.py: +Let's create a file world/pyramid.py: ```python map_str = ''' @@ -113,7 +113,6 @@ separate rooms. Rooms are created as needed. Unneeded rooms are stored away to avoid the overhead cost of creating new rooms again in the future. - """ from evennia import ( @@ -126,6 +125,7 @@ from evennia import ( from evennia.utils import inherits_from from evennia.typeclasses.attributes import AttributeProperty + def create_wilderness(name="default", mapprovider=None): """ Creates a new wilderness map. Does nothing if a wilderness map already @@ -216,7 +216,7 @@ class WildernessScript(DefaultScript): # Stores a dictionary of items on the map with their coordinates # The key is the item, the value are the coordinates as (x, y) tuple. itemcoordinates = AttributeProperty() - + # Determines whether or not rooms are recycled despite containing non-player objects # True means that leaving behind a non-player object will prevent the room from being recycled # in order to preserve the object @@ -241,7 +241,7 @@ class WildernessScript(DefaultScript): # allows quick retrieval if a new room is needed without having to # create it. self.db.unused_rooms = [] - + def at_server_start(self): """ Called after the server is started or reloaded. @@ -249,7 +249,6 @@ class WildernessScript(DefaultScript): for coordinates, room in self.db.rooms.items(): room.ndb.wildernessscript = self room.ndb.active_coordinates = coordinates - self.wilderness.mapprovider.at_prepare_room(coordinates, None, self) for item in self.db.itemcoordinates.keys(): # Items deleted while in the wilderness can leave None-type 'ghosts' # These need to be cleaned up @@ -299,7 +298,11 @@ class WildernessScript(DefaultScript): Returns: [Object, ]: list of Objects at coordinates """ - result = [ item for item, item_coords in self.itemcoordinates.items() if item_coords == coordinates and item is not None ] + result = [ + item + for item, item_coords in self.itemcoordinates.items() + if item_coords == coordinates and item is not None + ] return list(result) def move_obj(self, obj, new_coordinates): @@ -318,38 +321,56 @@ class WildernessScript(DefaultScript): # appear in its old room should that room be deleted. obj.location = None - # By default, we'll assume we won't be making a new room and change this flag if necessary. - create_room = False - # See if we already have a room for that location if room := self.db.rooms.get(new_coordinates): # There is. Try to destroy the old_room if it is not needed anymore self._destroy_room(old_room) else: # There is no room yet at new_location - # Is the old room in this wilderness? - if old_room in self.db.rooms.keys(): - # Is there anything still left in the old_room, besides the exits? - if len([ob for ob in old_room.contents if not inherits_from(ob, WildernessExit)]): - # There is, so we'll create a new room - room = self._create_room(new_coordinates, obj) - else: - # The room is empty, so we'll just reuse it - room = old_room + # Is the old room in a wilderness? + if hasattr(old_room, "wilderness"): + # Yes. Is it in THIS wilderness? + if old_room.wilderness == self: + # Should we preserve rooms with any objects? + if self.preserve_items: + # Yes - check if ANY objects besides the exits are in old_room + if len( + [ + ob + for ob in old_room.contents + if not inherits_from(ob, WildernessExit) + ] + ): + # There is, so we'll create a new room + room = self._create_room(new_coordinates, obj) + else: + # The room is empty, so we'll reuse it + room = old_room + else: + # Only preserve rooms if there are players behind + if len([ob for ob in old_room.contents if ob.has_account]): + # There is still a player there; create a new room + room = self._create_room(new_coordinates, obj) + else: + # The room is empty of players, so we'll reuse it + room = old_room - # Is the previous room from a different wilderness? - elif inherits_from(old_room, WildernessRoom) and old_room.wilderness != self: - # It does, so we make sure to leave the other wilderness properly - old_room.wilderness.at_post_object_leave(obj) - # We'll also need to create a new room in this wilderness - room = self._create_room(new_coordinates, obj) + # It's in a different wilderness + else: + # It does, so we make sure to leave the other wilderness properly + old_room.wilderness.at_post_object_leave(obj) + # We'll also need to create a new room in this wilderness + room = self._create_room(new_coordinates, obj) else: # Obj comes from outside the wilderness entirely # We need to make a new room room = self._create_room(new_coordinates, obj) - room.set_active_coordinates(new_coordinates, obj) + # Set `room` to the new coordinates, however it was made + room.set_active_coordinates(new_coordinates, obj) + + # Put obj back, now in the correct room obj.location = room obj.ndb.wilderness = self @@ -407,7 +428,7 @@ class WildernessScript(DefaultScript): """ Moves a room back to storage. If room is not a WildernessRoom or there is something left inside the room, then this does nothing. - + Implementation note: If `preserve_items` is False (the default) then any objects left in the rooms will be moved to None. You may want to implement your own cleanup or recycling routine for these objects. @@ -555,6 +576,7 @@ class WildernessRoom(DefaultRoom): rooms[self.coordinates] = self # Any object inside this room will get its location set to None + # unless it's a wilderness exit for item in self.contents: if not item.destination or item.destination != item.location: item.location = None @@ -608,11 +630,11 @@ class WildernessRoom(DefaultRoom): name += " {0}".format(self.coordinates) return name - + def get_display_desc(self, looker, **kwargs): """ Displays the description of the room. This is a core evennia hook. - + Allows the room's description to be customized in an ndb value, avoiding having to write to the database on moving. """ @@ -624,6 +646,7 @@ class WildernessRoom(DefaultRoom): # Otherwise, use the normal description hook. return super().get_display_desc(looker, **kwargs) + class WildernessExit(DefaultExit): """ This is an Exit object used inside a WildernessRoom. Instead of changing