rom24-quickmud-python 2.14.264__py3-none-any.whl → 2.14.298__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/admin_logging/admin.py +3 -3
- mud/combat/engine.py +19 -0
- mud/commands/character.py +4 -2
- mud/commands/combat.py +28 -5
- mud/commands/compare.py +4 -1
- mud/commands/doors.py +10 -6
- mud/commands/equipment.py +69 -13
- mud/commands/healer.py +6 -1
- mud/commands/info.py +7 -2
- mud/commands/inspection.py +6 -2
- mud/commands/inventory.py +23 -6
- mud/commands/obj_manipulation.py +3 -3
- mud/commands/session.py +18 -9
- mud/commands/socials.py +5 -2
- mud/skills/handlers.py +135 -34
- mud/spawning/templates.py +14 -0
- mud/world/look.py +126 -39
- {rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/METADATA +3 -3
- {rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/RECORD +23 -23
- {rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/WHEEL +0 -0
- {rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/entry_points.txt +0 -0
- {rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/licenses/LICENSE +0 -0
- {rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/top_level.txt +0 -0
mud/admin_logging/admin.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from datetime import
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
@@ -100,7 +100,7 @@ def log_admin_command(
|
|
|
100
100
|
"""
|
|
101
101
|
|
|
102
102
|
Path("log").mkdir(exist_ok=True)
|
|
103
|
-
timestamp = datetime.now(
|
|
103
|
+
timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
|
|
104
104
|
sanitized = _sanitize_command_line(command_line)
|
|
105
105
|
if character is not None:
|
|
106
106
|
try:
|
|
@@ -132,7 +132,7 @@ def rotate_admin_log(today: datetime | None = None) -> Path:
|
|
|
132
132
|
active = log_dir / "admin.log"
|
|
133
133
|
if not active.exists():
|
|
134
134
|
return active
|
|
135
|
-
dt = today or datetime.now(
|
|
135
|
+
dt = today or datetime.now(timezone.utc)
|
|
136
136
|
dated = log_dir / f"admin-{dt.strftime('%Y%m%d')}.log"
|
|
137
137
|
# Avoid clobbering: if dated file exists, append current log and remove active
|
|
138
138
|
if dated.exists():
|
mud/combat/engine.py
CHANGED
|
@@ -688,6 +688,25 @@ def apply_damage(
|
|
|
688
688
|
if victim.position == Position.DEAD:
|
|
689
689
|
return "Already dead."
|
|
690
690
|
|
|
691
|
+
# FIGHT-093 — "Stop up any residual loopholes." (ROM src/fight.c:697-713).
|
|
692
|
+
# A physical hit (dt >= TYPE_HIT) dealing more than 1200 raw damage is clamped
|
|
693
|
+
# to 1200; a non-immortal attacker is penalized with "You really shouldn't
|
|
694
|
+
# cheat." and has their wielded weapon extracted from the game (extract_obj).
|
|
695
|
+
# This runs before the >35/>80 reduction, exactly as in ROM.
|
|
696
|
+
if damage > 1200 and _should_check_weapon_defenses(dt):
|
|
697
|
+
damage = 1200
|
|
698
|
+
is_imm = getattr(attacker, "is_immortal", None)
|
|
699
|
+
attacker_immortal = bool(is_imm()) if callable(is_imm) else False
|
|
700
|
+
if not attacker_immortal:
|
|
701
|
+
# mirroring ROM src/fight.c:707-711 — get_eq_char(ch, WEAR_WIELD) then
|
|
702
|
+
# send "You really shouldn't cheat." and extract_obj on the weapon.
|
|
703
|
+
weapon = get_wielded_weapon(attacker)
|
|
704
|
+
_push_message(attacker, "You really shouldn't cheat.")
|
|
705
|
+
if weapon is not None:
|
|
706
|
+
from mud.game_loop import _extract_obj
|
|
707
|
+
|
|
708
|
+
_extract_obj(weapon)
|
|
709
|
+
|
|
691
710
|
# mirroring ROM src/fight.c:717-720 — damage soft-cap applied before all other
|
|
692
711
|
# modifiers; reduces raw spikes to prevent instant-kill from single high-damage hits.
|
|
693
712
|
if damage > 35:
|
mud/commands/character.py
CHANGED
|
@@ -38,7 +38,8 @@ def do_password(ch: Character, args: str) -> str:
|
|
|
38
38
|
parts = args.split(None, 1)
|
|
39
39
|
|
|
40
40
|
if len(parts) < 2:
|
|
41
|
-
|
|
41
|
+
# PASSWORD-002: ROM src/act_info.c:2889 — trailing period.
|
|
42
|
+
return "Syntax: password <old> <new>."
|
|
42
43
|
|
|
43
44
|
old_password = parts[0]
|
|
44
45
|
new_password = parts[1]
|
|
@@ -57,7 +58,8 @@ def do_password(ch: Character, args: str) -> str:
|
|
|
57
58
|
# The macro is UMAX(ch->wait, 40); a plain `= 40` would lower a higher
|
|
58
59
|
# existing wait. Use the canonical UMAX helper.
|
|
59
60
|
apply_wait_state(ch, 40)
|
|
60
|
-
|
|
61
|
+
# PASSWORD-002: ROM src/act_info.c:2896 — TWO spaces after "password.".
|
|
62
|
+
return "Wrong password. Wait 10 seconds."
|
|
61
63
|
|
|
62
64
|
# Gap 1 (P1): Check minimum length (ROM src/act_info.c:2897-2904)
|
|
63
65
|
if len(new_password) < 5:
|
mud/commands/combat.py
CHANGED
|
@@ -11,6 +11,7 @@ from mud.models.character import Character
|
|
|
11
11
|
from mud.models.constants import (
|
|
12
12
|
AC_BASH,
|
|
13
13
|
EX_CLOSED,
|
|
14
|
+
LEVEL_HERO,
|
|
14
15
|
LEVEL_IMMORTAL,
|
|
15
16
|
ActFlag,
|
|
16
17
|
AffectFlag,
|
|
@@ -45,6 +46,9 @@ _FLEE_MOVEMENT_LOSS: dict[Sector, int] = {
|
|
|
45
46
|
Sector.DESERT: 6,
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
# mirroring ROM src/act_move.c dir_name[] — indexed by door number (0..5).
|
|
50
|
+
_FLEE_DIR_NAMES: tuple[str, ...] = ("north", "east", "south", "west", "up", "down")
|
|
51
|
+
|
|
48
52
|
|
|
49
53
|
def _kill_safety_message(attacker: Character, victim: Character) -> str | None:
|
|
50
54
|
# FIGHT-074: pure mirror of ROM's is_safe() (src/fight.c:1059) — which contains
|
|
@@ -171,10 +175,10 @@ def do_kill(char: Character, args: str) -> str:
|
|
|
171
175
|
|
|
172
176
|
|
|
173
177
|
def do_kick(char: Character, args: str) -> str:
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
# KICK-001: ROM do_kick (src/fight.c:3109-3124) checks the PC level gate and
|
|
179
|
+
# NPC OFF_KICK gate BEFORE the `fighting == NULL` check, so a sub-level PC who
|
|
180
|
+
# is not fighting sees "You better leave the martial arts to fighters.", not
|
|
181
|
+
# "You aren't fighting anyone." The `fighting is None` check is relocated below.
|
|
178
182
|
try:
|
|
179
183
|
skill = skill_registry.get("kick")
|
|
180
184
|
except KeyError:
|
|
@@ -204,6 +208,12 @@ def do_kick(char: Character, args: str) -> str:
|
|
|
204
208
|
if required_level is not None and getattr(char, "level", 0) < required_level:
|
|
205
209
|
return "You better leave the martial arts to fighters."
|
|
206
210
|
|
|
211
|
+
# KICK-001: ROM src/fight.c:3121-3124 — `fighting == NULL` check comes AFTER
|
|
212
|
+
# the level/OFF_KICK gates.
|
|
213
|
+
opponent = getattr(char, "fighting", None)
|
|
214
|
+
if opponent is None:
|
|
215
|
+
return "You aren't fighting anyone."
|
|
216
|
+
|
|
207
217
|
if skill is not None:
|
|
208
218
|
if int(getattr(char, "wait", 0) or 0) > 0:
|
|
209
219
|
# INV-001 SINGLE-DELIVERY: return-channel only; a mailbox append would
|
|
@@ -815,8 +825,20 @@ def do_flee(char: Character, args: str) -> str:
|
|
|
815
825
|
continue # too exhausted — mirrors move_char returning without moving
|
|
816
826
|
flee_move_cost = cost
|
|
817
827
|
|
|
828
|
+
# MOVE-009: ROM src/fight.c:3002 flees via move_char(ch, door, FALSE),
|
|
829
|
+
# which broadcasts "$n leaves $T." to the fled-from room and "$n has
|
|
830
|
+
# arrived." to the destination (src/act_move.c:196-202) — suppressed for
|
|
831
|
+
# a sneaking or wizinvis fleer. The inline flee move omitted both.
|
|
832
|
+
show_movement = not (
|
|
833
|
+
char.has_affect(AffectFlag.SNEAK) or int(getattr(char, "invis_level", 0) or 0) >= LEVEL_HERO
|
|
834
|
+
)
|
|
835
|
+
dir_name = _FLEE_DIR_NAMES[door] if door < len(_FLEE_DIR_NAMES) else ""
|
|
836
|
+
if show_movement:
|
|
837
|
+
act_to_room(was_in, "$n leaves $T.", char, arg2=dir_name, exclude=char)
|
|
818
838
|
was_in.remove_character(char)
|
|
819
839
|
new_room.add_character(char)
|
|
840
|
+
if show_movement:
|
|
841
|
+
act_to_room(new_room, "$n has arrived.", char, exclude=char)
|
|
820
842
|
now_in = new_room
|
|
821
843
|
break
|
|
822
844
|
|
|
@@ -1202,7 +1224,8 @@ def do_trip(char: Character, args: str, *, victim: Character | None = None) -> s
|
|
|
1202
1224
|
else:
|
|
1203
1225
|
skill_level = _character_skill_percent(char, "trip")
|
|
1204
1226
|
if skill_level == 0:
|
|
1205
|
-
|
|
1227
|
+
# TRIP-001: ROM src/fight.c:2654 — TWO spaces after "Tripping?".
|
|
1228
|
+
return "Tripping? What's that?"
|
|
1206
1229
|
|
|
1207
1230
|
# Find target (skip when an explicit victim is supplied via delegation).
|
|
1208
1231
|
if victim is not None:
|
mud/commands/compare.py
CHANGED
|
@@ -38,7 +38,10 @@ def do_compare(char: Character, args: str) -> str:
|
|
|
38
38
|
# Compare two items
|
|
39
39
|
obj2 = get_obj_carry(char, parts[1])
|
|
40
40
|
if obj2 is None:
|
|
41
|
-
|
|
41
|
+
# COMPARE-002: ROM src/act_info.c:2338-2341 emits the SAME string as
|
|
42
|
+
# the missing-first-item branch (:2317) — not a distinct "second item"
|
|
43
|
+
# variant. The faithful port must reuse "You do not have that item.".
|
|
44
|
+
return "You do not have that item."
|
|
42
45
|
else:
|
|
43
46
|
# Compare against equipped item of same type
|
|
44
47
|
obj2 = _find_equipped_match(char, obj1)
|
mud/commands/doors.py
CHANGED
|
@@ -378,11 +378,14 @@ def do_lock(char: Character, args: str) -> str:
|
|
|
378
378
|
|
|
379
379
|
# Container (ROM C lines 627-656)
|
|
380
380
|
if item_type == ItemType.CONTAINER:
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
# LOCK-001: ROM do_lock's container arm has NO CONT_CLOSEABLE check
|
|
382
|
+
# (only do_open/do_close do — MOVE-008). Its first guard is CONT_CLOSED.
|
|
383
383
|
if not (values[1] & ContainerFlag.CLOSED):
|
|
384
384
|
return "It's not closed."
|
|
385
|
-
|
|
385
|
+
# LOCK-002: ROM src/act_move.c:637 gates on `value[2] < 0`. Key vnum 0
|
|
386
|
+
# (a keyless container) falls through to has_key → "You lack the key.",
|
|
387
|
+
# matching the portal sibling arm above (which already uses `< 0`).
|
|
388
|
+
if values[2] < 0:
|
|
386
389
|
return "It can't be locked."
|
|
387
390
|
if not _has_key(char, values[2]):
|
|
388
391
|
return "You lack the key."
|
|
@@ -483,11 +486,12 @@ def do_unlock(char: Character, args: str) -> str:
|
|
|
483
486
|
|
|
484
487
|
# Container (ROM C lines 761-791)
|
|
485
488
|
if item_type == ItemType.CONTAINER:
|
|
486
|
-
|
|
487
|
-
|
|
489
|
+
# LOCK-001: ROM do_unlock's container arm has NO CONT_CLOSEABLE check
|
|
490
|
+
# (only do_open/do_close do — MOVE-008). Its first guard is CONT_CLOSED.
|
|
488
491
|
if not (values[1] & ContainerFlag.CLOSED):
|
|
489
492
|
return "It's not closed."
|
|
490
|
-
|
|
493
|
+
# LOCK-002: ROM src/act_move.c:773 gates on `value[2] < 0` (see do_lock).
|
|
494
|
+
if values[2] < 0:
|
|
491
495
|
return "It can't be unlocked."
|
|
492
496
|
if not _has_key(char, values[2]):
|
|
493
497
|
return "You lack the key."
|
mud/commands/equipment.py
CHANGED
|
@@ -18,6 +18,25 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from mud.models.object import Object
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
# WEAR-015: wear-location flags checked BEFORE the HOLD branch in ROM `wear_obj`
|
|
22
|
+
# (every bit below HOLD's 1<<14 except WIELD, which Python dispatches by
|
|
23
|
+
# item_type). An object carrying any of these takes the armor/shield slot, not HOLD.
|
|
24
|
+
_PRE_HOLD_WEAR_MASK = (
|
|
25
|
+
WearFlag.WEAR_FINGER
|
|
26
|
+
| WearFlag.WEAR_NECK
|
|
27
|
+
| WearFlag.WEAR_BODY
|
|
28
|
+
| WearFlag.WEAR_HEAD
|
|
29
|
+
| WearFlag.WEAR_LEGS
|
|
30
|
+
| WearFlag.WEAR_FEET
|
|
31
|
+
| WearFlag.WEAR_HANDS
|
|
32
|
+
| WearFlag.WEAR_ARMS
|
|
33
|
+
| WearFlag.WEAR_SHIELD
|
|
34
|
+
| WearFlag.WEAR_ABOUT
|
|
35
|
+
| WearFlag.WEAR_WAIST
|
|
36
|
+
| WearFlag.WEAR_WRIST
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
21
40
|
# ROM str_app wield column for STR 0..25.
|
|
22
41
|
# Source: src/const.c:728 str_app[26], fourth field (wield).
|
|
23
42
|
# Max wieldable weight = _STR_WIELD[STR] * 10 (ROM src/act_obj.c:1624-1625).
|
|
@@ -141,6 +160,35 @@ def _can_wear_alignment(ch: Character, obj: Object) -> tuple[bool, str | None]:
|
|
|
141
160
|
return True, None
|
|
142
161
|
|
|
143
162
|
|
|
163
|
+
def _zap_align(ch: Character, obj: Object) -> str:
|
|
164
|
+
"""ROM `equip_char` alignment zap — WEAR-014 (src/handler.c:1765-1777).
|
|
165
|
+
|
|
166
|
+
When an evil PC dons an ITEM_ANTI_EVIL object (or good+ANTI_GOOD /
|
|
167
|
+
neutral+ANTI_NEUTRAL), ROM does NOT keep the item in inventory. `equip_char`
|
|
168
|
+
emits a TO_CHAR + TO_ROOM message using `$p` (the object's short_descr) and
|
|
169
|
+
drops the item to the floor (`obj_from_char` + `obj_to_room`). The prior
|
|
170
|
+
Python returned a generic "the item" message, emitted no room line, and left
|
|
171
|
+
the item CARRIED — a state divergence a player could observe (the item stays
|
|
172
|
+
in inventory instead of lying on the ground).
|
|
173
|
+
|
|
174
|
+
Residual: ROM prints the "You wear $p ..." line *before* the zap (the zap
|
|
175
|
+
lives in equip_char, called after the wear message). Python checks alignment
|
|
176
|
+
up-front, so it emits only the zap line — a minor cosmetic ordering residual;
|
|
177
|
+
the observable state (item on the floor) now matches ROM.
|
|
178
|
+
"""
|
|
179
|
+
from mud.commands.obj_manipulation import _obj_from_char
|
|
180
|
+
|
|
181
|
+
obj_name = getattr(obj, "short_descr", None) or getattr(obj, "name", None) or "it"
|
|
182
|
+
room = getattr(ch, "room", None)
|
|
183
|
+
_obj_from_char(ch, obj) # ROM obj_from_char (src/handler.c:1771)
|
|
184
|
+
if room is not None:
|
|
185
|
+
room.add_object(obj) # ROM obj_to_room (src/handler.c:1772)
|
|
186
|
+
# ROM src/handler.c:1770 — act("$n is zapped by $p and drops it.", TO_ROOM)
|
|
187
|
+
act_to_room(room, "$n is zapped by $p and drops it.", ch, arg1=obj, exclude=ch)
|
|
188
|
+
# ROM src/handler.c:1769 — act("You are zapped by $p and drop it.", TO_CHAR)
|
|
189
|
+
return f"You are zapped by {obj_name} and drop it."
|
|
190
|
+
|
|
191
|
+
|
|
144
192
|
def do_wear(ch: Character, args: str) -> str:
|
|
145
193
|
"""
|
|
146
194
|
Wear equipment (armor, clothing, jewelry).
|
|
@@ -232,9 +280,9 @@ def _wear_obj(ch: Character, obj: Object, fReplace: bool = True) -> str:
|
|
|
232
280
|
return f"You can't remove {existing_name}."
|
|
233
281
|
|
|
234
282
|
# Alignment zap (mirrors the HOLD/wear paths' _can_wear_alignment block).
|
|
235
|
-
can_wear,
|
|
283
|
+
can_wear, _ = _can_wear_alignment(ch, obj)
|
|
236
284
|
if not can_wear:
|
|
237
|
-
return
|
|
285
|
+
return _zap_align(ch, obj) # WEAR-014: drop to room per ROM equip_char
|
|
238
286
|
|
|
239
287
|
if not equipment:
|
|
240
288
|
ch.equipment = {}
|
|
@@ -258,8 +306,14 @@ def _wear_obj(ch: Character, obj: Object, fReplace: bool = True) -> str:
|
|
|
258
306
|
# then performs the two-hand-weapon check. Implemented below after the
|
|
259
307
|
# generic slot-remove (search "WEAR-009 SHIELD post-check").
|
|
260
308
|
|
|
261
|
-
# Check if item has HOLD flag - if so, use hold logic (ROM lines 1670-1677)
|
|
262
|
-
|
|
309
|
+
# Check if item has HOLD flag - if so, use hold logic (ROM lines 1670-1677).
|
|
310
|
+
# WEAR-015: ROM `wear_obj` dispatches wear flags in bit order, so every
|
|
311
|
+
# armor/shield slot (WEAR_FINGER..WEAR_WRIST, WEAR_SHIELD — all bits below
|
|
312
|
+
# HOLD's 1<<14) is checked BEFORE the HOLD branch. Only fall to HOLD when the
|
|
313
|
+
# object has NO earlier wear-location flag; otherwise let `_get_wear_location`
|
|
314
|
+
# place it in the armor slot (matching ROM's precedence). WIELD (1<<13) is
|
|
315
|
+
# item_type-dispatched separately above, so it's excluded from this mask.
|
|
316
|
+
if (wear_flags & WearFlag.HOLD) and not (wear_flags & _PRE_HOLD_WEAR_MASK):
|
|
263
317
|
# Check if hold slot is occupied
|
|
264
318
|
equipment = getattr(ch, "equipment", {})
|
|
265
319
|
hold_loc = int(WearLocation.HOLD)
|
|
@@ -274,9 +328,9 @@ def _wear_obj(ch: Character, obj: Object, fReplace: bool = True) -> str:
|
|
|
274
328
|
return f"You can't remove {existing_name}."
|
|
275
329
|
|
|
276
330
|
# Check alignment restrictions
|
|
277
|
-
can_hold,
|
|
331
|
+
can_hold, _ = _can_wear_alignment(ch, obj)
|
|
278
332
|
if not can_hold:
|
|
279
|
-
return
|
|
333
|
+
return _zap_align(ch, obj) # WEAR-014: drop to room per ROM equip_char
|
|
280
334
|
|
|
281
335
|
# Hold the item
|
|
282
336
|
if not equipment:
|
|
@@ -348,11 +402,11 @@ def _wear_obj(ch: Character, obj: Object, fReplace: bool = True) -> str:
|
|
|
348
402
|
return "Your hands are tied up with your weapon!"
|
|
349
403
|
|
|
350
404
|
# Check alignment restrictions (ROM src/handler.c:1765-1777)
|
|
351
|
-
can_wear,
|
|
405
|
+
can_wear, _ = _can_wear_alignment(ch, obj)
|
|
352
406
|
if not can_wear:
|
|
353
|
-
#
|
|
354
|
-
#
|
|
355
|
-
return
|
|
407
|
+
# WEAR-014: ROM's zap (equip_char) drops the item to the room and emits
|
|
408
|
+
# a $p-substituted TO_CHAR + TO_ROOM message — not a generic in-inventory error.
|
|
409
|
+
return _zap_align(ch, obj)
|
|
356
410
|
|
|
357
411
|
# Wear the item
|
|
358
412
|
if not equipment:
|
|
@@ -423,9 +477,9 @@ def _dispatch_wield(ch: Character, obj: Object, fReplace: bool = True) -> str:
|
|
|
423
477
|
if weight > _str_wield_max(str_stat):
|
|
424
478
|
return "It is too heavy for you to wield."
|
|
425
479
|
|
|
426
|
-
can_wield,
|
|
480
|
+
can_wield, _ = _can_wear_alignment(ch, obj)
|
|
427
481
|
if not can_wield:
|
|
428
|
-
return
|
|
482
|
+
return _zap_align(ch, obj) # WEAR-014: drop to room per ROM equip_char
|
|
429
483
|
|
|
430
484
|
# ROM src/act_obj.c:1631-1636 — two-hand vs shield check skipped for NPCs
|
|
431
485
|
# and for characters of SIZE_LARGE or greater.
|
|
@@ -435,7 +489,9 @@ def _dispatch_wield(ch: Character, obj: Object, fReplace: bool = True) -> str:
|
|
|
435
489
|
if weapon_flags[4] & WeaponFlag.TWO_HANDS:
|
|
436
490
|
shield_loc = int(WearLocation.SHIELD)
|
|
437
491
|
if shield_loc in equipment and equipment[shield_loc] is not None:
|
|
438
|
-
|
|
492
|
+
# WEAR-013: ROM src/act_obj.c:1635 ends this with a PERIOD, not a
|
|
493
|
+
# "!" (unlike the shield-branch "...weapon!" at :1606).
|
|
494
|
+
return "You need two hands free for that weapon."
|
|
439
495
|
|
|
440
496
|
if not equipment:
|
|
441
497
|
ch.equipment = {}
|
mud/commands/healer.py
CHANGED
|
@@ -240,7 +240,12 @@ def do_heal(char: Character, args: str = "") -> str:
|
|
|
240
240
|
|
|
241
241
|
arg = (args or "").strip().lower()
|
|
242
242
|
if not arg:
|
|
243
|
-
|
|
243
|
+
# HEALER-007: ROM src/healer.c:67 — act("$N says 'I offer the following
|
|
244
|
+
# spells:'", ...); act_new (src/comm.c:2379) capitalizes buf[0], so a
|
|
245
|
+
# lowercase-initial short_descr renders "A healer says ...". Match the
|
|
246
|
+
# sibling "not enough gold" branch (capitalize_act_line, INV-029/ACT-CAP).
|
|
247
|
+
header = capitalize_act_line(f"{_healer_name(healer)} says 'I offer the following spells:'")
|
|
248
|
+
lines = [header]
|
|
244
249
|
lines.extend(service.display_line for service in _SERVICES)
|
|
245
250
|
lines.append(" Type heal <type> to be healed.")
|
|
246
251
|
return "\n".join(lines)
|
mud/commands/info.py
CHANGED
|
@@ -353,8 +353,13 @@ def do_where(char: Character, args: str) -> str:
|
|
|
353
353
|
|
|
354
354
|
found = False
|
|
355
355
|
|
|
356
|
-
# ROM C iterates char_list
|
|
357
|
-
|
|
356
|
+
# ROM C iterates char_list from the head and returns the FIRST match
|
|
357
|
+
# (src/act_info.c:2445, with `break`). ROM head-inserts new chars into
|
|
358
|
+
# char_list (`create_mobile`, src/db.c:2256-2257), so a head-first walk is
|
|
359
|
+
# newest-first. Python `character_registry` is append-ordered (oldest
|
|
360
|
+
# first), so we iterate it reversed to reproduce ROM's newest-first match
|
|
361
|
+
# when several same-named mobs share an area (FINDING-044).
|
|
362
|
+
for victim in reversed(character_registry):
|
|
358
363
|
victim_room = getattr(victim, "room", None)
|
|
359
364
|
if not victim_room:
|
|
360
365
|
continue
|
mud/commands/inspection.py
CHANGED
|
@@ -4,7 +4,7 @@ from mud.models.character import Character
|
|
|
4
4
|
from mud.models.constants import Direction
|
|
5
5
|
from mud.utils.act import act_to_room
|
|
6
6
|
from mud.world.look import dir_names, look
|
|
7
|
-
from mud.world.vision import can_see_character,
|
|
7
|
+
from mud.world.vision import can_see_character, pers
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def do_scan(char: Character, args: str = "") -> str:
|
|
@@ -61,7 +61,11 @@ def do_scan(char: Character, args: str = "") -> str:
|
|
|
61
61
|
continue
|
|
62
62
|
if not can_see_character(char, p):
|
|
63
63
|
continue
|
|
64
|
-
|
|
64
|
+
# ROM scan_char (src/scan.c:133) uses PERS(victim, ch) — the bare
|
|
65
|
+
# short_descr/name, NOT show_char_to_char aura tags. describe_character
|
|
66
|
+
# injects (Pink/White Aura) prefixes, which ROM's scan never shows
|
|
67
|
+
# (FINDING-042).
|
|
68
|
+
who = pers(p, char)
|
|
65
69
|
if depth == 0:
|
|
66
70
|
lines.append(f"{who}, {distance[0]}")
|
|
67
71
|
else:
|
mud/commands/inventory.py
CHANGED
|
@@ -810,6 +810,18 @@ def _show_inventory_list(objects: list[Object], char: Character, show_nothing: b
|
|
|
810
810
|
else:
|
|
811
811
|
return "Nothing.\n"
|
|
812
812
|
|
|
813
|
+
# INVEN-001: ROM show_list_to_char formats each object via
|
|
814
|
+
# format_obj_to_char(obj, ch, fShort) (src/act_info.c:166), which prepends
|
|
815
|
+
# the object status tags ((Invis)/(Red Aura)/(Blue Aura)/(Magical)/(Glowing)/
|
|
816
|
+
# (Humming)) before the short_descr. That prefixed string is ALSO the
|
|
817
|
+
# combine/dedup key (strcmp at :180), so a glowing item and a plain identical
|
|
818
|
+
# item render as two separate lines instead of coalescing. Bare short_descr
|
|
819
|
+
# dropped the prefixes AND keyed the dedup on the wrong (prefix-blind) string.
|
|
820
|
+
from mud.utils.act import format_obj_to_char
|
|
821
|
+
|
|
822
|
+
def _display_name(obj: Object) -> str:
|
|
823
|
+
return format_obj_to_char(obj, char, f_short=True) or obj.short_descr or obj.name or "something"
|
|
824
|
+
|
|
813
825
|
# Format objects (ROM C lines 162-225)
|
|
814
826
|
if combine_enabled:
|
|
815
827
|
# Combine duplicate objects (ROM C lines 170-195)
|
|
@@ -817,8 +829,8 @@ def _show_inventory_list(objects: list[Object], char: Character, show_nothing: b
|
|
|
817
829
|
object_order: list[str] = []
|
|
818
830
|
|
|
819
831
|
for obj in visible_objects:
|
|
820
|
-
# Get object description (
|
|
821
|
-
obj_desc = obj
|
|
832
|
+
# Get object description (prefix-inclusive, per ROM dedup key)
|
|
833
|
+
obj_desc = _display_name(obj)
|
|
822
834
|
|
|
823
835
|
# ROM C lines 176-184: Look for duplicates (case sensitive)
|
|
824
836
|
if obj_desc in object_counts:
|
|
@@ -844,8 +856,7 @@ def _show_inventory_list(objects: list[Object], char: Character, show_nothing: b
|
|
|
844
856
|
# No combining: show each object on separate line (ROM C lines 222-223)
|
|
845
857
|
lines = []
|
|
846
858
|
for obj in visible_objects:
|
|
847
|
-
|
|
848
|
-
lines.append(obj_desc)
|
|
859
|
+
lines.append(_display_name(obj))
|
|
849
860
|
|
|
850
861
|
return "\n".join(lines) + "\n"
|
|
851
862
|
|
|
@@ -912,8 +923,14 @@ def do_equipment(char: Character, args: str = "") -> str:
|
|
|
912
923
|
|
|
913
924
|
# ROM C line 2277: if (can_see_obj (ch, obj))
|
|
914
925
|
if can_see_object(char, obj):
|
|
915
|
-
# ROM C line 2279
|
|
916
|
-
|
|
926
|
+
# EQUIP-002: ROM C line 2279 renders worn items via
|
|
927
|
+
# format_obj_to_char(obj, ch, TRUE), which prepends the object
|
|
928
|
+
# status tags ((Invis)/(Red Aura)/(Blue Aura)/(Magical)/(Glowing)/
|
|
929
|
+
# (Humming)) before the short_descr. The bare short_descr dropped
|
|
930
|
+
# every status prefix from `equipment` output.
|
|
931
|
+
from mud.utils.act import format_obj_to_char
|
|
932
|
+
|
|
933
|
+
obj_name = format_obj_to_char(obj, char, f_short=True) or obj.short_descr or obj.name or "object"
|
|
917
934
|
else:
|
|
918
935
|
# ROM C line 2283: send_to_char ("something.\n\r", ch);
|
|
919
936
|
obj_name = "something."
|
mud/commands/obj_manipulation.py
CHANGED
|
@@ -265,9 +265,9 @@ def do_put(char: Character, args: str) -> str:
|
|
|
265
265
|
messages.append(f"You put {obj_short} in {container_short}.")
|
|
266
266
|
count += 1
|
|
267
267
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
268
|
+
# PUT-005: ROM do_put's put-all branch (src/act_obj.c:451-491) is a bare
|
|
269
|
+
# loop with no `found` flag and no trailing message — nothing eligible ⇒
|
|
270
|
+
# ROM prints nothing. The prior "You have nothing to put." was non-ROM.
|
|
271
271
|
return "\n".join(messages)
|
|
272
272
|
|
|
273
273
|
|
mud/commands/session.py
CHANGED
|
@@ -149,15 +149,18 @@ def do_score(ch: Character, args: str) -> str:
|
|
|
149
149
|
lines.append(f"You have {practice} practices and {train} training sessions.")
|
|
150
150
|
|
|
151
151
|
# Carrying - ROM src/act_info.c:1514-1518 (immediately after practices)
|
|
152
|
-
carry_weight = getattr(ch, "carry_weight", 0)
|
|
153
152
|
carry_number = getattr(ch, "carry_number", 0)
|
|
154
|
-
from mud.world.movement import can_carry_n, can_carry_w
|
|
153
|
+
from mud.world.movement import can_carry_n, can_carry_w, get_carry_weight
|
|
155
154
|
|
|
155
|
+
# SCORE-002: ROM src/act_info.c:1517 prints `get_carry_weight (ch) / 10`,
|
|
156
|
+
# which adds coin weight (`silver/10 + gold*2/5`, src/merc.h:2118) to the
|
|
157
|
+
# raw item weight. Using bare `ch.carry_weight` dropped coin burden from the
|
|
158
|
+
# score sheet. get_carry_weight is non-negative, so `// 10` == C truncation.
|
|
156
159
|
max_carry_number = can_carry_n(ch)
|
|
157
160
|
max_carry_weight = can_carry_w(ch) // 10 # ROM divides by 10 for display
|
|
158
161
|
lines.append(
|
|
159
162
|
f"You are carrying {carry_number}/{max_carry_number} items "
|
|
160
|
-
f"with weight {
|
|
163
|
+
f"with weight {get_carry_weight(ch) // 10}/{max_carry_weight} pounds."
|
|
161
164
|
)
|
|
162
165
|
|
|
163
166
|
# Stats - ROM src/act_info.c:1520-1531 (perm(current) per stat).
|
|
@@ -339,15 +342,21 @@ def do_recall(ch: Character, args: str) -> str:
|
|
|
339
342
|
ROM Reference: src/act_move.c lines 1563-1628 (do_recall)
|
|
340
343
|
"""
|
|
341
344
|
from mud.combat.engine import stop_fighting
|
|
342
|
-
|
|
345
|
+
|
|
346
|
+
# RECALL-003: NPCs can't recall unless they carry ACT_PET (ROM
|
|
347
|
+
# src/act_move.c:1569-1573 — `IS_NPC(ch) && !IS_SET(ch->act, ACT_PET)`), and
|
|
348
|
+
# ROM `send_to_char("Only players can recall.")` then returns — it does NOT
|
|
349
|
+
# return silently, and it gates on the ACT_PET *flag*, not on `master` (a
|
|
350
|
+
# charmed non-pet mob is still blocked). The prior code returned "" and keyed
|
|
351
|
+
# on `master`, so a charmed non-pet mob would wrongly recall and no message
|
|
352
|
+
# was shown when an NPC path (e.g. a switched immortal) hit this.
|
|
353
|
+
from mud.models.constants import ROOM_VNUM_TEMPLE, ActFlag, AffectFlag, RoomFlag
|
|
343
354
|
from mud.registry import room_registry
|
|
344
355
|
from mud.utils.rng_mm import number_percent
|
|
345
356
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if ch.is_npc and getattr(ch, "master", None) is None:
|
|
350
|
-
return "" # ROM returns silently (src/act_move.c:1569-1573)
|
|
357
|
+
act_flags = int(getattr(ch, "act", 0) or 0)
|
|
358
|
+
if ch.is_npc and not (act_flags & int(ActFlag.PET)):
|
|
359
|
+
return "Only players can recall."
|
|
351
360
|
|
|
352
361
|
# Message to room FIRST (ROM C line 1575)
|
|
353
362
|
if ch.room:
|
mud/commands/socials.py
CHANGED
|
@@ -57,8 +57,11 @@ def perform_social(char: Character, name: str, arg: str) -> str:
|
|
|
57
57
|
if position == Position.STUNNED:
|
|
58
58
|
return "You are too stunned to do that."
|
|
59
59
|
# mirroring ROM src/interp.c:618-626 — POS_SLEEPING blocks every social
|
|
60
|
-
# except "snore" (the canonical Furey exception).
|
|
61
|
-
|
|
60
|
+
# except "snore" (the canonical Furey exception). SOCIAL-001: ROM compares
|
|
61
|
+
# the RESOLVED social's name (`social_table[cmd].name`, interp.c:623), NOT
|
|
62
|
+
# the typed argument, so a prefix like "snor" that resolves to snore is also
|
|
63
|
+
# allowed while asleep. `social` was already resolved via find_social above.
|
|
64
|
+
if position == Position.SLEEPING and social.name.lower() != "snore":
|
|
62
65
|
return "In your dreams, or what?"
|
|
63
66
|
victim = None
|
|
64
67
|
if arg:
|
mud/skills/handlers.py
CHANGED
|
@@ -1328,53 +1328,53 @@ def bash(
|
|
|
1328
1328
|
raise ValueError("bash requires both caster and target")
|
|
1329
1329
|
|
|
1330
1330
|
bash_type = int(DamageType.BASH)
|
|
1331
|
-
caster_name = _character_name(caster)
|
|
1332
|
-
target_name = _character_name(target)
|
|
1333
1331
|
room = getattr(caster, "room", None)
|
|
1334
1332
|
|
|
1333
|
+
# BASH-001: ROM do_bash (src/fight.c:2460-2482) calls damage(..., FALSE) on
|
|
1334
|
+
# BOTH branches — show=FALSE suppresses the dam_message, so the three {5..{x
|
|
1335
|
+
# flavor act() lines are the only output. The attacker's TO_CHAR flavor line
|
|
1336
|
+
# is the command return; TO_VICT/TO_NOTVICT are pushed. apply_damage(show=False)
|
|
1337
|
+
# pushes nothing, so returning the flavor line is single-delivery (no double).
|
|
1335
1338
|
if not success:
|
|
1336
|
-
#
|
|
1339
|
+
# ROM src/fight.c:2477-2481 — damage() first, then TO_CHAR/TO_NOTVICT/TO_VICT.
|
|
1340
|
+
apply_damage(caster, target, 0, bash_type, dt="bash", show=False)
|
|
1337
1341
|
if room is not None:
|
|
1338
|
-
|
|
1339
|
-
caster_him = _objective_pronoun(caster)
|
|
1340
|
-
_notvict_broadcast(
|
|
1341
|
-
room,
|
|
1342
|
-
caster,
|
|
1343
|
-
target,
|
|
1344
|
-
f"{caster_name} falls flat on {caster_his} face.",
|
|
1345
|
-
)
|
|
1342
|
+
act_to_room(room, "{5$n falls flat on $s face.{x", caster, exclude=caster)
|
|
1346
1343
|
_to_vict_send(
|
|
1347
1344
|
target,
|
|
1348
|
-
|
|
1345
|
+
act_format(
|
|
1346
|
+
"{5You evade $n's bash, causing $m to fall flat on $s face.{x",
|
|
1347
|
+
recipient=target,
|
|
1348
|
+
actor=caster,
|
|
1349
|
+
arg2=target,
|
|
1350
|
+
),
|
|
1349
1351
|
)
|
|
1350
|
-
return
|
|
1352
|
+
return act_format("{5You fall flat on your face!{x", recipient=caster, actor=caster, arg2=target)
|
|
1351
1353
|
|
|
1352
1354
|
chance = int(chance or 0)
|
|
1353
1355
|
size = max(0, int(getattr(caster, "size", 0) or 0))
|
|
1354
1356
|
upper = 2 + 2 * size + c_div(chance, 20)
|
|
1355
1357
|
damage = rng_mm.number_range(2, max(2, upper))
|
|
1356
1358
|
|
|
1357
|
-
#
|
|
1359
|
+
# ROM src/fight.c:2460-2465 — success broadcasts ({5..{x, PERS-rendered).
|
|
1358
1360
|
if room is not None:
|
|
1359
1361
|
_to_vict_send(
|
|
1360
1362
|
target,
|
|
1361
|
-
|
|
1362
|
-
)
|
|
1363
|
-
_notvict_broadcast(
|
|
1364
|
-
room,
|
|
1365
|
-
caster,
|
|
1366
|
-
target,
|
|
1367
|
-
f"{caster_name} sends {target_name} sprawling with a powerful bash.",
|
|
1363
|
+
act_format("{5$n sends you sprawling with a powerful bash!{x", recipient=target, actor=caster, arg2=target),
|
|
1368
1364
|
)
|
|
1365
|
+
act_to_room(room, "{5$n sends $N sprawling with a powerful bash.{x", caster, arg2=target, exclude=target)
|
|
1369
1366
|
|
|
1370
|
-
# DAZE_STATE
|
|
1367
|
+
# DAZE_STATE(victim, 3 * PULSE_VIOLENCE); ROM :2470-2472 position=RESTING then
|
|
1368
|
+
# damage(show=FALSE) (dam_message suppressed).
|
|
1371
1369
|
from mud.config import get_pulse_violence
|
|
1372
1370
|
|
|
1373
1371
|
victim_daze = 3 * get_pulse_violence()
|
|
1374
1372
|
target.daze = max(int(getattr(target, "daze", 0) or 0), victim_daze)
|
|
1375
|
-
|
|
1373
|
+
apply_damage(caster, target, damage, bash_type, dt="bash", show=False)
|
|
1374
|
+
# ROM sets POS_RESTING before damage() (:2470); in Python apply_damage's
|
|
1375
|
+
# set_fighting resets position, so RESTING is applied AFTER to make it stick.
|
|
1376
1376
|
target.position = Position.RESTING
|
|
1377
|
-
return
|
|
1377
|
+
return act_format("{5You slam into $N, and send $M flying!{x", recipient=caster, actor=caster, arg2=target)
|
|
1378
1378
|
|
|
1379
1379
|
|
|
1380
1380
|
def berserk(
|
|
@@ -3559,16 +3559,114 @@ def dispel_magic(caster: Character, target: Character | None = None) -> bool:
|
|
|
3559
3559
|
_send_to_char(caster, "You failed.")
|
|
3560
3560
|
return False
|
|
3561
3561
|
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3562
|
+
# MAGIC-050: ROM spell_dispel_magic (src/magic.c:2089-2247) walks a FIXED
|
|
3563
|
+
# hardcoded spell list in order — not the spell_effects dict — calling
|
|
3564
|
+
# check_dispel per spell, with per-effect act(TO_ROOM) wear-off lines for
|
|
3565
|
+
# certain spells, an AFF_SANCTUARY-bit fallback, and a final Ok./Spell failed.
|
|
3566
|
+
# to the caster. The dict-iteration port lost the order, the room messages,
|
|
3567
|
+
# the sanctuary-bit strip, and the result message. This mirrors the sibling
|
|
3568
|
+
# `cancellation` walk (same list) minus its NPC gate and level+2.
|
|
3569
|
+
found = False
|
|
3570
|
+
room = getattr(target, "room", None)
|
|
3571
|
+
|
|
3572
|
+
def _dispel(effect_name: str) -> bool:
|
|
3573
|
+
return check_dispel(level, target, effect_name)
|
|
3574
|
+
|
|
3575
|
+
if _dispel("armor"):
|
|
3576
|
+
found = True
|
|
3577
|
+
if _dispel("bless"):
|
|
3578
|
+
found = True
|
|
3579
|
+
if _dispel("blindness"):
|
|
3580
|
+
found = True
|
|
3581
|
+
act_to_room(room, "$n is no longer blinded.", target, exclude=target)
|
|
3582
|
+
if _dispel("calm"):
|
|
3583
|
+
found = True
|
|
3584
|
+
act_to_room(room, "$n no longer looks so peaceful...", target, exclude=target)
|
|
3585
|
+
if _dispel("change_sex"):
|
|
3586
|
+
found = True
|
|
3587
|
+
act_to_room(room, "$n looks more like $mself again.", target, exclude=target)
|
|
3588
|
+
if _dispel("charm_person"):
|
|
3589
|
+
found = True
|
|
3590
|
+
act_to_room(room, "$n regains $s free will.", target, exclude=target)
|
|
3591
|
+
if _dispel("chill_touch"):
|
|
3592
|
+
found = True
|
|
3593
|
+
act_to_room(room, "$n looks warmer.", target, exclude=target)
|
|
3594
|
+
if _dispel("curse"):
|
|
3595
|
+
found = True
|
|
3596
|
+
if _dispel("detect_evil"):
|
|
3597
|
+
found = True
|
|
3598
|
+
if _dispel("detect_good"):
|
|
3599
|
+
found = True
|
|
3600
|
+
if _dispel("detect_hidden"):
|
|
3601
|
+
found = True
|
|
3602
|
+
if _dispel("detect_invis"):
|
|
3603
|
+
found = True
|
|
3604
|
+
if _dispel("detect_magic"):
|
|
3605
|
+
found = True
|
|
3606
|
+
if _dispel("faerie_fire"):
|
|
3607
|
+
act_to_room(room, "$n's outline fades.", target, exclude=target)
|
|
3608
|
+
found = True
|
|
3609
|
+
if _dispel("fly"):
|
|
3610
|
+
act_to_room(room, "$n falls to the ground!", target, exclude=target)
|
|
3611
|
+
found = True
|
|
3612
|
+
if _dispel("frenzy"):
|
|
3613
|
+
act_to_room(room, "$n no longer looks so wild.", target, exclude=target)
|
|
3614
|
+
found = True
|
|
3615
|
+
if _dispel("giant_strength"):
|
|
3616
|
+
act_to_room(room, "$n no longer looks so mighty.", target, exclude=target)
|
|
3617
|
+
found = True
|
|
3618
|
+
if _dispel("haste"):
|
|
3619
|
+
act_to_room(room, "$n is no longer moving so quickly.", target, exclude=target)
|
|
3620
|
+
found = True
|
|
3621
|
+
if _dispel("infravision"):
|
|
3622
|
+
found = True
|
|
3623
|
+
if _dispel("invis"):
|
|
3624
|
+
act_to_room(room, "$n fades into existance.", target, exclude=target)
|
|
3625
|
+
found = True
|
|
3626
|
+
if _dispel("mass_invis"):
|
|
3627
|
+
act_to_room(room, "$n fades into existance.", target, exclude=target)
|
|
3628
|
+
found = True
|
|
3629
|
+
if _dispel("pass_door"):
|
|
3630
|
+
found = True
|
|
3631
|
+
if _dispel("protection_evil"):
|
|
3632
|
+
found = True
|
|
3633
|
+
if _dispel("protection_good"):
|
|
3634
|
+
found = True
|
|
3635
|
+
if _dispel("sanctuary"):
|
|
3636
|
+
act_to_room(room, "The white aura around $n's body vanishes.", target, exclude=target)
|
|
3637
|
+
found = True
|
|
3638
|
+
# ROM src/magic.c:2209-2217 — an AFF_SANCTUARY *bit* not backed by a
|
|
3639
|
+
# "sanctuary" spell effect (innate/item) is stripped on a failed saves_dispel.
|
|
3640
|
+
if (
|
|
3641
|
+
target.has_affect(AffectFlag.SANCTUARY)
|
|
3642
|
+
and not saves_dispel(level, int(getattr(target, "level", 0) or 0), -1)
|
|
3643
|
+
and "sanctuary" not in getattr(target, "spell_effects", {})
|
|
3644
|
+
):
|
|
3645
|
+
target.remove_affect(AffectFlag.SANCTUARY)
|
|
3646
|
+
act_to_room(room, "The white aura around $n's body vanishes.", target, exclude=target)
|
|
3647
|
+
found = True
|
|
3648
|
+
if _dispel("shield"):
|
|
3649
|
+
act_to_room(room, "The shield protecting $n vanishes.", target, exclude=target)
|
|
3650
|
+
found = True
|
|
3651
|
+
if _dispel("sleep"):
|
|
3652
|
+
found = True
|
|
3653
|
+
if _dispel("slow"):
|
|
3654
|
+
act_to_room(room, "$n is no longer moving so slowly.", target, exclude=target)
|
|
3655
|
+
found = True
|
|
3656
|
+
if _dispel("stone_skin"):
|
|
3657
|
+
act_to_room(room, "$n's skin regains its normal texture.", target, exclude=target)
|
|
3658
|
+
found = True
|
|
3659
|
+
if _dispel("weaken"):
|
|
3660
|
+
act_to_room(room, "$n looks stronger.", target, exclude=target)
|
|
3661
|
+
found = True
|
|
3565
3662
|
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3663
|
+
# ROM src/magic.c:2249-2251 — result message to the caster.
|
|
3664
|
+
if found:
|
|
3665
|
+
_send_to_char(caster, "Ok.")
|
|
3666
|
+
else:
|
|
3667
|
+
_send_to_char(caster, "Spell failed.")
|
|
3570
3668
|
|
|
3571
|
-
return
|
|
3669
|
+
return found
|
|
3572
3670
|
|
|
3573
3671
|
|
|
3574
3672
|
def earthquake(caster: Character, target=None) -> bool: # noqa: ARG001 - parity signature
|
|
@@ -5218,10 +5316,13 @@ def heat_metal(
|
|
|
5218
5316
|
room = getattr(target, "room", None)
|
|
5219
5317
|
iter_carrying = getattr(target, "iter_carrying", None)
|
|
5220
5318
|
if callable(iter_carrying):
|
|
5319
|
+
# Both Character (re-merged inventory+equipment by _carry_seq) and
|
|
5320
|
+
# MobInstance (single head-inserted inventory list — MAGIC-046) expose
|
|
5321
|
+
# iter_carrying in ROM victim->carrying LIFO order.
|
|
5221
5322
|
all_items = list(iter_carrying())
|
|
5222
5323
|
else:
|
|
5223
|
-
#
|
|
5224
|
-
#
|
|
5324
|
+
# Last-resort fallback for any other carrier type: inventory (head-inserted
|
|
5325
|
+
# LIFO) + equipment combination.
|
|
5225
5326
|
inventory = list(getattr(target, "inventory", []) or [])
|
|
5226
5327
|
equipment = dict(getattr(target, "equipment", {}) or {})
|
|
5227
5328
|
all_items = inventory + list(equipment.values())
|
mud/spawning/templates.py
CHANGED
|
@@ -500,6 +500,20 @@ class MobInstance:
|
|
|
500
500
|
self.inventory.insert(0, obj)
|
|
501
501
|
obj.carried_by = self
|
|
502
502
|
|
|
503
|
+
def iter_carrying(self) -> list[Object]:
|
|
504
|
+
"""Return this mob's items in ROM ``victim->carrying`` (LIFO) order.
|
|
505
|
+
|
|
506
|
+
MAGIC-046: unlike ``Character`` (which splits carried ``inventory`` from
|
|
507
|
+
worn ``equipment`` and must re-merge by ``_carry_seq``), a mob keeps
|
|
508
|
+
worn and carried items in the single ``inventory`` list — equip only
|
|
509
|
+
flips ``wear_loc`` (FINDING-025), and every acquisition path
|
|
510
|
+
(``add_to_inventory``/``equip``, reset G/E) head-inserts, so the list is
|
|
511
|
+
already newest → oldest exactly like ROM's ``ch->carrying``. Mechanics
|
|
512
|
+
mirroring ROM's carrying walk (``heat_metal``) iterate this instead of
|
|
513
|
+
the generic ``inventory + equipment.values()`` fallback.
|
|
514
|
+
"""
|
|
515
|
+
return list(self.inventory)
|
|
516
|
+
|
|
503
517
|
def remove_object(self, obj: Object) -> None:
|
|
504
518
|
# ROM src/handler.c obj_from_char: unequip first if worn, then strip
|
|
505
519
|
# from carried list. Mirror that here so disarm/remove paths actually
|
mud/world/look.py
CHANGED
|
@@ -2,8 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from mud.math.c_compat import c_div
|
|
4
4
|
from mud.models.character import Character
|
|
5
|
-
from mud.models.constants import AffectFlag, Direction, Position
|
|
6
|
-
from mud.world.vision import can_see_character,
|
|
5
|
+
from mud.models.constants import LEVEL_HERO, AffectFlag, CommFlag, Direction, PlayerFlag, Position
|
|
6
|
+
from mud.world.vision import can_see_character, pers
|
|
7
7
|
|
|
8
8
|
# mirroring ROM src/act_info.c show_char_to_char_0 — buf[0] = UPPER(buf[0]) and
|
|
9
9
|
# position suffixes appended when a mob is not at its start/default position.
|
|
@@ -21,26 +21,69 @@ _POSITION_SUFFIX: dict[Position, str] = {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
def _char_tags(observer: Character, victim) -> str:
|
|
25
|
+
"""Build ROM show_char_to_char_0's status-tag prefix (src/act_info.c:253-276).
|
|
26
|
+
|
|
27
|
+
The full fixed order is [AFK] (Invis) (Wizi) (Hide) (Charmed) (Translucent)
|
|
28
|
+
(Pink Aura) (Red Aura) (Golden Aura) (White Aura) (KILLER) (THIEF). Red/Golden
|
|
29
|
+
auras depend on the *observer* carrying detect-evil/good and the victim's
|
|
30
|
+
alignment; KILLER/THIEF are PC-only PLR flags. Tags live here (the room
|
|
31
|
+
listing), not in PERS/``describe_character`` — see ``pers`` docstring.
|
|
32
|
+
"""
|
|
33
|
+
tags: list[str] = []
|
|
34
|
+
has_affect = getattr(victim, "has_affect", None)
|
|
35
|
+
|
|
36
|
+
if int(getattr(victim, "comm", 0) or 0) & int(CommFlag.AFK):
|
|
37
|
+
tags.append("[AFK]")
|
|
38
|
+
if callable(has_affect):
|
|
39
|
+
if victim.has_affect(AffectFlag.INVISIBLE):
|
|
40
|
+
tags.append("(Invis)")
|
|
41
|
+
if int(getattr(victim, "invis_level", 0) or 0) >= LEVEL_HERO:
|
|
42
|
+
tags.append("(Wizi)")
|
|
43
|
+
if callable(has_affect):
|
|
44
|
+
if victim.has_affect(AffectFlag.HIDE):
|
|
45
|
+
tags.append("(Hide)")
|
|
46
|
+
if victim.has_affect(AffectFlag.CHARM):
|
|
47
|
+
tags.append("(Charmed)")
|
|
48
|
+
if victim.has_affect(AffectFlag.PASS_DOOR):
|
|
49
|
+
tags.append("(Translucent)")
|
|
50
|
+
if victim.has_affect(AffectFlag.FAERIE_FIRE):
|
|
51
|
+
tags.append("(Pink Aura)")
|
|
52
|
+
|
|
53
|
+
victim_align = int(getattr(victim, "alignment", 0) or 0)
|
|
54
|
+
obs_has_affect = getattr(observer, "has_affect", None)
|
|
55
|
+
# ROM IS_EVIL: alignment <= -350; IS_GOOD: alignment >= 350.
|
|
56
|
+
if victim_align <= -350 and callable(obs_has_affect) and observer.has_affect(AffectFlag.DETECT_EVIL):
|
|
57
|
+
tags.append("(Red Aura)")
|
|
58
|
+
if victim_align >= 350 and callable(obs_has_affect) and observer.has_affect(AffectFlag.DETECT_GOOD):
|
|
59
|
+
tags.append("(Golden Aura)")
|
|
60
|
+
|
|
61
|
+
if callable(has_affect) and victim.has_affect(AffectFlag.SANCTUARY):
|
|
62
|
+
tags.append("(White Aura)")
|
|
63
|
+
|
|
64
|
+
if not getattr(victim, "is_npc", False):
|
|
65
|
+
victim_act = int(getattr(victim, "act", 0) or 0)
|
|
66
|
+
if victim_act & int(PlayerFlag.KILLER):
|
|
67
|
+
tags.append("(KILLER)")
|
|
68
|
+
if victim_act & int(PlayerFlag.THIEF):
|
|
69
|
+
tags.append("(THIEF)")
|
|
70
|
+
|
|
71
|
+
return (" ".join(tags) + " ") if tags else ""
|
|
72
|
+
|
|
73
|
+
|
|
24
74
|
def _room_occupant_line(observer: Character, victim) -> str:
|
|
25
75
|
"""Render a room-list occupant — ROM src/act_info.c show_char_to_char_0.
|
|
26
76
|
|
|
27
77
|
An NPC whose position equals its start/default position and which has a
|
|
28
|
-
non-empty long_descr is listed by that long_descr (with
|
|
78
|
+
non-empty long_descr is listed by that long_descr (with the status tags
|
|
29
79
|
prefixed), e.g. "Hassan is here, waiting to dispense some justice."
|
|
30
80
|
Otherwise show PERS(victim) + a position suffix ("is resting here.", etc.).
|
|
31
81
|
"""
|
|
32
|
-
# LOOK-
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
prefixes = []
|
|
38
|
-
if hasattr(victim, "has_affect"):
|
|
39
|
-
if victim.has_affect(AffectFlag.FAERIE_FIRE):
|
|
40
|
-
prefixes.append("(Pink Aura)")
|
|
41
|
-
if victim.has_affect(AffectFlag.SANCTUARY):
|
|
42
|
-
prefixes.append("(White Aura)")
|
|
43
|
-
prefix = (" ".join(prefixes) + " ") if prefixes else ""
|
|
82
|
+
# LOOK char-tags: ROM builds the full status-tag prefix (all 12) then appends
|
|
83
|
+
# either long_descr or PERS. Use pers() for the base name (pure PERS, no aura
|
|
84
|
+
# injection) so the tags render once and in ROM order — describe_character is
|
|
85
|
+
# left for its combat/other callers, which a direct unit test locks.
|
|
86
|
+
prefix = _char_tags(observer, victim)
|
|
44
87
|
|
|
45
88
|
long_descr = getattr(victim, "long_descr", None)
|
|
46
89
|
# ROM uses start_pos for the long_descr gate; Python stores default_pos (same
|
|
@@ -49,8 +92,21 @@ def _room_occupant_line(observer: Character, victim) -> str:
|
|
|
49
92
|
if getattr(victim, "is_npc", False) and long_descr and getattr(victim, "position", None) == ref_pos:
|
|
50
93
|
return prefix + str(long_descr).rstrip("\r\n")
|
|
51
94
|
|
|
52
|
-
|
|
95
|
+
base = prefix + pers(victim, observer)
|
|
53
96
|
position = getattr(victim, "position", None)
|
|
97
|
+
# LOOK-017: ROM src/act_info.c:285-288 appends the victim's pcdata->title
|
|
98
|
+
# after PERS for a standing PC — gated on the OBSERVER's COMM_BRIEF and
|
|
99
|
+
# furniture (ch->comm / ch->on), a deliberate ROM quirk.
|
|
100
|
+
if (
|
|
101
|
+
not getattr(victim, "is_npc", False)
|
|
102
|
+
and not (int(getattr(observer, "comm", 0) or 0) & int(CommFlag.BRIEF))
|
|
103
|
+
and position == Position.STANDING
|
|
104
|
+
and getattr(observer, "on", None) is None
|
|
105
|
+
):
|
|
106
|
+
_pcdata = getattr(victim, "pcdata", None)
|
|
107
|
+
_title = getattr(_pcdata, "title", None) if _pcdata is not None else None
|
|
108
|
+
if _title:
|
|
109
|
+
base += _title
|
|
54
110
|
fighting = getattr(victim, "fighting", None)
|
|
55
111
|
if position == Position.FIGHTING:
|
|
56
112
|
# mirroring ROM src/act_info.c:404-417 show_char_to_char_0 POS_FIGHTING
|
|
@@ -59,14 +115,17 @@ def _room_occupant_line(observer: Character, victim) -> str:
|
|
|
59
115
|
elif fighting is observer:
|
|
60
116
|
fight_str = "YOU!"
|
|
61
117
|
elif getattr(victim, "room", None) is getattr(fighting, "room", object()):
|
|
62
|
-
|
|
118
|
+
# ROM src/act_info.c:412 uses PERS(victim->fighting, ch) — the bare
|
|
119
|
+
# name, NOT the show_char_to_char aura block. describe_character injects
|
|
120
|
+
# (Pink/White Aura), which ROM never shows here (FINDING-043, same class
|
|
121
|
+
# as the scan FINDING-042 aura/PERS leak).
|
|
122
|
+
fight_str = pers(fighting, observer) + "."
|
|
63
123
|
else:
|
|
64
124
|
fight_str = "someone who left??"
|
|
65
|
-
|
|
66
|
-
line = pers + " is here, fighting " + fight_str
|
|
125
|
+
line = base + " is here, fighting " + fight_str
|
|
67
126
|
else:
|
|
68
127
|
suffix = _POSITION_SUFFIX.get(position, "") if position is not None else ""
|
|
69
|
-
line =
|
|
128
|
+
line = base + suffix
|
|
70
129
|
# mirroring ROM src/act_info.c:421 buf[0] = UPPER(buf[0])
|
|
71
130
|
return line[0].upper() + line[1:] if line else line
|
|
72
131
|
|
|
@@ -307,20 +366,39 @@ def _look_char(char: Character, victim: Character) -> str:
|
|
|
307
366
|
condition = f"{short} is in awful condition."
|
|
308
367
|
else:
|
|
309
368
|
condition = f"{short} is bleeding to death."
|
|
369
|
+
# ROM src/act_info.c:480 — `buf[0] = UPPER(buf[0])` capitalizes the first
|
|
370
|
+
# char of the health line, so a mob's lowercase short_descr ("the beastly
|
|
371
|
+
# fido ...") renders "The beastly fido ..." (LOOK-014).
|
|
372
|
+
condition = condition[0].upper() + condition[1:] if condition else condition
|
|
310
373
|
lines.append(condition)
|
|
311
374
|
|
|
312
|
-
# Show equipment if visible
|
|
313
|
-
equipment = _show_equipment(victim)
|
|
375
|
+
# Show equipment if visible — ROM src/act_info.c:483-499 (LOOK-016).
|
|
376
|
+
equipment = _show_equipment(victim, char)
|
|
314
377
|
if equipment:
|
|
315
|
-
|
|
378
|
+
# ROM act("$N is using:", ...) capitalizes the rendered first char.
|
|
379
|
+
header = f"{short} is using:"
|
|
380
|
+
header = header[0].upper() + header[1:]
|
|
381
|
+
lines.append(f"\n{header}")
|
|
316
382
|
lines.append(equipment)
|
|
317
383
|
|
|
318
384
|
return "\n".join(lines)
|
|
319
385
|
|
|
320
386
|
|
|
321
|
-
def _show_equipment(
|
|
322
|
-
"""Show equipped items
|
|
323
|
-
|
|
387
|
+
def _show_equipment(victim: Character, observer: Character) -> str:
|
|
388
|
+
"""Show *victim*'s equipped items to *observer* — ROM show_char_to_char_1.
|
|
389
|
+
|
|
390
|
+
LOOK-016: mirrors ROM ``src/act_info.c:483-499`` — loop wear slots in
|
|
391
|
+
ascending order, gate each on ``can_see_obj(observer, obj)``, and render
|
|
392
|
+
``where_name[iWear] + format_obj_to_char(obj, observer, TRUE)`` (no indent,
|
|
393
|
+
with aura/status tags). The prior code read a **phantom** ``equipped``
|
|
394
|
+
attribute (via ``getattr``) where the real one is ``char.equipment``
|
|
395
|
+
(int-keyed by WearLocation), so the whole block was dead — the equipment-key
|
|
396
|
+
convention class via a wrong attribute NAME, invisible to the string-key
|
|
397
|
+
grep-guard.
|
|
398
|
+
"""
|
|
399
|
+
from mud.models.constants import WearLocation # noqa: F401 (enum keys hash-equal to int)
|
|
400
|
+
from mud.utils.act import format_obj_to_char
|
|
401
|
+
from mud.world.vision import can_see_object
|
|
324
402
|
|
|
325
403
|
wear_names = {
|
|
326
404
|
WearLocation.LIGHT: "<used as light> ",
|
|
@@ -345,13 +423,15 @@ def _show_equipment(char: Character) -> str:
|
|
|
345
423
|
}
|
|
346
424
|
|
|
347
425
|
lines = []
|
|
348
|
-
|
|
349
|
-
if isinstance(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
426
|
+
equipment = getattr(victim, "equipment", {})
|
|
427
|
+
if isinstance(equipment, dict):
|
|
428
|
+
# ROM iterates iWear 0..MAX_WEAR ascending; equipment is int-keyed.
|
|
429
|
+
for slot in sorted(equipment.keys()):
|
|
430
|
+
obj = equipment[slot]
|
|
431
|
+
if obj is None or not can_see_object(observer, obj):
|
|
432
|
+
continue
|
|
433
|
+
loc_name = wear_names.get(slot, "<unknown> ")
|
|
434
|
+
lines.append(f"{loc_name}{format_obj_to_char(obj, observer, True)}")
|
|
355
435
|
|
|
356
436
|
return "\n".join(lines)
|
|
357
437
|
|
|
@@ -417,10 +497,15 @@ def _look_in(char: Character, args: str) -> str:
|
|
|
417
497
|
if value[1] <= 0:
|
|
418
498
|
return "It is empty."
|
|
419
499
|
if value[0] > 0:
|
|
420
|
-
|
|
421
|
-
|
|
500
|
+
# LOOK-015: mirror ROM's exact integer comparisons
|
|
501
|
+
# (src/act_info.c:1141-1145) — `value[1] < value[0]/4` and
|
|
502
|
+
# `value[1] < 3*value[0]/4`. A rewritten `value[1]*100//value[0]`
|
|
503
|
+
# percentage truncates at a different point and mislabels boundary
|
|
504
|
+
# amounts (e.g. value=(10,2): ROM "about half-", percent form
|
|
505
|
+
# "less than half-"). value[0]/value[1] are non-negative, so // == C /.
|
|
506
|
+
if value[1] < value[0] // 4:
|
|
422
507
|
amount = "less than half-"
|
|
423
|
-
elif
|
|
508
|
+
elif value[1] < 3 * value[0] // 4:
|
|
424
509
|
amount = "about half-"
|
|
425
510
|
else:
|
|
426
511
|
amount = "more than half-"
|
|
@@ -477,9 +562,11 @@ def _look_direction(char: Character, room, direction: int) -> str:
|
|
|
477
562
|
keyword = getattr(exit_obj, "keyword", None)
|
|
478
563
|
exit_info = getattr(exit_obj, "exit_info", 0)
|
|
479
564
|
|
|
480
|
-
#
|
|
481
|
-
|
|
482
|
-
|
|
565
|
+
# ROM merc.h: EX_ISDOOR = (A) = 1, EX_CLOSED = (B) = 2. Never hardcode these
|
|
566
|
+
# (AGENTS.md flag-values rule) — the values below were previously swapped,
|
|
567
|
+
# so every keyword'd door (which always has the ISDOOR bit) rendered as
|
|
568
|
+
# "closed" even when open (LOOK-012).
|
|
569
|
+
from mud.models.constants import EX_CLOSED, EX_ISDOOR
|
|
483
570
|
|
|
484
571
|
if keyword and keyword.strip():
|
|
485
572
|
if exit_info & EX_CLOSED:
|
{rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rom24-quickmud-python
|
|
3
|
-
Version: 2.14.
|
|
3
|
+
Version: 2.14.298
|
|
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>
|
|
@@ -59,7 +59,7 @@ Dynamic: license-file
|
|
|
59
59
|
|
|
60
60
|
# QuickMUD - A Modern ROM 2.4 Python Port
|
|
61
61
|
|
|
62
|
-
[](https://github.com/Nostoi/rom24-quickmud-python)
|
|
63
63
|
[](https://www.python.org/downloads/)
|
|
64
64
|
[](https://opensource.org/licenses/MIT)
|
|
65
65
|
[](https://github.com/Nostoi/rom24-quickmud-python)
|
|
@@ -228,7 +228,7 @@ python -m mud # Start development server
|
|
|
228
228
|
**Stage: parity beta** — feature-complete and playable; parity fidelity is being
|
|
229
229
|
systematically hardened toward ROM-exact behavioral equivalence.
|
|
230
230
|
|
|
231
|
-
- **Version**: 2.14.
|
|
231
|
+
- **Version**: 2.14.268
|
|
232
232
|
- **Playability**: ✅ All 255 ROM commands implemented. Combat, spells, skills,
|
|
233
233
|
movement, shops, mob programs, OLC building, and admin tools work and pass their
|
|
234
234
|
tests. You can run a server and play today.
|
{rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/RECORD
RENAMED
|
@@ -19,7 +19,7 @@ mud/account/__init__.py,sha256=wwII0DQSMn6E8qGwukIw8is-7JEW7z-UDd6qdZDpSYU,1856
|
|
|
19
19
|
mud/account/account_manager.py,sha256=Ud1-nE55wfsTYokZ3i7dSNle9Hc7I7D0jllUOKMSSic,15449
|
|
20
20
|
mud/account/account_service.py,sha256=OmJ2CogFMZz4O7RI-THajyA5Pu5vn5Q8GCfcBXQpAh0,40597
|
|
21
21
|
mud/admin_logging/__init__.py,sha256=3W8Z1S7Dg-AtDTKPQIzuqL4_OojeyuAOryGH6BKozcg,40
|
|
22
|
-
mud/admin_logging/admin.py,sha256=
|
|
22
|
+
mud/admin_logging/admin.py,sha256=Gn99H4eORIt2KFt1zF7L4yEI0x5Cw1EYc7xoq4uo4NA,5280
|
|
23
23
|
mud/admin_logging/agent_trace.py,sha256=j1rtrINBp2RIB9BRGXK0awHTH8_nvSu4Co18zmdOU2w,359
|
|
24
24
|
mud/affects/engine.py,sha256=JtLaYZUyfm0hPJBmI-7-YcPOtcciGxsJGcz6d5utyrk,6690
|
|
25
25
|
mud/affects/saves.py,sha256=oiHYUagVbF3MLjYqLMAnUui_qYGr1zoUNsqDAhI2xKQ,5246
|
|
@@ -34,7 +34,7 @@ mud/characters/follow.py,sha256=8CBBB6hoimoqlKmWTkfVRekTXAJgICtvfqWqhqqLFsw,4438
|
|
|
34
34
|
mud/combat/__init__.py,sha256=YUphU7bKkxT3Qw0Gj2TcMmDoDyCYyRTQbHPCqKLjugE,215
|
|
35
35
|
mud/combat/assist.py,sha256=G4mQacit5Otcm7Ou1g8lgxZJz-B5QFsGcD5Gi3W0kmI,8775
|
|
36
36
|
mud/combat/death.py,sha256=S6KmG5nfPDHSsyPTK_SKqs3C7APyJ2eGF0LAjqj6HJE,25766
|
|
37
|
-
mud/combat/engine.py,sha256=
|
|
37
|
+
mud/combat/engine.py,sha256=3Qy7knjwakWCmmVKRpdFOZotaqhW37iOoACU86kxwn0,82545
|
|
38
38
|
mud/combat/kill_table.py,sha256=oR77me5GJLGXi3q5ZnQAdp3S5lsfJNhiCmaXOMQrNgA,1040
|
|
39
39
|
mud/combat/messages.py,sha256=c4kBvZFk2BneiJickTF3nRl84tq-InBJcLgKGzVTXMA,8708
|
|
40
40
|
mud/combat/safety.py,sha256=aRcywM5vSU3Ct2xzHW-Eeiwr71DemeU1g0oSTqCYB4U,9049
|
|
@@ -46,20 +46,20 @@ mud/commands/alias_cmds.py,sha256=SRG4hdX4HbLqQ9fbnKbPbe8AjPH4klLEdOFjeR4Ppxo,39
|
|
|
46
46
|
mud/commands/auto_settings.py,sha256=Ui99uWb7-MZ8JDvxSih-97tdLGrIlcCBkk4hjUct-t0,14593
|
|
47
47
|
mud/commands/build.py,sha256=RuirbGApM3Wm5xwlarU7VfqpF_JDef0_pSQYofzI5YU,152534
|
|
48
48
|
mud/commands/channels.py,sha256=EzAIgu4Hkp1unAdpmKb7TQ1DtuYUTEl_71SO4YnBuMM,1552
|
|
49
|
-
mud/commands/character.py,sha256=
|
|
50
|
-
mud/commands/combat.py,sha256=
|
|
49
|
+
mud/commands/character.py,sha256=rK4D0TrbU8bZyv0vx2CA5HdANTWmBV--C1JXd-TRbpk,6904
|
|
50
|
+
mud/commands/combat.py,sha256=hrETbdT_lUIoPmMBKenY1QWr8DEQ6ZHLaNmZcboy1zc,61633
|
|
51
51
|
mud/commands/communication.py,sha256=pFNih4ZePV3ZKrstm9WMU_ooMz00g_MOFzOHk91Qu-o,34963
|
|
52
|
-
mud/commands/compare.py,sha256=
|
|
52
|
+
mud/commands/compare.py,sha256=L-vSxp33rLEZe7_HOj901aX2g_Gv3FgQblsXgIZ4fcA,5124
|
|
53
53
|
mud/commands/consider.py,sha256=86stGtNOcdWI0I6sEtcDnB3R7qv6IKOGoUjbDnHuQ_c,3312
|
|
54
54
|
mud/commands/consumption.py,sha256=1t2lvDNmWzIfTa61KJruhxOJLtBuTjsV_Tcu2Byi-yU,15364
|
|
55
55
|
mud/commands/decorators.py,sha256=n_ezcovFkycAZOVxg0UNtMZLtiSlF0Yj8yqYt4YVY1U,345
|
|
56
56
|
mud/commands/dispatcher.py,sha256=sEOpkWiFGivFobZpg7GMGHlD_fEa9D34cFDf8eW3l_Q,52889
|
|
57
|
-
mud/commands/doors.py,sha256=
|
|
58
|
-
mud/commands/equipment.py,sha256=
|
|
57
|
+
mud/commands/doors.py,sha256=SSjYZ7J57FnxTzjT0Z1wbQa62ek1Nh_efgPgdFhGBgI,27299
|
|
58
|
+
mud/commands/equipment.py,sha256=bJ4lhUz4K2VDxh7cD_CtjFK1HN0OMMJuCciMWMdsFew,29550
|
|
59
59
|
mud/commands/feedback.py,sha256=MJtqeQgDeUEjteXce9fzAHgmtnneqpzS9oD6hIQknck,2460
|
|
60
60
|
mud/commands/give.py,sha256=xd9K05zxDlxVNHiQ2YzEC5OUCNKNULDHAfP1omMiu5s,10979
|
|
61
61
|
mud/commands/group_commands.py,sha256=xQJUQqXqAJOfmfgv4fvhEioNM-ivlA5bwDsias_ncsA,22509
|
|
62
|
-
mud/commands/healer.py,sha256
|
|
62
|
+
mud/commands/healer.py,sha256=5R-LeJc1QuJWsBjqoYcQLCIaQ6bg264X2I1YHDkcHWY,9260
|
|
63
63
|
mud/commands/help.py,sha256=J-oUmYD_6zyJ5RyqWk1M3Cipq6xJfGvpbZZe0mz678Q,11112
|
|
64
64
|
mud/commands/imc.py,sha256=aFrmiJJyeYMvcfhnNVCtJtKs04k-88BaLgmPj-vE-8o,12179
|
|
65
65
|
mud/commands/imm_admin.py,sha256=D4OczNuWVo5IQ74Jzz7j0Ypa6W09LyBPMDGmuwvQeHw,9279
|
|
@@ -72,10 +72,10 @@ mud/commands/imm_punish.py,sha256=zZPjJKbMbQ-saRPeE-27sI7UqgKUZP18gomNbyQh_X0,82
|
|
|
72
72
|
mud/commands/imm_search.py,sha256=TFgWqqo145sZW8GfMq9wID5iwAOY06XkPbFfRYOxKtc,46570
|
|
73
73
|
mud/commands/imm_server.py,sha256=JC4UkS8cDqmpQ91Dw34hfoQzgrLuvMZbgWXOLoltmAk,9148
|
|
74
74
|
mud/commands/imm_set.py,sha256=gHHXzCVvmAlBxqor9p3eoDQGaZYIDebXyJqTfNxe9WE,18423
|
|
75
|
-
mud/commands/info.py,sha256=
|
|
75
|
+
mud/commands/info.py,sha256=_TvpiXdOIX1tUaF9V0jDfTY2iDasDkYPJ_8imcx-guE,21135
|
|
76
76
|
mud/commands/info_extended.py,sha256=EuGPJK4qjpyIaD3m2cOdwXUpcs9bAevi-JCeu2BDUj0,11365
|
|
77
|
-
mud/commands/inspection.py,sha256=
|
|
78
|
-
mud/commands/inventory.py,sha256=
|
|
77
|
+
mud/commands/inspection.py,sha256=5ii1iYJT9oee-WJUrBjnhPIEugCe5hJNoeqMCIFOTnA,11000
|
|
78
|
+
mud/commands/inventory.py,sha256=aWfh_LoMCj4R1kzPWejmAarWl-5Pr0UNGo5ZIceA68s,37772
|
|
79
79
|
mud/commands/liquids.py,sha256=yeUzBIcVD9_K6QoeoBCaXipEZc1cYMbBkGP7aeZg-Pc,11546
|
|
80
80
|
mud/commands/magic_items.py,sha256=YBymF8EyYdENBqBMf6ujtGqM2onSv-kgMgCRrwBrIhw,14134
|
|
81
81
|
mud/commands/misc_info.py,sha256=gaaOEhV7hDLPJe2BUbxSAg-uFXjGfLfJxuLGgUeEDII,8350
|
|
@@ -84,14 +84,14 @@ mud/commands/mobprog_tools.py,sha256=DtfWBkzyYaXTqWoioupnkCH1Lf0mTfzvCZKlWQcSef4
|
|
|
84
84
|
mud/commands/movement.py,sha256=fSXODjxubl9bbEq65RlDxdTYKknrvAQ_YRkcGAOUqYk,3327
|
|
85
85
|
mud/commands/murder.py,sha256=mWW7AZRfrg86yVjc6EiXlXwApRC9ze5uryhUUXHyOGQ,5932
|
|
86
86
|
mud/commands/notes.py,sha256=hoSBmAN-Po6KGb3H6Yc8sGIJ0FNsn6U1pDpzb70pdek,18155
|
|
87
|
-
mud/commands/obj_manipulation.py,sha256=
|
|
87
|
+
mud/commands/obj_manipulation.py,sha256=ujye4CciOI0pDCXC8ds-7n-zPuW5Lb8eI8_2Zr5NbbI,28804
|
|
88
88
|
mud/commands/player_config.py,sha256=XCIRNGlDVw7jqD1uHFzPPoWSuS7v_2OAtFKPt2tdL_0,6548
|
|
89
89
|
mud/commands/player_info.py,sha256=aRCn3U-JmEka6zjp6dxRWvNTSF2XpuwVn2Ja6EhbRUg,7101
|
|
90
90
|
mud/commands/position.py,sha256=Br_iqeo_84yJGghMoUY5p2bXqbetiekthj2iK8C4-to,16360
|
|
91
91
|
mud/commands/remaining_rom.py,sha256=vGl3vBTfME99hhPPavQwUEempeaxr4g6Bwu2LzXDM_U,27959
|
|
92
|
-
mud/commands/session.py,sha256=
|
|
92
|
+
mud/commands/session.py,sha256=pgBAlVSliH2J6M1HTKoAakDstPp7-zPHkGfUMnCx2Ic,16910
|
|
93
93
|
mud/commands/shop.py,sha256=gynLzx08jrYZYiJJkHQeCABSgWkBvrrwTELJaYCYmMU,47657
|
|
94
|
-
mud/commands/socials.py,sha256=
|
|
94
|
+
mud/commands/socials.py,sha256=RNEzOw8xvxFUM9AoWSFhKeBOG-Opt-1DNiecgVkBUXQ,6492
|
|
95
95
|
mud/commands/thief_skills.py,sha256=B9UqeUVQZqE16VqOyh1cTO86oXy9m6K1iqOKdeDmXII,16578
|
|
96
96
|
mud/commands/typo_guards.py,sha256=SbvD9yZ6Z95qsCJN1CAQPtWNs6rsLH4W930_pc3SuoE,1435
|
|
97
97
|
mud/db/__init__.py,sha256=DuOdj63wifJjPIR252Z1WpYDdJ5M7vqoQVI3MqrGhYY,29
|
|
@@ -188,7 +188,7 @@ mud/security/bans.py,sha256=_nM9wEJbxxdLXoe0I31u8dTYgWP0wxMqMVB9mxxfhlk,9174
|
|
|
188
188
|
mud/security/hash_utils.py,sha256=GNd2W-TFLSIOk1WMQRuvJo8l4CueBYzPI0EhQuydxUg,656
|
|
189
189
|
mud/skills/__init__.py,sha256=zzIr-ejbrlrQF1CSepODWhpxQZv8rO2pgaqIi3vpMg0,241
|
|
190
190
|
mud/skills/groups.py,sha256=DRO7b-XPkP-DZudD-DVLMC74R0N6xXDFjceCrOct1Gw,8140
|
|
191
|
-
mud/skills/handlers.py,sha256=
|
|
191
|
+
mud/skills/handlers.py,sha256=jzkgfFvdqmRr3xyVdvEhOPZ7FB7aozr3ZJ-gktDflKM,303843
|
|
192
192
|
mud/skills/metadata.py,sha256=TbsOLZ1hKKAPTmrWBw4zKv1L3KCXByxpa2X2Rfir7G8,17480
|
|
193
193
|
mud/skills/registry.py,sha256=4e8GD9-SUdn_6FhM7yFguJINX2vjKv2Y4Tp7_49Rves,14899
|
|
194
194
|
mud/skills/say_spell.py,sha256=7Lcbx_oxA0JJQbaaILNb27xLbMGadauekyWHVnODiUM,4006
|
|
@@ -197,7 +197,7 @@ mud/spawning/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
|
197
197
|
mud/spawning/mob_spawner.py,sha256=aQRuctCIPpkVZlES5NH0dsvphkCWJWFCrJB5dy7aMKY,577
|
|
198
198
|
mud/spawning/obj_spawner.py,sha256=1jb1_sw7vvUVczXLi-dp5g_g6a-JVK2N4F4cuktiQF8,2930
|
|
199
199
|
mud/spawning/reset_handler.py,sha256=Dfkszau017ReeUd26R9DOl6UWB5YGEOrwHGgSTcPRCQ,35099
|
|
200
|
-
mud/spawning/templates.py,sha256=
|
|
200
|
+
mud/spawning/templates.py,sha256=hjQrZwFxDU4o3Tuja6DIdzBzTgTImUrbmV-13OLIsp8,31235
|
|
201
201
|
mud/utils/act.py,sha256=385Tfm77Wz6FhsGBvxi9zd67cyhQJ6uku5TyiEY-HtE,14329
|
|
202
202
|
mud/utils/bit.py,sha256=5W4uIoz_HVvUGBSyaO6JViuvseqO7s_1Gw0d2_y2m-4,4891
|
|
203
203
|
mud/utils/fix_sex.py,sha256=_MH1NkDOFn33aTlwUU6e2VMmO8oQJibQNvcH8_w1PPk,601
|
|
@@ -213,15 +213,15 @@ mud/utils/timing.py,sha256=fK4LNOEZk5iLUhkmX_VTBgk3zldDBwLjI-_UEDWhmCU,670
|
|
|
213
213
|
mud/world/__init__.py,sha256=HDEHtULHu1OWjOa3xglmhquW8L6NkJTWzLEURJ4BQjk,263
|
|
214
214
|
mud/world/char_find.py,sha256=AwP7IRukVEHcZY6qtakWYsf-fFJjha5aTR7OiOtj3_s,5647
|
|
215
215
|
mud/world/linking.py,sha256=mxEvgqA9Y6sDGXxN46aauU-jBhx4dOUcbgq64fIVz8g,1169
|
|
216
|
-
mud/world/look.py,sha256=
|
|
216
|
+
mud/world/look.py,sha256=Q3iaX8S1Dax6XaTR2XagQci1MHoxGDQn0Zi27cLDJUQ,25178
|
|
217
217
|
mud/world/movement.py,sha256=Rjcttco509exs42xvc1FkEnfJdF5qYX3BVIAaJoIcjE,26093
|
|
218
218
|
mud/world/obj_find.py,sha256=wVF3KlcEm4Bc4GiQZs4yWH5Ih87F-JSM7MtVI8cZEJs,5277
|
|
219
219
|
mud/world/time_persistence.py,sha256=GfhVr6H86Q4YXkeGhhgVAfm5s3ZBT8bF6K_zwqKRkAc,1663
|
|
220
220
|
mud/world/vision.py,sha256=Z7uDNjer2C6-VxlQp35wJO-FpaRiUIj5D1IkLq0wZ6g,12965
|
|
221
221
|
mud/world/world_state.py,sha256=58GTFAZ70d0Xp1FC8pU4PPJv5KlYI9Kmy_xeXoerfTo,9310
|
|
222
|
-
rom24_quickmud_python-2.14.
|
|
223
|
-
rom24_quickmud_python-2.14.
|
|
224
|
-
rom24_quickmud_python-2.14.
|
|
225
|
-
rom24_quickmud_python-2.14.
|
|
226
|
-
rom24_quickmud_python-2.14.
|
|
227
|
-
rom24_quickmud_python-2.14.
|
|
222
|
+
rom24_quickmud_python-2.14.298.dist-info/licenses/LICENSE,sha256=anQ2j9As6sIC8tZgQCXbo0-09JDH9vPiqhA9djnOvkY,1078
|
|
223
|
+
rom24_quickmud_python-2.14.298.dist-info/METADATA,sha256=kjZi7RVkj_GDL2WQoC_T_dm1NnWJUvtyvBtC2B6rWKE,19675
|
|
224
|
+
rom24_quickmud_python-2.14.298.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
225
|
+
rom24_quickmud_python-2.14.298.dist-info/entry_points.txt,sha256=VFru08UQTXZA_CkK-NBjJmWHyEX5a3864fQHjhaojbw,41
|
|
226
|
+
rom24_quickmud_python-2.14.298.dist-info/top_level.txt,sha256=Fk1WPmabIIjp7_iZXLYpbAVqiq7lG7TeAHt30AsOKtQ,4
|
|
227
|
+
rom24_quickmud_python-2.14.298.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{rom24_quickmud_python-2.14.264.dist-info → rom24_quickmud_python-2.14.298.dist-info}/top_level.txt
RENAMED
|
File without changes
|