rom24-quickmud-python 2.4.0__py3-none-any.whl → 2.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mud/commands/equipment.py CHANGED
@@ -8,7 +8,7 @@ from __future__ import annotations
8
8
 
9
9
  from typing import TYPE_CHECKING
10
10
 
11
- from mud.models.constants import ItemType, Position, WearLocation
11
+ from mud.models.constants import ItemType, Position, WearFlag, WearLocation
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from mud.models.character import Character
@@ -45,12 +45,13 @@ def do_wear(ch: Character, args: str) -> str:
45
45
 
46
46
  # Determine where this can be worn
47
47
  wear_flags = getattr(obj, "wear_flags", 0)
48
- item_type = getattr(obj, "item_type", ItemType.TRASH)
48
+ item_type_str = getattr(obj, "item_type", None)
49
+ item_type = int(item_type_str) if item_type_str else ItemType.TRASH
49
50
 
50
51
  # Weapons and held items should use wield/hold
51
52
  if item_type == ItemType.WEAPON:
52
53
  return "You need to wield weapons, not wear them."
53
- if wear_flags & WearLocation.WEAR_HOLD:
54
+ if wear_flags & WearFlag.HOLD:
54
55
  return "You need to hold that, not wear it."
55
56
 
56
57
  # Find appropriate wear location
@@ -73,7 +74,7 @@ def do_wear(ch: Character, args: str) -> str:
73
74
  obj.wear_loc = wear_loc
74
75
 
75
76
  # Remove from inventory
76
- inventory = getattr(ch, "carrying", [])
77
+ inventory = getattr(ch, "inventory", [])
77
78
  if obj in inventory:
78
79
  inventory.remove(obj)
79
80
 
@@ -105,8 +106,9 @@ def do_wield(ch: Character, args: str) -> str:
105
106
  if ch.position < Position.SLEEPING:
106
107
  return "You can't do that right now."
107
108
 
108
- # Check if it's a weapon
109
- item_type = getattr(obj, "item_type", ItemType.TRASH)
109
+ # Check if it's a weapon (item_type is stored as string, enum value is int)
110
+ item_type_str = getattr(obj, "item_type", None)
111
+ item_type = int(item_type_str) if item_type_str else ItemType.TRASH
110
112
  if item_type != ItemType.WEAPON:
111
113
  return "You can't wield that."
112
114
 
@@ -137,7 +139,7 @@ def do_wield(ch: Character, args: str) -> str:
137
139
  obj.wear_loc = wear_loc
138
140
 
139
141
  # Remove from inventory
140
- inventory = getattr(ch, "carrying", [])
142
+ inventory = getattr(ch, "inventory", [])
141
143
  if obj in inventory:
142
144
  inventory.remove(obj)
143
145
 
@@ -171,12 +173,12 @@ def do_hold(ch: Character, args: str) -> str:
171
173
 
172
174
  # Check if it can be held
173
175
  wear_flags = getattr(obj, "wear_flags", 0)
174
- if not (wear_flags & WearLocation.WEAR_HOLD):
176
+ if not (wear_flags & WearFlag.HOLD):
175
177
  return "You can't hold that."
176
178
 
177
179
  # Check if hold slot is occupied
178
180
  equipment = getattr(ch, "equipment", {})
179
- wear_loc = WearLocation.WEAR_HOLD
181
+ wear_loc = WearLocation.HOLD
180
182
 
181
183
  if wear_loc in equipment and equipment[wear_loc] is not None:
182
184
  existing = equipment[wear_loc]
@@ -191,14 +193,15 @@ def do_hold(ch: Character, args: str) -> str:
191
193
  obj.wear_loc = wear_loc
192
194
 
193
195
  # Remove from inventory
194
- inventory = getattr(ch, "carrying", [])
196
+ inventory = getattr(ch, "inventory", [])
195
197
  if obj in inventory:
196
198
  inventory.remove(obj)
197
199
 
198
200
  obj_name = getattr(obj, "short_descr", "something")
199
201
 
200
- # Special message for lights
201
- item_type = getattr(obj, "item_type", ItemType.TRASH)
202
+ # Special message for lights (item_type is stored as string)
203
+ item_type_str = getattr(obj, "item_type", None)
204
+ item_type = int(item_type_str) if item_type_str else ItemType.TRASH
202
205
  if item_type == ItemType.LIGHT:
203
206
  return f"You hold {obj_name} as your light."
204
207
 
@@ -207,7 +210,7 @@ def do_hold(ch: Character, args: str) -> str:
207
210
 
208
211
  def _wear_all(ch: Character) -> str:
209
212
  """Wear all wearable items in inventory."""
210
- inventory = getattr(ch, "carrying", [])
213
+ inventory = getattr(ch, "inventory", [])
211
214
  if not inventory:
212
215
  return "You are not carrying anything."
213
216
 
@@ -218,12 +221,13 @@ def _wear_all(ch: Character) -> str:
218
221
  continue
219
222
 
220
223
  # Skip weapons and held items
221
- item_type = getattr(obj, "item_type", ItemType.TRASH)
224
+ item_type_str = getattr(obj, "item_type", None)
225
+ item_type = int(item_type_str) if item_type_str else ItemType.TRASH
222
226
  wear_flags = getattr(obj, "wear_flags", 0)
