· 5 years ago · Dec 23, 2020, 09:04 PM
1from evennia import default_cmds
2from django.conf import settings
3from .models import Area
4from evennia.utils import inherits_from
5from world.utils import itemize
6from .api import match_area
7from django.db.models.signals import post_save, pre_delete, post_init, post_delete
8
9class CmdArea(default_cmds.MuxCommand):
10 """
11 Areas are named collections of rooms that exist in a hierarchial
12 manner similar to zones on some other servers. Areas are both
13 cosmetic, incorporating the area name into the room name, as well as
14 functional, by offering methods for message propagation throughout the
15 area, and various other features that are still in the planning stage.
16
17
18 Usage:
19 area[/set] <room>=<area> : Sets the Area of a room.
20 area/name <area>=<name> : Changes the name of an existing area.
21 area/parent <area1>=<area2> : Sets the parent of an area.
22 area/purge <area> : Removes all of the rooms assigned to an area.
23 area/destroy <area> : Destroys an area.
24 area/create <name>[=<desc>] : Creates an area.
25 area[/list] : Lists areas.
26 area/desc <area>=<desc> : Describes an area.
27 area/children <area> : View the children of an area.
28 area/rooms <area> : View the rooms of an area.
29 area/group <area>=<group> : Assign a group "owner" to an area.
30
31 area/clear <area> : Clear an area. Removes any parent,
32 all children, and resets the
33 'x.db.area' attribute on all rooms.
34
35 area/restore <area> : Iterates the list of rooms belonging
36 to an area, re-setting the 'x.db.area'
37 attribute on each room.
38
39 Credits: Darren@Seventh Sea MUSH.
40 """
41
42 key = "area"
43 aliases = ("areas", "zone", "zones", "realms")
44 help_category = "Building"
45 locks = "not is_guest"
46
47
48 def create_area(self):
49 if not self.lhs:
50 self.caller.msg("You must give the name of the area.")
51 return
52
53 area = Area(db_key=self.lhs, db_description=self.rhs if self.rhs else "")
54 area.save()
55
56 self.caller.msg(f"Area |C{area.key}|n created.")
57
58
59 def destroy_area(self):
60 if not self.args:
61 self.caller.msg("Usage: area/delete <area>")
62 return
63
64 area = match_area(self.args)
65 if not area:
66 self.caller.msg(f"Area |C{self.args}|n not found.")
67 return
68
69 saved_name = area.db_key
70
71 # Reset room.db.area
72 area.purge_rooms()
73 area.delete()
74 self.caller.msg(f"Area |C{saved_name}|n destroyed.")
75
76
77 def set_area(self):
78 if not self.lhs:
79 self.caller.msg("Usage: area[/set] <room>[=<area>]")
80 return
81
82 # Match room.
83 room = self.caller.search(self.lhs)
84 if not room:
85 return
86
87 # Make certain it's a room.
88 if not inherits_from(room, settings.BASE_ROOM_TYPECLASS):
89 self.caller.msg("Target must be a room.")
90 return
91
92 # Match area.
93 if not self.rhs:
94 area = None
95 else:
96 area = match_area(self.rhs)
97 if not area:
98 self.caller.msg(f"Area |C{self.rhs}|n not found.")
99 return
100
101
102 # Save the current area, if any.
103 old_area = room.db.area or None
104
105 # Need this check so we don't match None
106 if area and old_area == area:
107 self.caller.msg(f"|C{area.key}|n is already the area for that room.")
108 return
109
110 # If there was already an area set, clear it.
111 if old_area:
112 old_area.db_rooms.remove(room)
113 old_area.save()
114
115 if area:
116 area.db_rooms.add(room)
117 area.save()
118
119 # Ideally this would be a ForeignKey field in the Room model but alas...
120 room.db.area = area
121
122 # is this necessary?
123 room.save()
124 self.caller.msg(f"Area of room |C{room.get_display_name(self.caller)}|n set to |C{area.full_name()}|n.")
125 else:
126 del room.db.area
127
128 # Is this necessary?
129 room.save()
130 self.caller.msg(f"Area of room |C{room.get_display_name(self.caller)}|n cleared.")
131
132 def set_area_name(self):
133 if not self.lhs:
134 self.caller.msg("You must specify the area to rename.")
135 return
136
137 if not self.rhs:
138 self.caller.msg("The area name cannot be blank.")
139 return
140
141
142 area = match_area(self.lhs)
143 if not area:
144 self.caller.msg(f"Area |C{self.lhs}|n not found.")
145 return
146
147 old_name = area.db_key
148 area.db_key = self.rhs
149 area.save()
150
151 self.caller.msg(f"Area name changed from |C{old_name}|n to |C{area.db_key}|n.")
152
153
154 # @area/parent <area1>=<area2>
155 def set_area_parent(self):
156
157 # Check args. Complain and exit if necessary.
158 if not self.lhs or not self.rhs:
159 self.caller.msg("Usage: area/parent <area1>=<area2>")
160 return
161
162 # Match target. This returns a valid Area object or None.
163 area1 = match_area(self.lhs)
164
165 # If we didn't find the target area, complain and exit.
166 if not area1:
167 self.caller.msg(f"Target area |C{self.lhs}|n not found.")
168 return
169
170 # Match source.
171 area2 = match_area(self.rhs)
172
173 # If we didn't find the source area, complain and exit.
174 if not area2:
175 self.caller.msg(f"Source area |C{self.rhs}|n not found.")
176 return
177
178 # If target already has a parent set, remove it from the children of the current parent.
179 old_parent = area1.db_parent or None
180 if old_parent and old_parent is not area2:
181 self.caller.msg(f"Removed area |C{area1.key}|n from children of old parent |C{old_parent.key}|n.")
182 old_parent.db_children.remove(area1)
183 old_parent.save()
184
185 # Sanity check.
186 if area1.db_children and area2 in area1.db_children.all():
187 self.caller.msg("An area cannot be both an ancestor and a child of the parent!")
188 return
189
190 # Set the new parent.
191 area1.db_parent = area2
192 area1.save()
193
194 # Add the target to the children of the source.
195 if not area1 in area2.db_children.all():
196 area2.db_children.add(area1)
197 area2.save()
198
199 # Let the player know what they did.
200 self.caller.msg(f"Parent of area |C{area1.key}|n set to |C{area2.key}|n.")
201
202
203 # List top-level areas (those without a parent).
204 def list_areas(self):
205
206 area = Area.objects.filter(db_parent__isnull=True)
207
208 table = self.styled_table("Name(ID)", "Children", width=self.caller.screenwidth)
209 for x in area:
210 children = itemize([f"{y.key}(#{y.id})" for y in x.db_children.all()])
211 table.add_row(f"{x.key}(#{x.id})", children)
212
213 self.caller.msg(str(table))
214
215 # Set the description of the area.
216 def set_area_desc(self):
217 if not self.lhs or not self.rhs:
218 self.caller.msg("Usage: area/desc <area>=<text>")
219 return
220
221 area = match_area(self.lhs)
222 if not area:
223 self.caller.msg(f"Area |C{self.lhs}|n not found.")
224 return
225
226 area.db_desc = self.rhs
227 area.save()
228
229 self.caller.msg(f"Area |C{area.key}|n desc {'set' if self.rhs else 'cleared'}.")
230
231 # View the children of an Area.
232 def area_children(self):
233 if not self.args:
234 self.caller.msg("Usage: area/children <area>")
235 return
236
237 area = match_area(self.args)
238 if not area:
239 self.caller.msg(f"Area |C{self.args}|n not found.")
240 return
241
242 if not area.db_children.count():
243 self.caller.msg(f"Area |C{area.key}|n does not have any children.")
244 return
245
246
247 table = self.styled_table("Area Name", "Parent", "Kids", width=self.client_width())
248 for x in area.db_children.all():
249 table.add_row(x.key, x.parent, x.db_children.count())
250 self.caller.msg(str(table))
251
252
253 # View the rooms of an Area.
254 def area_rooms(self):
255 if not self.args:
256 self.caller.msg("Usage: area/rooms <area>")
257 return
258
259 area = match_area(self.args)
260 if not area:
261 self.caller.msg(f"Area |C{self.args}|n not found.")
262 return
263
264
265 self.caller.msg(f"\nRooms in area |C{area.key}(#{area.id})|n:")
266 #self.caller.msg(f"|{self.caller.options.border_color}{'-' * 40}|n")
267 if area.db_rooms.count() == 0:
268 self.caller.msg(f"Area |C{area.key}|n does not have any children.")
269 return
270
271 table = self.styled_table("Room Name", "Area Name", width=40)
272 for x in area.db_rooms.all():
273 table.add_row(f"{x.key}({x.id})",
274 x.db.area.full_name() if x.db.area else "(None)")
275
276 self.caller.msg(str(table))
277 #self.caller.msg(f"|{self.caller.options.border_color}{'-' * 40}|n")
278
279
280 def area_clear(self):
281 if not self.args:
282 self.caller.msg("Usage: area/clear <area>")
283 return
284
285 area = match_area(self.args)
286 if not area:
287 self.caller.msg(f"Area |C{self.args}|n not found.")
288 return
289
290 area.db_parent = None
291 area.purge_rooms()
292 area.save()
293
294 self.caller.msg(f"Area |C{area.key}|n orphaned.")
295
296
297 def restore_area(self):
298 if not self.args:
299 self.caller.msg("Usage: area/restore <area>")
300 return
301
302 area = match_area(self.args)
303 if not area:
304 self.caller.msg(f"Area |C{self.args}|n not found.")
305 return
306
307 rooms = area.db_rooms.all()
308 if not rooms:
309 self.caller.msg(f"Area |C{area.key}|n does not contain any rooms.")
310 return
311
312 for x in rooms:
313 old = x.db.area
314 if not old or old != area:
315 x.db.area = area
316 self.caller.msg(f"Area set on |C{x.key}|n.")
317
318
319 # Cmdhandler.
320 def func(self):
321
322 # Allow area <target>=<source> syntax
323 if not self.switches:
324 if not self.args:
325 switch = "list"
326 else:
327 switch = "set"
328 else:
329 switch = self.switches[0]
330
331 if switch == "create":
332 self.create_area()
333 elif switch == "destroy":
334 self.destroy_area()
335 elif switch == "set":
336 self.set_area()
337 elif switch == "desc":
338 self.set_area_desc()
339 elif switch == "parent":
340 self.set_area_parent()
341 elif switch == "name":
342 self.set_area_name()
343 elif switch == "rooms":
344 self.area_rooms()
345 elif switch == "children":
346 self.area_children()
347 elif switch == "list":
348 self.list_areas()
349 elif switch == "clear":
350 self.area_clear()
351 elif switch == "restore":
352 self.restore_area()
353 else:
354 self.msg("No such switch.")
355
356def delete_area(sender, **kwargs):
357 model = kwargs.get('instance', None)
358 if model:
359 for x in model.db_rooms.all():
360 x.attributes.remove("area")
361
362
363post_delete.connect(delete_area, sender=Area)
364