osrlib 2.0.1__tar.gz → 2.2.0__tar.gz

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 (61) hide show
  1. {osrlib-2.0.1 → osrlib-2.2.0}/PKG-INFO +1 -1
  2. {osrlib-2.0.1 → osrlib-2.2.0}/pyproject.toml +1 -1
  3. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/character.py +13 -3
  4. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/classes.py +12 -6
  5. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/combat.py +215 -133
  6. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/effects.py +11 -5
  7. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/events.py +95 -8
  8. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/items.py +113 -49
  9. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/monsters.py +87 -13
  10. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/ruleset.py +6 -5
  11. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/spells.py +21 -2
  12. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/adventure.py +73 -41
  13. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/battle.py +1307 -287
  14. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/commands.py +1000 -300
  15. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/dungeon.py +119 -32
  16. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/encounter.py +200 -65
  17. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/events.py +258 -37
  18. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/exploration.py +158 -60
  19. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/interpreter.py +72 -43
  20. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/narrative.py +4 -4
  21. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/party.py +63 -15
  22. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/quests.py +10 -7
  23. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/session.py +445 -70
  24. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/triggers.py +45 -19
  25. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/views.py +20 -4
  26. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/monsters.json +572 -287
  27. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/spells.json +2 -1
  28. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/messages.py +35 -2
  29. {osrlib-2.0.1 → osrlib-2.2.0}/LICENSE +0 -0
  30. {osrlib-2.0.1 → osrlib-2.2.0}/LICENSE-OGL.md +0 -0
  31. {osrlib-2.0.1 → osrlib-2.2.0}/README.md +0 -0
  32. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/__init__.py +0 -0
  33. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/__init__.py +0 -0
  34. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/abilities.py +0 -0
  35. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/alignment.py +0 -0
  36. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/clock.py +0 -0
  37. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/creature.py +0 -0
  38. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/dice.py +0 -0
  39. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/npc.py +0 -0
  40. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/rng.py +0 -0
  41. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/tables.py +0 -0
  42. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/treasure.py +0 -0
  43. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/core/validation.py +0 -0
  44. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/__init__.py +0 -0
  45. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/content_pack.py +0 -0
  46. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/gates.py +0 -0
  47. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/crawl/stocking.py +0 -0
  48. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/LICENSE-OGL.md +0 -0
  49. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/__init__.py +0 -0
  50. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/abilities.json +0 -0
  51. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/classes.json +0 -0
  52. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/combat_tables.json +0 -0
  53. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/encounter_tables.json +0 -0
  54. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/equipment.json +0 -0
  55. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/languages.json +0 -0
  56. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/magic_items.json +0 -0
  57. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/data/treasure.json +0 -0
  58. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/errors.py +0 -0
  59. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/persistence.py +0 -0
  60. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/py.typed +0 -0
  61. {osrlib-2.0.1 → osrlib-2.2.0}/src/osrlib/versioning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osrlib
3
- Version: 2.0.1
3
+ Version: 2.2.0
4
4
  Summary: B/X (1981 Basic/Expert) tabletop RPG rules engine for turn-based dungeon crawlers
5
5
  Keywords: basic-expert,bx,dungeon-crawler,game-engine,osr,rpg,srd,ttrpg
6
6
  Author: Marsh Macy
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "osrlib"
3
- version = "2.0.1"
3
+ version = "2.2.0"
4
4
  description = "B/X (1981 Basic/Expert) tabletop RPG rules engine for turn-based dungeon crawlers"
5
5
  readme = "README.md"
6
6
  # The wheel ships Open Game Content (osrlib/data/*.json and its LICENSE-OGL.md
@@ -228,9 +228,10 @@ class Character(BaseModel):
228
228
  id: str | None = None
229
229
  """The character's id within a game, like `"character-0001"`, or `None` before a session assigns one.
230
230
 
231
- [`GameSession.new`][osrlib.crawl.session.GameSession.new] assigns an id to each party member whose id is `None`,
232
- from the session's [`IdAllocator`][osrlib.core.monsters.IdAllocator]. Commands refer to a character by this id. An
233
- event about a character with no id uses the character's `name` instead.
231
+ [`GameSession.new`][osrlib.crawl.session.GameSession.new] assigns an id from the session's
232
+ [`IdAllocator`][osrlib.core.monsters.IdAllocator] to each party member whose id is `None`.
233
+ [`AddMember`][osrlib.crawl.commands.AddMember] does the same for a character who joins during play. Commands refer
234
+ to a character by this id. An event about a character with no id uses the character's `name` instead.
234
235
  """
235
236
 
