rom24-quickmud-python 1.2.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.
Files changed (135) hide show
  1. mud/__init__.py +0 -0
  2. mud/__main__.py +40 -0
  3. mud/account/__init__.py +20 -0
  4. mud/account/account_manager.py +62 -0
  5. mud/account/account_service.py +80 -0
  6. mud/advancement.py +62 -0
  7. mud/affects/saves.py +123 -0
  8. mud/agent/__init__.py +0 -0
  9. mud/agent/agent_protocol.py +19 -0
  10. mud/agent/character_agent.py +61 -0
  11. mud/combat/__init__.py +3 -0
  12. mud/combat/engine.py +189 -0
  13. mud/commands/__init__.py +3 -0
  14. mud/commands/admin_commands.py +77 -0
  15. mud/commands/advancement.py +36 -0
  16. mud/commands/alias_cmds.py +44 -0
  17. mud/commands/build.py +18 -0
  18. mud/commands/combat.py +16 -0
  19. mud/commands/communication.py +55 -0
  20. mud/commands/decorators.py +11 -0
  21. mud/commands/dispatcher.py +206 -0
  22. mud/commands/healer.py +81 -0
  23. mud/commands/help.py +14 -0
  24. mud/commands/imc.py +19 -0
  25. mud/commands/inspection.py +113 -0
  26. mud/commands/inventory.py +42 -0
  27. mud/commands/movement.py +71 -0
  28. mud/commands/notes.py +44 -0
  29. mud/commands/shop.py +138 -0
  30. mud/commands/socials.py +34 -0
  31. mud/config.py +59 -0
  32. mud/db/__init__.py +0 -0
  33. mud/db/init.py +27 -0
  34. mud/db/migrate_from_files.py +87 -0
  35. mud/db/migrations.py +7 -0
  36. mud/db/models.py +98 -0
  37. mud/db/seed.py +28 -0
  38. mud/db/session.py +11 -0
  39. mud/devtools/__init__.py +0 -0
  40. mud/devtools/agent_demo.py +19 -0
  41. mud/entrypoint.py +34 -0
  42. mud/game_loop.py +117 -0
  43. mud/imc/__init__.py +17 -0
  44. mud/imc/protocol.py +32 -0
  45. mud/loaders/__init__.py +27 -0
  46. mud/loaders/area_loader.py +73 -0
  47. mud/loaders/base_loader.py +38 -0
  48. mud/loaders/help_loader.py +17 -0
  49. mud/loaders/json_area_loader.py +203 -0
  50. mud/loaders/json_loader.py +285 -0
  51. mud/loaders/mob_loader.py +104 -0
  52. mud/loaders/obj_loader.py +76 -0
  53. mud/loaders/reset_loader.py +29 -0
  54. mud/loaders/room_loader.py +63 -0
  55. mud/loaders/shop_loader.py +41 -0
  56. mud/loaders/social_loader.py +16 -0
  57. mud/loaders/specials_loader.py +63 -0
  58. mud/logging/__init__.py +0 -0
  59. mud/logging/admin.py +40 -0
  60. mud/logging/agent_trace.py +9 -0
  61. mud/math/c_compat.py +27 -0
  62. mud/mobprog.py +72 -0
  63. mud/models/__init__.py +106 -0
  64. mud/models/area.py +33 -0
  65. mud/models/area_json.py +27 -0
  66. mud/models/board.py +49 -0
  67. mud/models/board_json.py +16 -0
  68. mud/models/character.py +195 -0
  69. mud/models/character_json.py +46 -0
  70. mud/models/constants.py +423 -0
  71. mud/models/conversion.py +45 -0
  72. mud/models/help.py +28 -0
  73. mud/models/help_json.py +14 -0
  74. mud/models/json_io.py +64 -0
  75. mud/models/mob.py +82 -0
  76. mud/models/note.py +29 -0
  77. mud/models/note_json.py +16 -0
  78. mud/models/obj.py +82 -0
  79. mud/models/object.py +28 -0
  80. mud/models/object_json.py +40 -0
  81. mud/models/player_json.py +29 -0
  82. mud/models/room.py +86 -0
  83. mud/models/room_json.py +46 -0
  84. mud/models/shop.py +21 -0
  85. mud/models/shop_json.py +17 -0
  86. mud/models/skill.py +25 -0
  87. mud/models/skill_json.py +20 -0
  88. mud/models/social.py +78 -0
  89. mud/models/social_json.py +20 -0
  90. mud/net/__init__.py +9 -0
  91. mud/net/ansi.py +27 -0
  92. mud/net/connection.py +174 -0
  93. mud/net/protocol.py +57 -0
  94. mud/net/session.py +21 -0
  95. mud/net/telnet_server.py +31 -0
  96. mud/network/__init__.py +0 -0
  97. mud/network/websocket_server.py +83 -0
  98. mud/network/websocket_session.py +21 -0
  99. mud/notes.py +45 -0
  100. mud/persistence.py +185 -0
  101. mud/registry.py +5 -0
  102. mud/scripts/convert_are_to_json.py +162 -0
  103. mud/scripts/convert_help_are_to_json.py +92 -0
  104. mud/scripts/convert_player_to_json.py +112 -0
  105. mud/scripts/convert_shops_to_json.py +64 -0
  106. mud/scripts/convert_skills_to_json.py +166 -0
  107. mud/scripts/convert_social_are_to_json.py +92 -0
  108. mud/scripts/load_test_data.py +17 -0
  109. mud/security/__init__.py +0 -0
  110. mud/security/bans.py +112 -0
  111. mud/security/hash_utils.py +20 -0
  112. mud/server.py +8 -0
  113. mud/skills/__init__.py +3 -0
  114. mud/skills/handlers.py +795 -0
  115. mud/skills/registry.py +97 -0
  116. mud/spawning/__init__.py +1 -0
  117. mud/spawning/mob_spawner.py +13 -0
  118. mud/spawning/obj_spawner.py +18 -0
  119. mud/spawning/reset_handler.py +222 -0
  120. mud/spawning/templates.py +63 -0
  121. mud/spec_funs.py +57 -0
  122. mud/time.py +48 -0
  123. mud/utils/rng_mm.py +123 -0
  124. mud/wiznet.py +74 -0
  125. mud/world/__init__.py +11 -0
  126. mud/world/linking.py +31 -0
  127. mud/world/look.py +29 -0
  128. mud/world/movement.py +135 -0
  129. mud/world/world_state.py +179 -0
  130. rom24_quickmud_python-1.2.2.dist-info/METADATA +236 -0
  131. rom24_quickmud_python-1.2.2.dist-info/RECORD +135 -0
  132. rom24_quickmud_python-1.2.2.dist-info/WHEEL +5 -0
  133. rom24_quickmud_python-1.2.2.dist-info/entry_points.txt +2 -0
  134. rom24_quickmud_python-1.2.2.dist-info/licenses/LICENSE +21 -0
  135. rom24_quickmud_python-1.2.2.dist-info/top_level.txt +1 -0