223
227
 
224
228
  if item_type == ItemType.WEAPON:
225
229
  continue
226
- if wear_flags & WearLocation.WEAR_HOLD:
230
+ if wear_flags & WearFlag.HOLD:
227
231
  continue
228
232
 
229
233
  # Try to wear it
@@ -255,39 +259,40 @@ def _wear_all(ch: Character) -> str:
255
259
  def _get_wear_location(obj: Object, wear_flags: int) -> WearLocation | None:
256
260
  """Determine which slot an item should be worn in."""
257
261
  # Priority order for wear locations (from ROM)
258
- if wear_flags & WearLocation.WEAR_FINGER:
259
- return WearLocation.WEAR_FINGER
260
- if wear_flags & WearLocation.WEAR_NECK:
261
- return WearLocation.WEAR_NECK
262
- if wear_flags & WearLocation.WEAR_BODY:
263
- return WearLocation.WEAR_BODY
264
- if wear_flags & WearLocation.WEAR_HEAD:
265
- return WearLocation.WEAR_HEAD
266
- if wear_flags & WearLocation.WEAR_LEGS:
267
- return WearLocation.WEAR_LEGS
268
- if wear_flags & WearLocation.WEAR_FEET:
269
- return WearLocation.WEAR_FEET
270
- if wear_flags & WearLocation.WEAR_HANDS:
271
- return WearLocation.WEAR_HANDS
272
- if wear_flags & WearLocation.WEAR_ARMS:
273
- return WearLocation.WEAR_ARMS
274
- if wear_flags & WearLocation.WEAR_ABOUT:
275
- return WearLocation.WEAR_ABOUT
276
- if wear_flags & WearLocation.WEAR_WAIST:
277
- return WearLocation.WEAR_WAIST
278
- if wear_flags & WearLocation.WEAR_WRIST:
279
- return WearLocation.WEAR_WRIST
280
- if wear_flags & WearLocation.WEAR_SHIELD:
281
- return WearLocation.WEAR_SHIELD
282
- if wear_flags & WearLocation.WEAR_FLOAT:
283
- return WearLocation.WEAR_FLOAT
262
+ # Check WearFlag bits and return corresponding WearLocation slot
263
+ if wear_flags & WearFlag.WEAR_FINGER:
264
+ return WearLocation.FINGER_L # Will need multi-slot handling later
265
+ if wear_flags & WearFlag.WEAR_NECK:
266
+ return WearLocation.NECK_1
267
+ if wear_flags & WearFlag.WEAR_BODY:
268
+ return WearLocation.BODY
269
+ if wear_flags & WearFlag.WEAR_HEAD:
270
+ return WearLocation.HEAD
271
+ if wear_flags & WearFlag.WEAR_LEGS:
272
+ return WearLocation.LEGS
273
+ if wear_flags & WearFlag.WEAR_FEET:
274
+ return WearLocation.FEET
275
+ if wear_flags & WearFlag.WEAR_HANDS:
276
+ return WearLocation.HANDS
277
+ if wear_flags & WearFlag.WEAR_ARMS:
278
+ return WearLocation.ARMS
279
+ if wear_flags & WearFlag.WEAR_ABOUT:
280
+ return WearLocation.ABOUT
281
+ if wear_flags & WearFlag.WEAR_WAIST:
282
+ return WearLocation.WAIST
283
+ if wear_flags & WearFlag.WEAR_WRIST:
284
+ return WearLocation.WRIST_L # Will need multi-slot handling later
285
+ if wear_flags & WearFlag.WEAR_SHIELD:
286
+ return WearLocation.SHIELD
287
+ if wear_flags & WearFlag.WEAR_FLOAT:
288
+ return WearLocation.FLOAT
284
289
 
285
290
  return None
286
291
 
287
292
 
288
293
  def _find_obj_inventory(ch: Character, name: str) -> Object | None:
289
294
  """Find an object in character's inventory by name."""
290
- inventory = getattr(ch, "carrying", [])
295
+ inventory = getattr(ch, "inventory", [])
291
296
  if not inventory or not name:
292
297
  return None
293
298
 
@@ -3,6 +3,7 @@ Player essential object commands - put, remove, quaff, sacrifice.
3
3
 
4
4
  ROM Reference: src/act_obj.c
5
5
  """
6
+
6
7
  from __future__ import annotations
7
8
 
8
9
  from mud.models.character import Character
@@ -21,11 +22,11 @@ CONT_PUT_ON = 16
21
22
  def get_obj_list(char: Character, name: str, obj_list: list) -> object | None:
22
23
  """
23
24
  Find an object in a list by name.
24
-
25
+
25
26
  ROM Reference: src/handler.c get_obj_list
26
27
  """
27
28
  name_lower = name.lower()
28
-
29
+
29
30
  # Handle numbered prefix (2.sword, 3.potion)
30
31
  count = 0
31
32
  number = 1
@@ -33,26 +34,26 @@ def get_obj_list(char: Character, name: str, obj_list: list) -> object | None:
33
34
  parts = name.split(".", 1)
34
35
  number = int(parts[0])
35
36
  name_lower = parts[1].lower()
36
-
37
+
37
38
  for obj in obj_list:
38
39
  obj_name = getattr(obj, "name", "").lower()
39
40
  short = getattr(obj, "short_descr", "").lower()
40
-
41
+
41
42
  # Check if name matches any keyword
42
43
  if name_lower in obj_name.split() or name_lower in obj_name or name_lower in short:
43
44
  count += 1
44
45
  if count == number:
45
46
  return obj
46
-
47
+
47
48
  return None
48
49
 
49
50
 
50
51
  def do_put(char: Character, args: str) -> str:
51
52
  """