236
237
  name: str = Field(min_length=1)
@@ -287,6 +288,15 @@ class Character(BaseModel):
287
288
  [`kill`][osrlib.core.effects.kill] applies, not a hit point value.
288
289
  """
289
290
 
291
+ nonregen_damage: int = Field(default=0, ge=0)
292
+ """The fire and acid damage that a worn ring of regeneration can't heal, up to `max_hp`.
293
+
294
+ While the character wears the ring, [`deal_damage`][osrlib.core.combat.deal_damage] adds fire and acid damage
295
+ here. The ring heals the character only up to `max_hp` less this amount. Other healing, like a potion, heals this
296
+ damage too. So each time the character takes damage while wearing the ring, `deal_damage` first lowers this value
297
+ to the damage the character had before that hit. It's 0 for a character who has never worn the ring.
298
+ """
299
+
290
300
  inventory: Inventory = Field(default_factory=Inventory)
291
301
  """Everything the character carries, wears, and wields, and their coins. See
292
302
  [`Inventory`][osrlib.core.items.Inventory] and the buying and equipping functions in
@@ -1180,9 +1180,10 @@ class DrainResult(BaseModel):
1180
1180
  """
1181
1181
 
1182
1182
  hp_lost: int
1183
- """The hit points the drain takes, from both maximum and current hit points.
1183
+ """The hit points the drain rolls to take, from both maximum and current hit points.
1184
1184
 
1185
1185
  Neither total drops below 1 while the character has a level left, so the totals can fall by less than this.
1186
+ The drain event in `events` has its own `hp_lost`, which is how far the maximum actually fell.
1186
1187
  """
1187
1188
 
1188
1189
  xp_after: int | None = None
@@ -1249,8 +1250,10 @@ def drain_levels(
1249
1250
  `streams.get(`[`ADVANCEMENT_STREAM`][osrlib.core.character.ADVANCEMENT_STREAM]`)`,
1250
1251
  the stream that level gains draw from.
1251
1252
  spawn_consequence: A description of what a victim drained of all levels becomes, like the
1252
- description of the monster's `energy_drain` ability. This function copies it onto the
1253
- drain event for you to show, and osrlib doesn't act on it. `None` leaves it off.
1253
+ description of the monster's `energy_drain` ability. When the drain kills the character,
1254
+ this function copies it onto the `combat.drain.slain` event for you to show. osrlib
1255
+ doesn't act on it. When the character survives, the function doesn't copy it, because
1256
+ the character hasn't become anything. `None` leaves it off.
1254
1257
 
1255
1258
  Returns:
1256
1259
  What the character lost, and the events that describe it.
@@ -1290,6 +1293,9 @@ def drain_levels(
1290
1293
  result = drain_levels(character, fighter, levels=1, xp_policy="halfway", stream=advancement)
1291
1294
  print(result.levels_lost, result.new_level, result.hp_lost, result.slain)
1292
1295
  # 1 1 9 False
1296
+
1297
+ # The lost level's d8 came up 8, and Rurik's CON modifier adds 1, so the drain takes 9
1298
+ # hit points: maximum hit points fall from 10 to 1.
1293
1299
  print(character.level, character.xp, character.max_hp)
1294
1300
  # 1 1000 1
1295
1301
  ```
@@ -1301,6 +1307,7 @@ def drain_levels(
1301
1307
  if xp_policy not in ("halfway", "level_minimum"):
1302
1308
  raise ValueError(f"unknown xp policy {xp_policy!r}")
1303
1309
  former_level = character.level
1310
+ former_max_hp = character.max_hp
1304
1311
  hp_rolls: list[int] = []
1305
1312
  hp_lost = 0
1306
1313
  slain = False
@@ -1334,7 +1341,7 @@ def drain_levels(
1334
1341
  target_id=character.id or character.name,
1335
1342
  levels_lost=levels_lost,
1336
1343
  new_level=0,
1337
- hp_lost=hp_lost,
1344
+ hp_lost=former_max_hp - character.max_hp,
1338
1345
  spawn_consequence=spawn_consequence,
1339
1346
  )
1340
1347
  )
@@ -1360,9 +1367,8 @@ def drain_levels(
1360
1367
  target_id=character.id or character.name,
1361
1368
  levels_lost=former_level - character.level,
1362
1369
  new_level=character.level,
1363
- hp_lost=hp_lost,
1370
+ hp_lost=former_max_hp - character.max_hp,
1364
1371
  xp_after=xp_after,
1365
- spawn_consequence=spawn_consequence,
1366
1372
  )
1367
1373
  )
1368
1374
  events.append(