mud/models/help.py ADDED
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .help_json import HelpJson
6
+
7
+
8
+ @dataclass
9
+ class HelpEntry:
10
+ """Runtime representation of a help entry."""
11
+
12
+ keywords: list[str]
13
+ text: str
14
+ level: int = 0
15
+
16
+ @classmethod
17
+ def from_json(cls, data: HelpJson) -> "HelpEntry":
18
+ return cls(**data.to_dict())
19
+
20
+
21
+ # placeholder registry to track loaded help entries
22
+ help_registry: dict[str, HelpEntry] = {}
23
+
24
+
25
+ def register_help(entry: HelpEntry) -> None:
26
+ """Register a help entry under each keyword."""
27
+ for keyword in entry.keywords:
28
+ help_registry[keyword.lower()] = entry
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .json_io import JsonDataclass
6
+
7
+
8
+ @dataclass
9
+ class HelpJson(JsonDataclass):
10
+ """Help entry matching ``schemas/help.schema.json``."""
11
+
12
+ keywords: list[str]
13
+ text: str
14
+ level: int = 0
mud/models/json_io.py ADDED
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import MISSING, asdict, fields, is_dataclass
5
+ from typing import Any, Type, TypeVar, get_args, get_origin, get_type_hints, cast
6
+
7
+ T = TypeVar("T")
8
+ JsonT = TypeVar("JsonT", bound="JsonDataclass")
9
+
10
+
11
+ def _convert_value(annotation, value):
12
+ origin = get_origin(annotation)
13
+ if is_dataclass(annotation):
14
+ return dataclass_from_dict(annotation, value)
15
+ if origin is list:
16
+ (item_type,) = get_args(annotation)
17
+ return [_convert_value(item_type, v) for v in value]
18
+ if origin is dict:
19
+ key_type, val_type = get_args(annotation)
20
+ # assume keys are primitive; only convert values
21
+ return {k: _convert_value(val_type, v) for k, v in value.items()}
22
+ return value
23
+
24
+
25
+ def dataclass_from_dict(cls: Type[T], data: dict) -> T:
26
+ """Instantiate ``cls`` from ``data`` applying dataclass defaults."""
27
+ kwargs = {}
28
+ hints = get_type_hints(cls)
29
+ for f in fields(cast(Any, cls)):
30
+ ann = hints.get(f.name, f.type)
31
+ if f.name in data:
32
+ kwargs[f.name] = _convert_value(ann, data[f.name])
33
+ elif f.default is not MISSING:
34
+ kwargs[f.name] = f.default
35
+ elif f.default_factory is not MISSING: # type: ignore[attr-defined]
36
+ kwargs[f.name] = f.default_factory() # type: ignore[misc]
37
+ return cls(**kwargs) # type: ignore[arg-type]
38
+
39
+
40
+ def dataclass_to_dict(obj: Any) -> dict:
41
+ """Convert ``obj`` into a JSON-serializable ``dict``."""
42
+ return asdict(obj)
43
+
44
+
45
+ def load_dataclass(cls: Type[T], fp) -> T:
46
+ """Load ``cls`` from a JSON file-like object."""
47
+ data = json.load(fp)
48
+ return dataclass_from_dict(cls, data)
49
+
50
+
51
+ def dump_dataclass(obj: Any, fp, **json_kwargs) -> None:
52
+ """Dump dataclass instance ``obj`` to ``fp`` as JSON."""
53
+ json.dump(dataclass_to_dict(obj), fp, **json_kwargs)
54
+
55
+
56
+ class JsonDataclass:
57
+ """Mixin adding ``to_dict``/``from_dict`` helpers to dataclasses."""
58
+
59
+ def to_dict(self) -> dict:
60
+ return dataclass_to_dict(self)
61
+
62
+ @classmethod
63
+ def from_dict(cls: Type[JsonT], data: dict) -> JsonT:
64
+ return dataclass_from_dict(cls, data)
mud/models/mob.py ADDED
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from typing import List, Optional, Tuple
4
+
5
+ @dataclass
6
+ class MobProgram:
7
+ """Representation of MPROG_LIST"""
8
+ trig_type: int
9
+ trig_phrase: Optional[str] = None
10
+ vnum: int = 0
11
+ code: Optional[str] = None
12
+
13
+ @dataclass
14
+ class MobIndex:
15
+ """Python representation of MOB_INDEX_DATA"""
16
+ vnum: int
17
+ player_name: Optional[str] = None
18
+ short_descr: Optional[str] = None
19
+ long_descr: Optional[str] = None
20
+ description: Optional[str] = None
21
+ race: Optional[str] = None
22
+ act_flags: str = ''
23
+ affected_by: str = ''
24
+ alignment: int = 0
25
+ group: int = 0
26
+ level: int = 1
27
+ thac0: int = 20
28
+ ac: str = '1d1+0'
29
+ hit_dice: str = '1d1+0'
30
+ mana_dice: str = '1d1+0'
31
+ damage_dice: str = '1d4+0'
32
+ damage_type: str = 'beating'
33
+ ac_pierce: int = 0
34
+ ac_bash: int = 0
35
+ ac_slash: int = 0
36
+ ac_exotic: int = 0
37
+ offensive: str = ''
38
+ immune: str = ''
39
+ resist: str = ''
40
+ vuln: str = ''
41
+ start_pos: str = 'standing'
42
+ default_pos: str = 'standing'
43
+ sex: str = 'neutral'
44
+ wealth: int = 0
45
+ form: str = '0'
46
+ parts: str = '0'
47
+ size: str = 'medium'
48
+ material: str = '0'
49
+ spec_fun: Optional[str] = None
50
+ pShop: Optional[object] = None
51
+ mprogs: List[MobProgram] = field(default_factory=list)
52
+ area: Optional['Area'] = None
53
+ new_format: bool = False
54
+ count: int = 0
55
+ killed: int = 0
56
+ # Legacy compatibility fields
57
+ act: int = 0
58
+ hitroll: int = 0
59
+ hit: Tuple[int, int, int] = (0, 0, 0)
60
+ mana: Tuple[int, int, int] = (0, 0, 0)
61
+ damage: Tuple[int, int, int] = (0, 0, 0)
62
+ dam_type: int = 0
63
+ off_flags: int = 0
64
+ imm_flags: int = 0
65
+ res_flags: int = 0
66
+ vuln_flags: int = 0
67
+ start_pos: int = 0
68
+ default_pos: int = 0
69
+ sex: int = 0
70
+ race: int = 0
71
+ wealth: int = 0
72
+ form: int = 0
73
+ parts: int = 0
74
+ size: int = 0
75
+ material: Optional[str] = None
76
+ mprog_flags: int = 0
77
+
78
+ def __repr__(self) -> str:
79
+ return f"<MobIndex vnum={self.vnum} name={self.short_descr!r}>"
80
+
81
+
82
+ mob_registry: dict[int, MobIndex] = {}
mud/models/note.py ADDED
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .note_json import NoteJson
6
+
7
+
8
+ @dataclass
9
+ class Note:
10
+ """Runtime representation of a message board note."""
11
+
12
+ sender: str
13
+ to: str
14
+ subject: str
15
+ text: str
16
+ timestamp: float
17
+
18
+ def to_json(self) -> NoteJson:
19
+ return NoteJson(
20
+ sender=self.sender,
21
+ to=self.to,
22
+ subject=self.subject,
23
+ text=self.text,
24
+ timestamp=self.timestamp,
25
+ )
26
+
27
+ @classmethod
28
+ def from_json(cls, data: NoteJson) -> "Note":
29
+ return cls(**data.to_dict())
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from .json_io import JsonDataclass
6
+
7
+
8
+ @dataclass
9
+ class NoteJson(JsonDataclass):
10
+ """Schema-aligned representation of a message board note."""
11
+
12
+ sender: str
13
+ to: str
14
+ subject: str
15
+ text: str
16
+ timestamp: float
mud/models/obj.py ADDED
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+ from .room import ExtraDescr, Room
3
+ from .character import Character
4
+ from dataclasses import dataclass, field
5
+ from typing import List, Optional
6
+
7
+ @dataclass
8
+ class Affect:
9
+ """Representation of AFFECT_DATA"""
10
+ where: int
11
+ type: int
12
+ level: int
13
+ duration: int
14
+ location: int
15
+ modifier: int
16
+ bitvector: int
17
+
18
+ @dataclass
19
+ class ObjIndex:
20
+ """Python representation of OBJ_INDEX_DATA"""
21
+ vnum: int
22
+ name: Optional[str] = None
23
+ short_descr: Optional[str] = None
24
+ description: Optional[str] = None
25
+ material: Optional[str] = None
26
+ item_type: str = 'trash'
27
+ extra_flags: int = 0
28
+ wear_flags: str = ''
29
+ level: int = 0
30
+ condition: str = 'P'
31
+ count: int = 0
32
+ weight: int = 0
33
+ cost: int = 0
34
+ value: List[int] = field(default_factory=lambda: [0] * 5)
35
+ affects: List[dict] = field(default_factory=list) # {'location': int, 'modifier': int}
36
+ extra_descr: List[dict] = field(default_factory=list) # {'keyword': str, 'description': str}
37
+ area: Optional['Area'] = None
38
+ new_format: bool = False
39
+ reset_num: int = 0
40
+ next: Optional['ObjIndex'] = None
41
+ # Legacy compatibility
42
+ affected: List[Affect] = field(default_factory=list)
43
+
44
+ def __repr__(self) -> str:
45
+ return f"<ObjIndex vnum={self.vnum} name={self.short_descr!r}>"
46
+
47
+ obj_index_registry: dict[int, ObjIndex] = {}
48
+
49
+ @dataclass
50
+ class ObjectData:
51
+ """Python representation of OBJ_DATA"""
52
+ item_type: int
53
+ extra_flags: int = 0
54
+ wear_flags: int = 0
55
+ wear_loc: int = 0
56
+ weight: int = 0
57
+ cost: int = 0
58
+ level: int = 0
59
+ condition: int = 0
60
+ timer: int = 0
61
+ value: List[int] = field(default_factory=lambda: [0] * 5)
62
+ owner: Optional[str] = None
63
+ name: Optional[str] = None
64
+ short_descr: Optional[str] = None
65
+ description: Optional[str] = None
66
+ material: Optional[str] = None
67
+ carried_by: Optional['Character'] = None
68
+ in_obj: Optional['ObjectData'] = None
69
+ contains: List['ObjectData'] = field(default_factory=list)
70
+ extra_descr: List['ExtraDescr'] = field(default_factory=list)
71
+ affected: List[Affect] = field(default_factory=list)
72
+ pIndexData: Optional[ObjIndex] = None
73
+ in_room: Optional['Room'] = None
74
+ enchanted: bool = False
75
+ next_content: Optional['ObjectData'] = None
76
+ next: Optional['ObjectData'] = None
77
+
78
+ def __repr__(self) -> str:
79
+ return f"<ObjectData type={self.item_type} name={self.short_descr!r}>"
80
+
81
+
82
+ object_registry: list[ObjectData] = []
mud/models/object.py ADDED
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from typing import Optional, List, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from .room import Room
7
+
8
+ from .obj import ObjIndex
9
+
10
+
11
+ @dataclass
12
+ class Object:
13
+ """Instance of an object tied to a prototype."""
14
+ instance_id: Optional[int]
15
+ prototype: ObjIndex
16
+ location: Optional['Room'] = None
17
+ contained_items: List['Object'] = field(default_factory=list)
18
+ level: int = 0
19
+ # Instance values — copy of prototype.value for runtime mutations (e.g., locks/charges)
20
+ value: List[int] = field(default_factory=lambda: [0, 0, 0, 0, 0])
21
+
22
+ @property
23
+ def name(self) -> Optional[str]:
24
+ return self.prototype.name
25
+
26
+ @property
27
+ def short_descr(self) -> Optional[str]:
28
+ return getattr(self.prototype, "short_descr", None)
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List, Optional
5
+
6
+ from .json_io import JsonDataclass
7
+
8
+
9
+ @dataclass
10
+ class AffectJson(JsonDataclass):
11
+ """Per-object affect modifier."""
12
+ location: str
13
+ modifier: int
14
+
15
+
16
+ @dataclass
17
+ class ExtraDescriptionJson(JsonDataclass):
18
+ """Extra description block for objects."""
19
+ keyword: str
20
+ description: str
21
+
22
+
23
+ @dataclass
24
+ class ObjectJson(JsonDataclass):
25
+ """Object record matching ``schemas/object.schema.json``."""
26
+ id: int
27
+ name: str
28
+ description: str
29
+ item_type: str
30
+ values: List[int]
31
+ weight: int
32
+ cost: int
33
+ short_description: Optional[str] = None
34
+ flags: List[str] = field(default_factory=list)
35
+ wear_flags: List[str] = field(default_factory=list)
36
+ level: int = 0
37
+ condition: int = 0
38
+ material: str = ""
39
+ affects: List[AffectJson] = field(default_factory=list)
40
+ extra_descriptions: List[ExtraDescriptionJson] = field(default_factory=list)
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, List, Optional
5
+
6
+ from .json_io import JsonDataclass
7
+
8
+
9
+ @dataclass
10
+ class PlayerJson(JsonDataclass):
11
+ """Player record matching ``schemas/player.schema.json``."""
12
+
13
+ name: str
14
+ level: int
15
+ hit: int
16
+ max_hit: int
17
+ mana: int
18
+ max_mana: int
19
+ move: int
20
+ max_move: int
21
+ gold: int
22
+ silver: int
23
+ exp: int
24
+ position: int
25
+ room_vnum: Optional[int]
26
+ inventory: List[int] = field(default_factory=list)
27
+ equipment: Dict[str, int] = field(default_factory=dict)
28
+ plr_flags: int = 0
29
+ comm_flags: int = 0
mud/models/room.py ADDED
@@ -0,0 +1,86 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from typing import List, Optional, TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from mud.models.object import Object
7
+ from mud.spawning.templates import MobInstance
8
+ from mud.models.area import Area
9
+ from mud.models.character import Character
10
+
11
+ from .constants import Direction
12
+ from .room_json import ResetJson
13
+
14
+ @dataclass
15
+ class ExtraDescr:
16
+ """Python representation of EXTRA_DESCR_DATA"""
17
+ keyword: Optional[str] = None
18
+ description: Optional[str] = None
19
+
20
+ @dataclass
21
+ class Exit:
22
+ """Representation of EXIT_DATA"""
23
+ to_room: Optional['Room'] = None
24
+ vnum: Optional[int] = None
25
+ exit_info: int = 0
26
+ key: int = 0
27
+ keyword: Optional[str] = None
28
+ description: Optional[str] = None
29
+ flags: str = '0' # String representation of exit flags
30
+ rs_flags: int = 0
31
+ orig_door: int = 0
32
+
33
+ @dataclass
34
+ class Room:
35
+ """Runtime room container built from area files."""
36
+ vnum: int
37
+ name: Optional[str] = None
38
+ description: Optional[str] = None
39
+ owner: Optional[str] = None
40
+ area: Optional['Area'] = None
41
+ room_flags: int = 0
42
+ light: int = 0
43
+ sector_type: int = 0
44
+ heal_rate: int = 0
45
+ mana_rate: int = 0
46
+ clan: int = 0
47
+ exits: List[Optional[Exit]] = field(default_factory=lambda: [None] * len(Direction))
48
+ extra_descr: List[ExtraDescr] = field(default_factory=list)
49
+ resets: List[ResetJson] = field(default_factory=list)
50
+ people: List['Character'] = field(default_factory=list)
51
+ contents: List['Object'] = field(default_factory=list)
52
+ next: Optional['Room'] = None
53
+
54
+ def __repr__(self) -> str:
55
+ return f"<Room vnum={self.vnum} name={self.name!r}>"
56
+
57
+ def add_character(self, char: 'Character') -> None:
58
+ if char not in self.people:
59
+ self.people.append(char)
60
+ char.room = self
61
+
62
+ def remove_character(self, char: 'Character') -> None:
63
+ if char in self.people:
64
+ self.people.remove(char)
65
+
66
+ def add_object(self, obj: 'Object') -> None:
67
+ if obj not in self.contents:
68
+ self.contents.append(obj)
69
+ if hasattr(obj, "location"):
70
+ obj.location = self
71
+
72
+ def add_mob(self, mob: 'MobInstance') -> None:
73
+ if mob not in self.people:
74
+ self.people.append(mob)
75
+ mob.room = self
76
+
77
+ def broadcast(self, message: str, exclude: Optional['Character'] = None) -> None:
78
+ for char in self.people:
79
+ if char is exclude:
80
+ continue
81
+ if hasattr(char, 'messages'):
82
+ char.messages.append(message)
83
+
84
+
85
+ room_registry: dict[int, Room] = {}
86
+
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict, List, Optional
5
+
6
+ from .json_io import JsonDataclass
7
+
8
+
9
+ @dataclass
10
+ class ExitJson(JsonDataclass):
11
+ """Exit specification for JSON rooms."""
12
+ to_room: int
13
+ description: Optional[str] = None
14
+ keyword: Optional[str] = None
15
+ flags: List[str] = field(default_factory=list)
16
+
17
+
18
+ @dataclass
19
+ class ExtraDescriptionJson(JsonDataclass):
20
+ """Extra description block for JSON rooms."""
21
+ keyword: str
22
+ description: str
23
+
24
+
25
+ @dataclass
26
+ class ResetJson(JsonDataclass):
27
+ """Reset command affecting a room."""
28
+ command: str
29
+ arg1: Optional[int] = None
30
+ arg2: Optional[int] = None
31
+ arg3: Optional[int] = None
32
+ arg4: Optional[int] = None
33
+
34
+
35
+ @dataclass
36
+ class RoomJson(JsonDataclass):
37
+ """Room record matching ``schemas/room.schema.json``."""
38
+ id: int
39
+ name: str
40
+ description: str
41
+ sector_type: str
42
+ area: int
43
+ flags: List[str] = field(default_factory=list)
44
+ exits: Dict[str, ExitJson] = field(default_factory=dict)
45
+ extra_descriptions: List[ExtraDescriptionJson] = field(default_factory=list)
46
+ resets: List[ResetJson] = field(default_factory=list)
mud/models/shop.py ADDED
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from .shop_json import ShopJson
6
+
7
+
8
+ @dataclass
9
+ class Shop:
10
+ """Runtime representation of a shop."""
11
+
12
+ keeper: int
13
+ buy_types: list[str] = field(default_factory=list)
14
+ profit_buy: int = 100
15
+ profit_sell: int = 100
16
+ open_hour: int = 0
17
+ close_hour: int = 23
18
+
19
+ @classmethod
20
+ def from_json(cls, data: ShopJson) -> "Shop":
21
+ return cls(**data.to_dict())
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from .json_io import JsonDataclass
6
+
7
+
8
+ @dataclass
9
+ class ShopJson(JsonDataclass):
10
+ """Shop record matching ``schemas/shop.schema.json``."""
11
+
12
+ keeper: int
13
+ buy_types: list[str] = field(default_factory=list)
14
+ profit_buy: int = 100
15
+ profit_sell: int = 100
16
+ open_hour: int = 0
17
+ close_hour: int = 23
mud/models/skill.py ADDED
@@ -0,0 +1,25 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Dict
5
+
6
+ from .skill_json import SkillJson
7
+
8
+
9
+ @dataclass
10
+ class Skill:
11
+ """Runtime representation of a skill or spell."""
12
+
13
+ name: str
14
+ type: str
15
+ function: str
16
+ target: str = "victim"
17
+ mana_cost: int = 0
18
+ lag: int = 0
19
+ cooldown: int = 0
20
+ failure_rate: float = 0.0
21
+ messages: Dict[str, str] = field(default_factory=dict)
22
+
23
+ @classmethod
24
+ def from_json(cls, data: SkillJson) -> "Skill":
25
+ return cls(**data.to_dict())
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from .json_io import JsonDataclass
6
+
7
+
8
+ @dataclass
9
+ class SkillJson(JsonDataclass):
10
+ """Schema-aligned representation of a skill or spell."""
11
+
12
+ name: str
13
+ type: str
14
+ function: str
15
+ target: str = "victim"
16
+ mana_cost: int = 0
17
+ lag: int = 0
18
+ cooldown: int = 0
19
+ failure_rate: float = 0.0
20
+ messages: dict[str, str] = field(default_factory=dict)