52
53
  Put an item into a container.
53
-
54
+
54
55
  ROM Reference: src/act_obj.c do_put (lines 346-490)
55
-
56
+
56
57
  Usage:
57
58
  - put <item> <container>
58
59
  - put <item> in <container>
@@ -61,156 +62,156 @@ def do_put(char: Character, args: str) -> str:
61
62
  """
62
63
  if not args or not args.strip():
63
64
  return "Put what in what?"
64
-
65
+
65
66
  parts = args.strip().split()
66
67
  if len(parts) < 2:
67
68
  return "Put what in what?"
68
-
69
+
69
70
  item_name = parts[0]
70
71
  container_name = parts[-1]
71
-
72
+
72
73
  # Handle "put x in y" or "put x on y"
73
74
  if len(parts) >= 3 and parts[1].lower() in ("in", "on"):
74
75
  container_name = parts[2] if len(parts) > 2 else parts[-1]
75
-
76
+
76
77
  # Can't put into all
77
78
  if container_name.lower() == "all" or container_name.lower().startswith("all."):
78
79
  return "You can't do that."
79
-
80
+
80
81
  # Find the container
81
82
  container = get_obj_here(char, container_name)
82
83
  if container is None:
83
84
  return f"I see no {container_name} here."
84
-
85
+
85
86
  # Check if it's a container
86
87
  item_type = _get_item_type(container)
87
88
  if item_type != ItemType.CONTAINER and str(item_type) != "container":
88
89
  return "That's not a container."
89
-
90
+
90
91
  # Check if closed
91
92
  container_value = getattr(container, "value", [0, 0, 0, 0, 0])
92
93
  if len(container_value) > 1 and (container_value[1] & CONT_CLOSED):
93
94
  container_name = getattr(container, "name", "container")
94
95
  return f"The {container_name.split()[0]} is closed."
95
-
96
+
96
97
  # Handle single item or all
97
98
  if item_name.lower() != "all" and not item_name.lower().startswith("all."):
98
99
  # Single item
99
100
  obj = get_obj_carry(char, item_name)
100
101
  if obj is None:
101
102
  return "You do not have that item."
102
-
103
+
103
104
  if obj is container:
104
105
  return "You can't fold it into itself."
105
-
106
+
106
107
  # Check if can drop
107
108
  if not _can_drop_obj(char, obj):
108
109
  return "You can't let go of it."
109
-
110
+
110
111
  # Check weight
111
112
  obj_weight = _get_obj_weight(obj)
112
113
  container_weight = _get_true_weight(container)
113
114
  max_weight = container_value[0] * 10 if len(container_value) > 0 else 1000
114
115
  max_single = container_value[3] * 10 if len(container_value) > 3 else 1000
115
-
116
+
116
117
  if obj_weight + container_weight > max_weight or obj_weight > max_single:
117
118
  return "It won't fit."
118
-
119
+
119
120
  # Transfer the item
120
121
  _obj_from_char(char, obj)
121
122
  _obj_to_obj(obj, container)
122
-
123
+
123
124
  obj_name = getattr(obj, "short_descr", "something")
124
125
  container_short = getattr(container, "short_descr", "something")
125
-
126
+
126
127
  if len(container_value) > 1 and (container_value[1] & CONT_PUT_ON):
127
128
  return f"You put {obj_name} on {container_short}."
128
129
  else:
129
130
  return f"You put {obj_name} in {container_short}."
130
-
131
+
131
132
  else:
132
133
  # Put all or all.<type>
133
134
  filter_name = None
134
135
  if item_name.lower().startswith("all."):
135
136
  filter_name = item_name[4:].lower()
136
-
137
+
137
138
  carrying = list(getattr(char, "carrying", []))
138
139
  count = 0
139
140
  messages = []
140
-
141
+
141
142
  for obj in carrying:
142
143
  # Skip if doesn't match filter
143
144
  if filter_name:
144
145
  obj_name = getattr(obj, "name", "").lower()
145
146
  if filter_name not in obj_name:
146
147
  continue
147
-
148
+
148
149
  # Skip if worn
149
150
  if getattr(obj, "wear_loc", -1) != -1:
150
151
  continue
151
-
152
+
152
153
  # Skip container itself
153
154
  if obj is container:
154
155
  continue
155
-
156
+
156
157
  # Skip if can't drop
157
158
  if not _can_drop_obj(char, obj):
158
159
  continue
159
-
160
+
160
161
  # Check weight
161
162
  obj_weight = _get_obj_weight(obj)
162
163
  container_weight = _get_true_weight(container)
163
164
  max_weight = container_value[0] * 10 if len(container_value) > 0 else 1000
164
165
  max_single = container_value[3] * 10 if len(container_value) > 3 else 1000
165
-
166
+
166
167
  if obj_weight + container_weight > max_weight or obj_weight > max_single:
167
168
  continue
168
-
169
+
169
170
  # Transfer
170
171
  _obj_from_char(char, obj)
171
172
  _obj_to_obj(obj, container)
172
-
173
+
173
174
  obj_short = getattr(obj, "short_descr", "something")
174
175
  container_short = getattr(container, "short_descr", "something")
175
-
176
+
176
177
  if len(container_value) > 1 and (container_value[1] & CONT_PUT_ON):
177
178
  messages.append(f"You put {obj_short} on {container_short}.")
178
179
  else:
179
180
  messages.append(f"You put {obj_short} in {container_short}.")
180
181
  count += 1
181
-
182
+
182
183
  if count == 0:
183
184
  return "You have nothing to put."
184
-
185
+
185
186
  return "\n".join(messages)
186
187
 
187
188
 
188
189
  def do_remove(char: Character, args: str) -> str:
189
190
  """
