The comms system
Send messages to player consoles and manage the comms-tree route system.
Overview
The comms module provides two related capabilities:
Broadcast messages — comms_broadcast sends a text message to the comms panel of a ship (or all ships). Messages appear as incoming transmissions on the comms console. Use comms_broadcast for NPC dialogue, status updates, and mission narration that the crew hears.
Comms routes — the //comms/<path> route system defines the interactive comms tree that players navigate using the comms console. Use comms_route_to from Python/MAST to programmatically navigate the tree, and comms_select to set which NPC a player is talking to.
Quick example
//comms/alien
+ "Greetings, humans." //comms/alien/greet
//comms/alien/greet
+ "We come in peace.":
signal_emit("alliance_offered")
+ "Stand down!":
target(alien_id, player_ship_id)
== patrol ==
comms_broadcast(ship_id, "Enemy spotted at grid 7-Alpha!", "red")
->END
from sbs_utils.procedural.comms import comms_broadcast, comms_route_to
comms_broadcast(ship_id, "Incoming transmission!", "yellow")
comms_route_to(client_id, "alien/greet")
API
comms(path=None, buttons=None, timeout=None)
Suspend the current task and present comms buttons, waiting for a choice.
Must be called from a server task (client_id == 0). The task resumes
when a comms button is pressed or timeout resolves. The
//comms/path route hierarchy controls which buttons appear.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | None
|
Initial comms path. Defaults to
|
None
|
buttons
|
dict | None
|
Inline button definitions as
|
None
|
timeout
|
Promise | None
|
A promise that ends the comms
interaction when it resolves (e.g. from |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
CommsPromise |
CommsPromise
|
Resolves when a button is selected or timeout fires. |
Example
await comms() await comms(timeout=delay_promise(seconds=30))
comms_add_button(message, label=None, color=None, data=None, path=None)
Add a button to the currently active comms panel at runtime.
Injects a sticky button into the comms button list of whichever
ButtonPromise is currently navigating. Call this from inside a
//comms/ route to add dynamic buttons beyond those defined statically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
Button label text. |
required |
label
|
label | None
|
MAST label to run when pressed. Defaults to None. |
None
|
color
|
str | None
|
Button text color. Defaults to None (inherits the comms default). |
None
|
data
|
dict | None
|
Variables passed to the button handler. Defaults to None. |
None
|
path
|
str | None
|
Comms sub-path to restrict the button to. Defaults to None (shown at the current path). |
None
|
Example
//comms/patrol comms_add_button("Retreat", retreat_label, color="yellow")
comms_broadcast(ids_or_obj, msg, color=None, category=None, severity=None)
Send a text message to the text waterfall of one or more targets.
Accepts player ship IDs or client/console IDs. Ship IDs use
send_message_to_player_ship; client IDs use
send_message_to_client.
ALSO appends to the ship's log (procedural.log_panel), which is the waterfall's
replacement - see mkdocs build/messages.md. Both surfaces are written during the changeover
so they can be compared side by side; retiring the waterfall is then deleting the
engine half of this function.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids_or_obj
|
Agent ID, client ID, or set/list of either to send to.
Pass |
required | |
msg
|
str
|
The message text. Supports |
required |
color
|
str
|
Text color as a name or hex string, e.g.
|
None
|
category
|
str
|
Which log TAB this belongs in - |
None
|
severity
|
str
|
|
None
|
Example
comms_broadcast(SHIP_ID, "Red alert!", color="red", severity="danger")
comms_grid_buttons(origin_id, selected_id)
The buttons the grid menu is currently offering, as row dicts.
Returns:
| Type | Description |
|---|---|
|
list[dict]: |
|
|
the menu offers them. Empty when no interaction is open for this pair. |
index is the position in the UNFILTERED button list, not the row number. A
button hidden by its if or already used (*) is skipped here but still consumes an
index - set_buttons enumerates before it filters, and the press path looks the
button up by that index. Pass index back to :func:comms_grid_press and never the
row's position.
The conditions and the label are evaluated on the PROMISE's task, not the caller's:
COMMS_SELECTED_ID and the rest live there.
comms_grid_press(origin_id, selected_id, index, client_id=None)
Press a grid button by its index from :func:comms_grid_buttons.
Takes the same path the engine widget's press takes - the event
cosmos_event_handler builds for press_grid_button, through
ConsoleDispatcher.dispatch_message. That is what keeps one-shot buttons marked
used and the menu redrawn afterwards; pressing the promise's button directly skips
both.
Returns:
| Name | Type | Description |
|---|---|---|
bool |
True if a press was dispatched. False when no interaction is open or the |
|
|
index names nothing - a stale row after the list moved, which is ordinary. |
comms_grid_revision(origin_id, selected_id)
What an on change watches to know the drawn list would differ.
Cheap - it re-runs the conditions but never the routes, so it is safe per tick. It
does NOT see a button that appeared because a route's structure changed; nothing in
comms rebuilds on a tick, so that still needs a press, a navigate, or
:func:comms_refresh_open.
comms_history_add(player_id, other_id, entry)
Record one exchange between a player ship and a contact.
comms_history_clear()
Per-mission state: last mission's conversations are not this one's.
comms_history_for(player_id, other_id, limit=None)
Exchanges between the two, oldest first. limit takes the most RECENT n.
comms_history_size()
Probe for the reset ledger.
comms_info(name, face=None, color=None)
Update the comms selection info panel with a name and portrait.
Sets the name and face shown in the comms console's selection panel for
the current origin/selected ship pair. Use this from a //comms/ route
to customise what the player sees before pressing buttons.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Display name to show in the info panel. |
required |
face
|
str
|
Face asset string for the portrait. Defaults to the face registered for the selected object. |
None
|
color
|
str
|
Text color. Defaults to |
None
|
Example
comms_info("Commander Karn", face="crew/karn", color="red")
comms_info_card(client_id, message=None, title=None, color=None, face=None, icon_index=None, banner=None, button=None, time=10, history=True, path=None, notify=None)
Send an "incoming comms" card to one or more clients' info panel.
A reusable wrapper over gui_info_panel_send_message for narrative / ambient
comms that should read as a hail - a speaker name + color (and optional
face/icon/banner), kept in the panel's history and auto-dismissed - instead of
an ephemeral text-waterfall line. Use this for chatter, hails, and quest
hand-offs; keep comms_broadcast for pure mechanical status text.
The color is applied to both the title and the body. If button is
given, the call returns an awaitable Promise that resolves when a player
presses it (so the card can ask for a decision).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client_id
|
int | set
|
Client/console id(s) to receive the card. Commonly
|
required |
message
|
str
|
Card body text. |
None
|
title
|
str
|
Header line - typically the speaker / clan name. |
None
|
color
|
str
|
Color for the title and body (name or hex). |
None
|
face
|
str
|
Face/portrait string to show alongside the message. |
None
|
icon_index
|
int
|
Icon index to show alongside the message. |
None
|
banner
|
str
|
Larger banner text above the title. |
None
|
button
|
str | list
|
Button label(s); when set the call returns an awaitable Promise that resolves on press. |
None
|
time
|
int
|
Auto-dismiss after this many seconds (when there is no button). Defaults to 10. |
10
|
history
|
bool
|
Keep the card in the panel log. Defaults to True. |
True
|
path
|
str
|
Info-panel tab path. Defaults to |
None
|
notify
|
bool
|
Interrupt - show the card live and switch the
panel to its tab. Defaults to None ("only if it has a button"), so a
plain card is filed in the log and the attention half is left to an
overlay (see |
None
|
Returns:
| Type | Description |
|---|---|
|
Promise | None: Resolves on button press, or None if no button was given. |
Example
comms_info_card(all_roles("console, comms"), "You're a long way from friends, captain.", title="Ashfang Raiders", color="#ee3333")
comms_info_clear(client_id, path=None)
Clear a client's info-panel comms tab (no message) and fall back to the ship-data tab. Mirrors the HereThereBeMonsters clear-comms idiom.
comms_info_face_override(face=None)
Override the face portrait shown in the comms panel for this interaction.
Sets a one-time face override on the current ButtonPromise. The
override applies until the player selects a different comms target or the
interaction ends.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
face
|
str | None
|
Face asset string to show, or |
None
|
Example
comms_info_face_override("crew/commander")
comms_map_filter_clear(ship_id)
Remove the comms map filter from this ship: the map shows everything again.
comms_map_filter_get(ship_id)
The ids this ship's comms map is filtered to, or [] when it shows everything.
comms_map_filter_set(ship_id, ids)
Show only ids - and the ship itself - on this ship's comms map.
Only ids the engine knows are written - live space objects. A lifeform, grid object, fleet or dead id is dropped. The ship's own id is always in the list, so the crew never loses their own ship from the map, and a lens that matches nothing still writes a non-empty list (empty would mean "no filter"). Skips the write when the list has not changed since the last one, since every write goes over the network.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ship_id
|
Agent | int
|
The player ship whose comms map is filtered. |
required |
ids
|
iterable
|
Agent ids or objects to show. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
list |
The ids shown (the ship included), sorted. |
comms_message(msg, from_ids_or_obj, to_ids_or_obj, title=None, face=None, color=None, title_color=None, is_receive=True, from_name=None)
Send a comms message with explicit sender and receiver control.
Lower-level function used by comms_transmit and comms_receive.
Handles lifeforms, side colors, CommsOverride, and emits the
comms_message signal. Prefer comms_transmit or comms_receive
unless you need direct sender/receiver control.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
The message body text. Supports |
required |
from_ids_or_obj
|
Sender agent ID(s) or object(s). |
required | |
to_ids_or_obj
|
Receiver agent ID(s) or object(s). Pass |
required | |
title
|
str
|
Header text for the message. Defaults to EMPTY - the sender's name is a field of its own now, so the title carries only what the script wrote. It used to default to the sender's comms ID, and a title given alongside it was packed on behind it as "Lt Rios (TSN): Orders". |
None
|
face
|
str
|
Face asset string for the sender portrait. Defaults to the face registered for the sender. |
None
|
color
|
str
|
Body text color. Defaults to |
None
|
title_color
|
str
|
Title text color. Defaults to the sender's side color. |
None
|
is_receive
|
bool
|
|
True
|
from_name
|
str
|
Override the display name of the sender.
Defaults to None (uses the sender object's |
None
|
Note
When BOTH ends are player ships a transmit reaches both bridges: the sender gets the outgoing copy and the receiving crew gets the matching incoming one, each named for the other ship. Send it once - a second call with the ids swapped now duplicates it.
Note
The console threads messages by CONTACT - the other party in the conversation. For a lifeform that is the LIFEFORM's id, not its host ship's, so two crew aboard one hull are two conversations rather than one, and a lifeform hailing another lifeform arrives on the far bridge named for the person who sent it. The title names the same contact the thread is filed under, in both directions.
Example
comms_message("Incoming!", ENEMY_ID, SHIP_ID, title="Commander")
comms_navigate(path, face=None, comms_badge=None)
Navigate the current comms interaction to a different button path.
Changes which //comms/ route sub-path is active, updating the buttons
shown to the player. Call this from inside a comms button handler to
implement multi-level comms menus.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Target comms sub-path, e.g. |
required |
face
|
str | None
|
Face override to apply when navigating. Defaults to None (keep current face). |
None
|
comms_badge
|
object | None
|
Lifeform or ID to associate as the comms badge on the new path. Defaults to None. |
None
|
Example
//comms + "Talk to Commander" comms_navigate("commander") //comms/commander + "Order attack" comms_receive("Attack formation!", title="Commander")
comms_navigate_override(ids_or_obj, sel_ids_or_obj, path=None, path_must_match=True)
Navigate a comms interaction from outside the comms task.
Refreshes the buttons shown for the specified origin/selected pair. Use this when the story needs to update comms buttons from a non-comms task (e.g. a timer or event handler). If the pair is currently selected, the new buttons appear immediately.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids_or_obj
|
Player ship ID(s) or object(s) (origin side). |
required | |
sel_ids_or_obj
|
Target ID(s) or object(s) (selected side). |
required | |
path
|
str | None
|
Comms sub-path to navigate to. Defaults to None (reuses the current path of the active interaction). |
None
|
path_must_match
|
bool
|
Only navigate if the active path
already matches |
True
|
Example
comms_navigate_override(SHIP_ID, ENEMY_ID, "commander/angry")
comms_override(origin_id=None, selected_id=None, face=None, from_name=None)
Create a context manager to override comms sender/receiver fields.
Use as a with block to temporarily redirect comms calls
(comms_transmit, comms_receive, etc.) to specific IDs or a fixed
face/name without changing the underlying event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
origin_id
|
int | None
|
Override the origin (player ship)
ID. Accepts any form accepted by |
None
|
selected_id
|
int | None
|
Override the selected (target) ID. Defaults to None. |
None
|
face
|
str | None
|
Override the face asset string. Defaults to None. |
None
|
from_name
|
str | None
|
Override the sender display name. Defaults to None. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
CommsOverride |
A context manager; use with |
Example
with comms_override(origin_id=SHIP_ID, selected_id=ENEMY_ID): comms_receive("Surrender or be destroyed.", title="Klingon")
comms_receive(msg, title=None, face=None, color=None, title_color=None)
Receive a comms message on a player ship from the selected target.
Reads origin and selected IDs from the current event context (or
COMMS_ORIGIN_ID/COMMS_SELECTED_ID task variables). Sends the
message with a < < prefix indicating an incoming transmission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
The message body text. Supports |
required |
title
|
str
|
Header text for the message. Defaults to EMPTY - the sender's name is a field of its own now, so the title carries only what the script wrote. It used to default to the sender's comms ID, and a title given alongside it was packed on behind it as "Lt Rios (TSN): Orders". |
None
|
face
|
str
|
Face asset string for the portrait. Defaults to the face registered for the sender. |
None
|
color
|
str
|
Body text color. Defaults to |
None
|
title_color
|
str
|
Title text color. Defaults to the sender's side color. |
None
|
Example
comms_receive("Docking clearance granted.", title="Station")
comms_receive_internal(msg, ids_or_obj=None, from_name=None, title=None, face=None, color=None, title_color=None)
Receive an internal crew comms message (ship talking to itself).
Sends a message where both sender and receiver are the same ship, used for
incoming internal crew messages (e.g. engineering to bridge). The ship is
read from the event context or from ids_or_obj.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
The message body text. Supports |
required |
ids_or_obj
|
Agent ID(s) or object(s) of the receiving ship. Defaults to the origin ship from the current event context. |
None
|
|
from_name
|
str
|
Name of the internal sender (e.g.
|
None
|
title
|
str
|
Title bar text. Defaults to None. |
None
|
face
|
str
|
Face asset string for the portrait. Defaults to
the face registered for |
None
|
color
|
str
|
Body text color. Defaults to |
None
|
title_color
|
str
|
Title text color. Defaults to None. |
None
|
Example
comms_receive_internal("Power restored.", from_name="Engineering")
comms_refresh_open(ids_or_obj=None)
Re-run the comms routes for every OPEN comms menu, so buttons whose conditions changed appear or disappear now instead of on the next selection.
For state that changes outside any particular pair - a story flag that shows or hides
a button everywhere - where :func:comms_navigate_override would need every
origin x selected pair spelled out. Stays on the current path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids_or_obj
|
only menus opened by these origins (player ships). None = all. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
int |
int
|
how many open menus were refreshed. |
comms_selection_annotator(fn)
Add something to the comms selection title.
fn(origin_id, selected_id, title) -> title. The comms panel already shows who you
have selected; this lets an addon say something about them that the crew would
otherwise have to hail to discover - "DS 1 - 2 jobs", say.
Two rules the annotator must obey, both the engine's:
- ASCII, and no
:or;. A selection title is a style-property string to the engine, so those two characters are PARSED rather than drawn - the same reasonhail_answer_labelrefuses them. - Keep it short, and truncate your own suffix rather than the name. The name is how the crew know who they clicked on.
Pass None to remove it.
comms_selection_annotator_clear()
Drop the annotator (called by reset_mission_state).
comms_set_2dview_focus(client_id, focus_id=0, EVENT=None)
Set the 2D radar view to follow an alternate ship for a comms client.
Stores focus_id as the alternate ship to track on the 2D radar for
both the client and its assigned ship. The view only actually updates if
the 2d_follow inventory flag is set on the client.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client_id
|
int
|
The client whose radar should be updated. |
required |
focus_id
|
int
|
ID of the ship to track, or |
0
|
EVENT
|
Unused; present for route-handler compatibility. |
None
|
Example
comms_set_2dview_focus(CLIENT_ID, ENEMY_ID)
comms_speech_bubble(msg, seconds=3, color=None, client_id=None, selected_id=None)
Display a speech bubble attached to the currently selected space object.
Attaches a timed text bubble to the selected object on the client's 2D
radar. The client and selected object are read from the current event
context — client_id and selected_id parameters are accepted but
currently overridden by the event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
Text to display in the speech bubble. |
required |
seconds
|
float
|
Duration the bubble is shown. Pass |
3
|
color
|
str
|
Text color as a name or hex string. Defaults
to |
None
|
client_id
|
int | None
|
Currently unused; read from event. |
None
|
selected_id
|
int | None
|
Currently unused; read from event. |
None
|
Example
comms_speech_bubble("Curse you, Terran!", seconds=5)
comms_story_buttons(ids, sel_ids, buttons, path, nav_button=None)
Inject story-controlled buttons into active comms interactions and wait for a choice.
Attaches a CommsChoiceButtonPromise to every active comms task that
matches the given origin/selected pairs. The buttons appear at path
and disappear when one is pressed. An optional nav_button adds a
navigation button at the parent path to enter this sub-menu.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ids
|
Player ship ID(s) or object(s) (origin side). |
required | |
sel_ids
|
Target ID(s) or object(s) (selected side). |
required | |
buttons
|
list[str]
|
Button label strings to present. |
required |
path
|
str
|
Comms path where the buttons appear, e.g.
|
required |
nav_button
|
str | None
|
Label for a navigation button
shown at the parent path that leads to |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
CommsChoiceButtonPromise |
CommsChoiceButtonPromise
|
Resolves with the pressed button label. |
Example
result = await comms_story_buttons( SHIP_ID, ENEMY_ID, ["Accept surrender", "Reject"], "comms/surrender", nav_button="Discuss surrender" )
comms_transmit(msg, title=None, face=None, color=None, title_color=None)
Transmit a comms message from the player ship to the selected target.
Reads origin and selected IDs from the current event context (or
COMMS_ORIGIN_ID/COMMS_SELECTED_ID task variables). Sends the
message with a > > prefix indicating an outgoing transmission.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
The message body text. Supports |
required |
title
|
str
|
Header text for the message. Defaults to EMPTY - the sender's name is a field of its own now, so the title carries only what the script wrote. It used to default to the sender's comms ID, and a title given alongside it was packed on behind it as "Lt Rios (TSN): Orders". |
None
|
face
|
str
|
Face asset string for the portrait. Defaults to the face registered for the sender. |
None
|
color
|
str
|
Body text color. Defaults to |
None
|
title_color
|
str
|
Title text color. Defaults to the sender's side color. |
None
|
Note
When the selected target is another PLAYER ship this delivers both
halves - the > > copy on the sending bridge and the < < copy on
the receiving one. One call is the whole exchange.
Example
comms_transmit("Requesting docking clearance.", title="Artemis")
comms_transmit_internal(msg, ids_or_obj=None, to_name=None, title=None, face=None, color=None, title_color=None)
Transmit an internal crew comms message (ship talking to itself).
Sends a message where both sender and receiver are the same ship, used for internal crew communications (e.g. bridge to engineering). The origin ship is read from the current event context.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
msg
|
str
|
The message body text. Supports |
required |
ids_or_obj
|
Unused — origin ship is always read from the event. |
None
|
|
to_name
|
str
|
Name of the internal recipient (e.g.
|
None
|
title
|
str
|
Title bar text. Defaults to None. |
None
|
face
|
str
|
Face asset string for the portrait. Defaults to
the face registered for |
None
|
color
|
str
|
Body text color. Defaults to |
None
|
title_color
|
str
|
Title text color. Defaults to None. |
None
|
Example
comms_transmit_internal("Shields holding.", to_name="Engineering")