osrlib.crawl.party
The crawl party: marching order, group movement, and combat ranks.
You build a Party out of the
Characters your game rolled up, and you hand it to
GameSession.new beside the adventure. From then on the
session owns it: the party moves as one body, and session.party is where you read it back.
The member list order is marching order. There is no second field to keep in step with it, so the
first member is the one in front. ReorderParty is the only
command that rewrites that order, and reorder is what it calls.
Dead members stay in the list. Their gear is still on them, so removing them would lose it, and
deciding a corpse is left behind is a game's call to make with the referee commands rather than
something the rules do for you. Nothing that counts bodies counts a dead one: movement rate, combat
ranks, ability checks, and provisions all read
living_members.
Party
Bases: BaseModel
The adventuring party, in marching order.
Construct one with at least one member and pass it to
GameSession.new, which assigns each member an entity id
and keeps the party for the life of the session. The methods here answer what the crawl procedures
need to know about the group as a whole: who is still standing, how fast the group walks, and who
stands where in a fight.
A party is mutable and validates on assignment, so the session can heal, wound, and re-equip its
members in place. The list is never empty: a party whose last member dies ends the session in
game_over rather than emptying out.
Attributes:
| Name | Type | Description |
|---|---|---|
members |
list[Character]
|
The characters, front of the line first. |
Examples:
from osrlib.core.abilities import AbilityScore
from osrlib.core.alignment import Alignment
from osrlib.core.character import Character
from osrlib.crawl.party import Party
rolled = {
"name": "Hild",
"class_id": "fighter",
"race": "human",
"level": 1,
"xp": 0,
"scores": {ability: 12 for ability in AbilityScore},
"alignment": Alignment.LAWFUL,
"max_hp": 8,
"current_hp": 8,
}
party = Party(members=[Character(**rolled), Character(**{**rolled, "name": "Osric"})])
print([member.name for member in party.living_members()])
# ['Hild', 'Osric']
members
class-attribute
instance-attribute
The party's characters, in marching order: index 0 walks in front and meets what the party
walks into first. At least one member is required. Ids are assigned by
GameSession.new when the party joins a session, so a
party you just built has None in every Character.id until then. Change the order with the
ReorderParty command rather than by assigning here, so
the change is logged and replays.
living_members
Return the living members, in marching order.
A member is living until something gives them the dead condition. This is the list every
group rule works from, so a fallen member stops counting toward movement, ranks, and checks
the moment they drop, without leaving the party.
Returns:
| Type | Description |
|---|---|
list[Character]
|
The members without the |
member
Return the member with character_id.
Use this to turn an id out of a command or an event back into the character it names. Ids
come from GameSession.new, which stamps each member
as character-NNNN in party order, and events carry them rather than names.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
character_id
|
str
|
The member's entity id. |
required |
Returns:
| Type | Description |
|---|---|
Character
|
The character. Dead members answer here too, because their gear and their record are still the party's. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no member has that id. The message names the id. |
movement_rate
Return the party's exploration rate: the slowest living member's.
B/X moves a group at the pace of its slowest member, so one overloaded character slows
everybody. Call Character.movement_rate
for one character's own allowance, and
exploration_rate for the rate the running
session charges the party, which computes the same minimum and halves a member's rate first
when hunger or thirst has caught up with them under the deprivation_penalties ruleset flag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ruleset
|
Ruleset
|
The ruleset whose encumbrance mode governs. Encumbrance is what turns carried weight into a rate, and the modes differ in what they weigh. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The rate in feet per exploration turn: 120, 90, 60, 30, or 0. A rate of 0 means the party cannot move at all, either because its slowest living member is overloaded or because nobody is alive to walk. |
ranks
Chunk the living members into combat ranks of width, in marching order.
A rank is one row of the formation: the first width living members stand in front and take
the melee, the rest queue behind them. Battle calls this for you with the width it measured
from the space the party is standing in, so you rarely pass your own. Call it yourself to
draw the formation, or to answer "who is in front" outside a fight.
Ranks are derived on every read rather than stored, so the fallen collapse forward on their own: when a front-rank member dies, the next living member is in front from the next read on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
width
|
int
|
How many characters stand abreast. Battle derives this from the party's frontage
(see |
required |
Returns:
| Type | Description |
|---|---|
list[list[Character]]
|
The ranks, front first. The last rank holds the remainder and can be shorter than
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
reorder
Rewrite the marching order in place.
This is what ReorderParty calls once it has accepted
the command. Issue that command through
GameSession.execute rather than calling this
yourself, so the change is logged and a replay reproduces it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
character_ids
|
Sequence[str]
|
Every member's id, in the new order. It has to be a permutation of the current membership: no id may be added, dropped, or repeated. Dead members are named here like anyone else, since they are still in the party. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the ids are not exactly the current membership, or if any member has no id yet (a party that has not joined a session). |