190
191
  Remove a worn item.
191
-
192
+
192
193
  ROM Reference: src/act_obj.c do_remove (lines 1740-1763)
193
-
194
+
194
195
  Usage: remove <item>
195
196
  """
196
197
  if not args or not args.strip():
197
198
  return "Remove what?"
198
-
199
+
199
200
  item_name = args.strip().split()[0]
200
-
201
+
201
202
  # Find worn item
202
203
  obj = get_obj_wear(char, item_name)
203
204
  if obj is None:
204
205
  return "You do not have that item."
205
-
206
+
206
207
  # Get wear location
207
208
  wear_loc = getattr(obj, "wear_loc", -1)
208
209
  if wear_loc == -1:
209
210
  return "You aren't wearing that."
210
-
211
+
211
212
  # Remove the item
212
213
  _remove_obj(char, obj)
213
-
214
+
214
215
  obj_name = getattr(obj, "short_descr", "something")
215
216
  return f"You stop using {obj_name}."
216
217
 
@@ -218,64 +219,64 @@ def do_remove(char: Character, args: str) -> str:
218
219
  def do_sacrifice(char: Character, args: str) -> str:
219
220
  """
220
221
  Sacrifice an item for silver coins.
221
-
222
+
222
223
  ROM Reference: src/act_obj.c do_sacrifice (lines 1765-1862)
223
-
224
+
224
225
  Usage: sacrifice <item>
225
226
  """
226
227
  if not args or not args.strip():
227
228
  # Self-sacrifice message
228
229
  char_name = getattr(char, "name", "someone")
229
230
  return "Mota appreciates your offer and may accept it later."
230
-
231
+
231
232
  item_name = args.strip().split()[0]
232
233
  char_name = getattr(char, "name", "someone")
233
-
234
+
234
235
  if item_name.lower() == char_name.lower():
235
236
  return "Mota appreciates your offer and may accept it later."
236
-
237
+
237
238
  # Find item in room
238
239
  room = getattr(char, "room", None)
239
240
  if not room:
240
241
  return "You can't find it."
241
-
242
+
242
243
  contents = getattr(room, "contents", [])
243
244
  obj = get_obj_list(char, item_name, contents)
244
-
245
+
245
246
  if obj is None:
246
247
  return "You can't find it."
247
-
248
+
248
249
  # Check for PC corpse with contents
249
250
  item_type = _get_item_type(obj)
250
251
  if item_type == ItemType.CORPSE_PC or str(item_type) == "corpse_pc":
251
252
  obj_contents = getattr(obj, "contains", [])
252
253
  if obj_contents:
253
254
  return "Mota wouldn't like that."
254
-
255
+
255
256
  # Check if can take/sacrifice
256
257
  wear_flags = getattr(obj, "wear_flags", 0)
257
258
  if not hasattr(obj, "wear_flags"):
258
259
  proto = getattr(obj, "prototype", None)
259
260
  if proto:
260
261
  wear_flags = getattr(proto, "wear_flags", 0)
261
-
262
+
262
263
  extra_flags = getattr(obj, "extra_flags", 0)
263
264
  if not hasattr(obj, "extra_flags"):
264
265
  proto = getattr(obj, "prototype", None)
265
266
  if proto:
266
267
  extra_flags = getattr(proto, "extra_flags", 0)
267
-
268
+
268
269
  ITEM_TAKE = 1
269
270
  ITEM_NO_SAC = 0x4000 # Bit 14
270
-
271
+
271
272
  if not (wear_flags & ITEM_TAKE):
272
273
  obj_name = getattr(obj, "short_descr", "That")
273
274
  return f"{obj_name} is not an acceptable sacrifice."
274
-
275
+
275
276
  if extra_flags & ITEM_NO_SAC:
276
277
  obj_name = getattr(obj, "short_descr", "That")
277
278
  return f"{obj_name} is not an acceptable sacrifice."
278
-
279
+
279
280
  # Check if someone is using the object
280
281
  room_people = getattr(room, "people", [])
281
282
  for person in room_people:
@@ -283,24 +284,23 @@ def do_sacrifice(char: Character, args: str) -> str:
283
284
  person_name = getattr(person, "name", "Someone")
284
285
  obj_name = getattr(obj, "short_descr", "it")
285
286
  return f"{person_name} appears to be using {obj_name}."
286
-
287
+
287
288
  # Calculate silver reward
288
289
  obj_level = getattr(obj, "level", 1)
289
290
  obj_cost = getattr(obj, "cost", 0)
290
-
291
+
291
292
  silver = max(1, obj_level * 3)
292
-
293
+
293
294
  # Non-corpse items capped at cost
294
- if item_type not in (ItemType.CORPSE_NPC, ItemType.CORPSE_PC) and \
295
- str(item_type) not in ("corpse_npc", "corpse_pc"):
295
+ if item_type not in (ItemType.CORPSE_NPC, ItemType.CORPSE_PC) and str(item_type) not in ("corpse_npc", "corpse_pc"):
296
296
  silver = min(silver, obj_cost) if obj_cost > 0 else silver
297
-
297
+
298
298
  # Give silver
299
299
  char.silver = getattr(char, "silver", 0) + silver
300
-
300
+
301
301
  # Remove object
302
302
  _extract_obj(char, obj)
303
-
303
+
304
304
  # Auto-split if enabled
305
305
  act_flags = getattr(char, "act", 0)
306
306
  PLR_AUTOSPLIT = 0x00002000
@@ -309,8 +309,9 @@ def do_sacrifice(char: Character, args: str) -> str:
309
309
  members = _count_group_members(char)
310
310
  if members > 1:
311
311
  from mud.commands.group_commands import do_split
312
+
312
313
  do_split(char, str(silver))
313
-
314
+
314
315
  if silver == 1:
315
316
  return "Mota gives you one silver coin for your sacrifice."
316
317
  else:
@@ -320,51 +321,52 @@ def do_sacrifice(char: Character, args: str) -> str:
320
321
  def do_quaff(char: Character, args: str) -> str:
321
322
  """
