Skip to content

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 "comms" (root).

None
buttons dict | None

Inline button definitions as {"button label": label_to_run, ...}. Buttons are sticky (equivalent to + buttons in MAST). Defaults to None.

None
timeout Promise | None

A promise that ends the comms interaction when it resolves (e.g. from delay_promise). Defaults to None (no timeout).

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 None to send to the event's parent_id.

required
msg str

The message text. Supports {var} interpolation.

required
color str

Text color as a name or hex string, e.g. "red" or "#3ff". Defaults to "#fff".

None
category str

Which log TAB this belongs in - "ship" or "mission". Omitted (the default) means it appears in the Log tab, which shows everything, and in no subset tab. That is what makes tagging incremental: nothing is lost by not being tagged.

None
severity str

"tip" / "warning" / "danger". Draws the entry as a callout. Reserved for things that matter - a box costs two rows, so one per line would halve how much log fits on screen.

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]: index, label, color and icon (or None), in the order

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 "white".

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 all_roles("console, comms") or a ship's linked comms consoles.

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 "message".

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 announce). Pass True to keep the old always-interrupt behaviour.

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 to clear any existing override and revert to the default.

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 {var} interpolation.

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 None to send the message to the sender (internal communication).

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 "#fff".

None
title_color str

Title text color. Defaults to the sender's side color.

None
is_receive bool

True = the player ship RECEIVED this (tagged recv); False = the player ship TRANSMITTED it (tagged send). Defaults to True.

True
from_name str

Override the display name of the sender. Defaults to None (uses the sender object's comms_id). Sent to the console as its own name field, beside the title.

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. "patrol" (expanded to //comms/patrol). Pass None or "" to reset to the root.

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 path, avoiding disorienting mid-menu jumps. Defaults to True.

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 to_set. Defaults to None.

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 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 {var} interpolation.

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 "#fff".

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 {var} interpolation.

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. "Engineering"). Used to look up a registered face via face_Engineering inventory key. Defaults to the ship's name.

None
title str

Title bar text. Defaults to None.

None
face str

Face asset string for the portrait. Defaults to the face registered for from_name.

None
color str

Body text color. Defaults to "#fff".

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 reason hail_answer_label refuses 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 to reset to the client's own ship. Defaults to 0.

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 0 for a permanent bubble. Defaults to 3.

3
color str

Text color as a name or hex string. Defaults to "#fff".

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. "comms/rescue".

required
nav_button str | None

Label for a navigation button shown at the parent path that leads to path. Defaults to None (no nav button).

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 {var} interpolation.

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 "#fff".

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 {var} interpolation.

required
ids_or_obj

Unused — origin ship is always read from the event.

None
to_name str

Name of the internal recipient (e.g. "Engineering"). Used to look up a registered face via face_Engineering inventory key. Defaults to the ship's name.

None
title str

Title bar text. Defaults to None.

None
face str

Face asset string for the portrait. Defaults to the face registered for to_name.

None
color str

Body text color. Defaults to "#fff".

None
title_color str

Title text color. Defaults to None.

None
Example

comms_transmit_internal("Shields holding.", to_name="Engineering")