322
323
  Drink a potion.
323
-
324
+
324
325
  ROM Reference: src/act_obj.c do_quaff (lines 1865-1906)
325
-
326
+
326
327
  Usage: quaff <potion>
327
328
  """
328
329
  if not args or not args.strip():
329
330
  return "Quaff what?"
330
-
331
+
331
332
  item_name = args.strip().split()[0]
332
-
333
+
333
334
  # Find potion in inventory
334
335
  obj = get_obj_carry(char, item_name)
335
336
  if obj is None:
336
337
  return "You do not have that potion."
337
-
338
+
338
339
  # Check if it's a potion
339
340
  item_type = _get_item_type(obj)
340
341
  if item_type != ItemType.POTION and str(item_type) != "potion":
341
342
  return "You can quaff only potions."
342
-
343
+
343
344
  # Check level
344
345
  obj_level = getattr(obj, "level", 1)
345
346
  char_level = getattr(char, "level", 1)
346
-
347
+
347
348
  if char_level < obj_level:
348
349
  return "This liquid is too powerful for you to drink."
349
-
350
+
350
351
  obj_name = getattr(obj, "short_descr", "something")
351
-
352
+
352
353
  # Cast the spells from the potion
353
354
  obj_value = getattr(obj, "value", [0, 0, 0, 0, 0])
354
355
  spell_level = obj_value[0] if len(obj_value) > 0 else 1
355
-
356
+
356
357
  for i in range(1, 4):
357
358
  if len(obj_value) > i and obj_value[i]:
358
359
  _obj_cast_spell(obj_value[i], spell_level, char, char, None)
359
-
360
+
360
361
  # Remove the potion
361
362
  _extract_obj(char, obj)
362
-
363
+
363
364
  return f"You quaff {obj_name}."
364
365
 
365
366
 
366
367
  # Helper functions
367
368
 
369
+
368
370
  def _get_item_type(obj) -> ItemType:
369
371
  """Get item type from object or prototype."""
370
372
  item_type = getattr(obj, "item_type", None)
@@ -382,12 +384,12 @@ def _get_obj_weight(obj) -> int:
382
384
  proto = getattr(obj, "prototype", None)
383
385
  if proto:
384
386
  weight = getattr(proto, "weight", 0)
385
-
387
+
386
388
  # Add contents weight
387
389
  contains = getattr(obj, "contains", [])
388
390
  for contained in contains:
389
391
  weight += _get_obj_weight(contained)
390
-
392
+
391
393
  return weight
392
394
 
393
395
 
@@ -415,7 +417,7 @@ def _obj_from_char(char: Character, obj) -> None:
415
417
  if obj in carrying:
416
418
  carrying.remove(obj)
417
419
  obj.carried_by = None
418
-
420
+
419
421
  # Update carry weight/number
420
422
  weight = _get_obj_weight(obj)
421
423
  char.carry_weight = max(0, getattr(char, "carry_weight", 0) - weight)
@@ -437,16 +439,25 @@ def _remove_obj(char: Character, obj) -> None:
437
439
  wear_loc = getattr(obj, "wear_loc", -1)
438
440
  if wear_loc == -1:
439
441
  return
440
-
442
+
441
443
  obj.wear_loc = -1 # WEAR_NONE
442
-
443
- # Move to inventory
444
- carrying = getattr(char, "carrying", None)
445
- if carrying is None:
446
- char.carrying = []
447
- carrying = char.carrying
448
- if obj not in carrying:
449
- carrying.append(obj)
444
+
445
+ # Remove from equipment dict
446
+ equipment = getattr(char, "equipment", {})
447
+ if equipment:
448
+ # Find and remove from equipment dict by value
449
+ for slot, equipped_obj in list(equipment.items()):
450
+ if equipped_obj == obj:
451
+ del equipment[slot]
452
+ break
453
+
454
+ # Move to inventory (Character model uses 'inventory', not 'carrying')
455
+ inventory = getattr(char, "inventory", None)
456
+ if inventory is None:
457
+ char.inventory = []
458
+ inventory = char.inventory
459
+ if obj not in inventory:
460
+ inventory.append(obj)
450
461
 
451
462
 
452
463
  def _extract_obj(char: Character, obj) -> None:
@@ -456,11 +467,11 @@ def _extract_obj(char: Character, obj) -> None:
456
467
  contents = getattr(room, "contents", [])
457
468
  if obj in contents:
458
469
  contents.remove(obj)
459
-
470
+
460
471
  carrying = getattr(char, "carrying", [])
461
472
  if obj in carrying:
462
473
  carrying.remove(obj)
463
-
474
+
464
475
  # Clear references
465
476
  obj.in_room = None
466
477
  obj.carried_by = None
@@ -472,13 +483,13 @@ def _count_group_members(char: Character) -> int:
472
483
  room = getattr(char, "room", None)
473
484
  if not room:
474
485
  return 1
475
-
486
+
476
487
  count = 0
477
488
  room_people = getattr(room, "people", [])
478
489
  for person in room_people:
479
490
  if _is_same_group(person, char):
480
491
  count += 1
481
-
492
+
482
493
  return max(1, count)
483
494
 
484
495
 
@@ -486,11 +497,11 @@ def _is_same_group(char1: Character, char2: Character) -> bool:
486
497
  """Check if two characters are in the same group."""
487
498
  if char1 is char2:
488
499
  return True
489
-
500
+
490
501
  # Check if following same leader
491
502
  leader1 = getattr(char1, "leader", None) or char1
492
503
  leader2 = getattr(char2, "leader", None) or char2
493
-
504
+
494
505
  return leader1 is leader2
495
506
 
496
507
 
mud/world/obj_find.py CHANGED
@@ -3,6 +3,7 @@ Object finding utilities - find objects by name.
3
3
 
4
4
  ROM Reference: src/handler.c get_obj_carry, get_obj_wear, get_obj_here, etc.
5
5
  """
6
+
6
7
  from __future__ import annotations
7
8
 
8
9
  from typing import TYPE_CHECKING
@@ -15,16 +16,16 @@ if TYPE_CHECKING:
15
16
  def get_obj_carry(char: "Character", name: str) -> "Obj | None":
16
17
  """
17
18
  Find an object in character's inventory by name.
18
-
19
+
19
20
  ROM Reference: src/handler.c get_obj_carry
20
21
  """
21
22
  if not name:
22
23
  return None
23
-
24
+
24
25
  # Parse N.name format
25
26
  count = 0
26
27
  target_count = 1
27
-
28
+
28
29
  if "." in name:
29
30
  parts = name.split(".", 1)
30
31
  try:
@@ -32,35 +33,35 @@ def get_obj_carry(char: "Character", name: str) -> "Obj | None":
32
33
  name = parts[1]
33
34
  except ValueError:
34
35
  pass
35
-
36
+
36
37
  name_lower = name.lower()
37
-
38
+
38
39
  # Search inventory
39
40
  for obj in getattr(char, "carrying", []):
40
41
  obj_name = getattr(obj, "name", "").lower()
41
42
  obj_short = (getattr(obj, "short_descr", "") or "").lower()
42
-
43
+
43
44
  if name_lower in obj_name or name_lower in obj_short:
44
45
  count += 1
45
46
  if count == target_count:
46
47
  return obj
47
-
48
+
48
49
  return None
49
50
 
50
51
 
51
52
  def get_obj_wear(char: "Character", name: str) -> "Obj | None":
52
53
  """
53
54
  Find an object in character's equipment by name.
54
-
55
+
55
56
  ROM Reference: src/handler.c get_obj_wear
56
57
  """
57
58
  if not name:
58
59
  return None
59
-
60
+
60
61
  # Parse N.name format
61
62
  count = 0
62
63
  target_count = 1
63
-
64
+
64
65
  if "." in name:
65
66
  parts = name.split(".", 1)
66
67
  try:
@@ -68,32 +69,32 @@ def get_obj_wear(char: "Character", name: str) -> "Obj | None":
68
69
  name = parts[1]
69
70
  except ValueError:
70
71
  pass
71
-
72
+
72
73
  name_lower = name.lower()
73
-
74
- # Search equipment
75
- equipped = getattr(char, "equipped", {})
74
+
75
+ # Search equipment (Character model uses 'equipment', not 'equipped')
76
+ equipped = getattr(char, "equipment", {})
76
77
  for obj in equipped.values():
77
78
  if obj is None:
78
79
  continue
79
-
80
+
80
81
  obj_name = getattr(obj, "name", "").lower()
81
82
  obj_short = (getattr(obj, "short_descr", "") or "").lower()
82
-
83
+
83
84
  if name_lower in obj_name or name_lower in obj_short:
84
85
  count += 1
85
86
  if count == target_count:
86
87
  return obj
87
-
88
+
88
89
  return None
89
90
 
90
91
 
91
92
  def get_obj_here(char: "Character", name: str) -> "Obj | None":
92
93
  """
93
94
  Find an object in the room by name.
94
-
95
+
95
96
  ROM Reference: src/handler.c get_obj_here
96
-
97
+
97
98
  Searches:
98
99
  1. Character's inventory
99
100
  2. Character's equipment
@@ -101,26 +102,26 @@ def get_obj_here(char: "Character", name: str) -> "Obj | None":
101
102
  """
102
103
  if not name:
103
104
  return None
104
-
105
+
105
106
  # Check inventory first
106
107
  obj = get_obj_carry(char, name)
107
108
  if obj:
108
109
  return obj
109
-
110
+
110
111
  # Check equipment
111
112
  obj = get_obj_wear(char, name)
112
113
  if obj:
113
114
  return obj
114
-
115
+
115
116
  # Check room
116
117
  room = getattr(char, "room", None)
117
118
  if not room:
118
119
  return None
119
-
120
+
120
121
  # Parse N.name format
121
122
  count = 0
122
123
  target_count = 1
123
-
124
+
124
125
  if "." in name:
125
126
  parts = name.split(".", 1)
126
127
  try:
@@ -128,36 +129,36 @@ def get_obj_here(char: "Character", name: str) -> "Obj | None":
128
129
  name = parts[1]
129
130
  except ValueError:
130
131
  pass
131
-
132
+
132
133
  name_lower = name.lower()
133
-
134
+
134
135
  for obj in getattr(room, "contents", []):
135
136
  obj_name = getattr(obj, "name", "").lower()
136
137
  obj_short = (getattr(obj, "short_descr", "") or "").lower()
137
-
138
+
138
139
  if name_lower in obj_name or name_lower in obj_short:
139
140
  count += 1
140
141
  if count == target_count:
141
142
  return obj
142
-
143
+
143
144
  return None
144
145
 
145
146
 
146
147
  def get_obj_world(char: "Character", name: str) -> "Obj | None":
147
148
  """
148
149
  Find an object anywhere in the world by name.
149
-
150
+
150
151
  ROM Reference: src/handler.c get_obj_world
151
152
  """
152
153
  from mud.registry import object_registry
153
-
154
+
154
155
  if not name:
155
156
  return None
156
-
157
+
157
158
  # Parse N.name format
158
159
  count = 0
159
160
  target_count = 1
160
-
161
+
161
162
  if "." in name:
162
163
  parts = name.split(".", 1)
163
164
  try:
@@ -165,16 +166,16 @@ def get_obj_world(char: "Character", name: str) -> "Obj | None":
165
166
  name = parts[1]
166
167
  except ValueError:
167
168
  pass
168
-
169
+
169
170
  name_lower = name.lower()
170
-
171
+
171
172
  for obj in object_registry.values():
172
173
  obj_name = getattr(obj, "name", "").lower()
173
174
  obj_short = (getattr(obj, "short_descr", "") or "").lower()
174
-
175
+
175
176
  if name_lower in obj_name or name_lower in obj_short:
176
177
  count += 1
177
178
  if count == target_count:
178
179
  return obj
179
-
180
+
180
181
  return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rom24-quickmud-python
3
- Version: 2.4.0
3
+ Version: 2.4.2
4
4
  Summary: A modern Python port of the ROM 2.4b6 MUD engine with full telnet server and JSON world loading
5
5
  Author-email: Mark Jedrzejczyk <mark.jedrzejczyk@gmail.com>
6
6
  Maintainer-email: Mark Jedrzejczyk <mark.jedrzejczyk@gmail.com>
@@ -53,7 +53,7 @@ Dynamic: license-file
53
53
 
54
54
  # QuickMUD - A Modern ROM 2.4 Python Port
55
55
 
56
- [![Version](https://img.shields.io/badge/version-2.3.1-blue.svg)](https://github.com/avinson/rom24-quickmud)
56
+ [![Version](https://img.shields.io/badge/version-2.4.2-blue.svg)](https://github.com/avinson/rom24-quickmud)
57
57
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
58
58
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
59
59
  [![Tests](https://img.shields.io/badge/tests-1555%20passing-brightgreen.svg)](https://github.com/Nostoi/rom24-quickmud-python)
@@ -55,7 +55,7 @@ mud/commands/consumption.py,sha256=oKhXIS0mPRr-qq8HRxEHrrsSaHY_rgfjqjd8xC46_Eg,8
55
55
  mud/commands/decorators.py,sha256=n_ezcovFkycAZOVxg0UNtMZLtiSlF0Yj8yqYt4YVY1U,345
56
56
  mud/commands/dispatcher.py,sha256=cWfQaUG8KH3KAwZKFb5UC9Ti-8KGJO4dsCIZ4heKd2w,38406
57
57
  mud/commands/doors.py,sha256=M3TUHyD2PULh5H4Dsj1k6Jgn8qvAIWW687d2tyTVdyM,16058
58
- mud/commands/equipment.py,sha256=RknjKfCLeR1djSHF3jvB3LHcTjFpuuRJJNSgdtnn17A,9075
58
+ mud/commands/equipment.py,sha256=3k_hfmY_DgwN94cMkGrwfmYWfSDsAYPWvGwYjyfo-JI,9445
59
59
  mud/commands/feedback.py,sha256=qEmUlVy1KC6ludpAREdqvf8gpa8jmgN4AMLor0EoO7E,2472
60
60
  mud/commands/give.py,sha256=njzoCLD6m2ZKzvi8yjBwzJbR-hrN7JtBCKDxXsvn1zc,6760
61
61
  mud/commands/group_commands.py,sha256=wmJj07pSImQLzGQ7QrGZPpmRGlhMQoEK4tj_cWMTLlg,12413
@@ -84,7 +84,7 @@ mud/commands/mobprog_tools.py,sha256=1OXRolsjmVuBn_FtKc_I1M_sTYLYgSspqc8U9f5xFLA
84
84
  mud/commands/movement.py,sha256=JT67SMTQ8ZRISEcaSnYBNxk37_JAxDgt1AaOYuyBAN0,2725
85
85
  mud/commands/murder.py,sha256=tLYlhu2OJ52_LCOEweAC-3J4B4okATPOuwISfauazNE,3783
86
86
  mud/commands/notes.py,sha256=DqKaCnUuKenpdItUJMTOW9CHeZ5F2Xg4Df9JMB0OEoU,15794
87
- mud/commands/obj_manipulation.py,sha256=g4ZTIUrt6mdq7jqfMR7KymeLETEWF2sNZZVGEbTVo9s,15466
87
+ mud/commands/obj_manipulation.py,sha256=om_RyP9mwlrpi2ZvMSe4i55Xq7idpaS1OQOn-ZYM5mU,15370
88
88
  mud/commands/player_config.py,sha256=1J7v-viVe40slfLfzDC42ZLLMwF0vixdWkWFlMIvc0I,6082
89
89
  mud/commands/player_info.py,sha256=rZBb3s4saq9kVyjP5Xg7K6udPiZTyptp9O0Ynpe8Etw,4811
90
90
  mud/commands/position.py,sha256=hK-VBeiKjvngAbGlhzXk_GRwIaZu4bXb3FMHuH8L-6U,2418
@@ -197,12 +197,12 @@ mud/world/char_find.py,sha256=9k4q1ucT1O_YvN1WUMKh-ePfA1eSEwR2JP0fAyg1P4Q,3523
197
197
  mud/world/linking.py,sha256=mxEvgqA9Y6sDGXxN46aauU-jBhx4dOUcbgq64fIVz8g,1169
198
198
  mud/world/look.py,sha256=n9ecKP3y9lEZuzKn2GHsXyd-azfD_vbfeHHCGdEKZls,9631
199
199
  mud/world/movement.py,sha256=Y7it7pXrPORgKyy2tRB8br_kb4-s9UK-gj0N-E2U9oM,18695
200
- mud/world/obj_find.py,sha256=7QjVAhA-XEqAEm0CoanNtBuYj6rKijNSu8Vu1JcueMY,4304
200
+ mud/world/obj_find.py,sha256=4VAUSwWxhyYIMZIfxdDhKMAP5FZ0NpRikgX02vy0elo,4201
201
201
  mud/world/vision.py,sha256=q8VjzSzm0cbNrHX6-o0j1UG-jlcM3Z9bzUxK6T-Bsi8,9862
202
202
  mud/world/world_state.py,sha256=W9ABMADlY5H-ZmywACsryFKZp34OufjzMRD6WT331qg,7917
203
- rom24_quickmud_python-2.4.0.dist-info/licenses/LICENSE,sha256=anQ2j9As6sIC8tZgQCXbo0-09JDH9vPiqhA9djnOvkY,1078
204
- rom24_quickmud_python-2.4.0.dist-info/METADATA,sha256=fbzQz278u1MuZZ0fLJF9EKAtwrmNG5hmO_AoIBGkJjY,11748
205
- rom24_quickmud_python-2.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
206
- rom24_quickmud_python-2.4.0.dist-info/entry_points.txt,sha256=VFru08UQTXZA_CkK-NBjJmWHyEX5a3864fQHjhaojbw,41
207
- rom24_quickmud_python-2.4.0.dist-info/top_level.txt,sha256=Fk1WPmabIIjp7_iZXLYpbAVqiq7lG7TeAHt30AsOKtQ,4
208
- rom24_quickmud_python-2.4.0.dist-info/RECORD,,
203
+ rom24_quickmud_python-2.4.2.dist-info/licenses/LICENSE,sha256=anQ2j9As6sIC8tZgQCXbo0-09JDH9vPiqhA9djnOvkY,1078
204
+ rom24_quickmud_python-2.4.2.dist-info/METADATA,sha256=WfHxo2qytCggbZ2GW2IVwi9XOcU9uXx-gk-ME3N7iAo,11748
205
+ rom24_quickmud_python-2.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
206
+ rom24_quickmud_python-2.4.2.dist-info/entry_points.txt,sha256=VFru08UQTXZA_CkK-NBjJmWHyEX5a3864fQHjhaojbw,41
207
+ rom24_quickmud_python-2.4.2.dist-info/top_level.txt,sha256=Fk1WPmabIIjp7_iZXLYpbAVqiq7lG7TeAHt30AsOKtQ,4
208
+ rom24_quickmud_python-2.4.2.dist-info/RECORD,,