Skip to content

Mod Integration Channels

LoMce edited this page Jun 15, 2026 · 6 revisions

Channels: Private & Team Radio

A channel lets two (or more) players hear each other beyond proximity - team comms, faction radio, or a private one-to-one link - with a chosen audio shape (full-volume radio, muffled, or proximity-shaped).

← Back to Mod Integration


How it works

You return the channel a player belongs to this frame. Two players that resolve the same key hear each other. Keys are namespaced by your mod id internally, so they never collide with other mods or the built-in channels.

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
{
    // Everyone on the same team shares one key -> they hear each other.
    byte team = MyRoles.TeamOf(ctx.Player);
    return team != byte.MaxValue
        ? new VoiceChannelResult($"team:{team}")
        : null;   // not on a channel this frame
});

Return null when the player is not on any channel.


VoiceChannelResult

new VoiceChannelResult(
    Key,                         // string: same key on two players = they hear each other
    TwoWay = true,               // false = directional (see below)
    Shape  = VoiceAudioShape.Radio,
    Volume = 1f);

Audio shape

VoiceAudioShape Sound
Radio (default) Full volume, no falloff - classic team comms.
Muffle Heard, but low-pass filtered. Good for "muffled link" effects.
Proximity Proximity-shaped - still falls off with distance, but the pair are linked.

Volume (0–1) scales the channel audio.

Directional channels

TwoWay: true (default) - both players hear each other.

TwoWay: false - a one-directional link. The reverse direction is suppressed unless the listener also shares a two-way link. Use this for "this role hears its target, but the target does not hear back".


Patterns

Faction radio (all members hear each other, full volume):

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
    MyRoles.IsCultist(ctx.Player)
        ? new VoiceChannelResult("cult")
        : null);

Private pair (owner ↔ target, keyed by the owner so only that pair matches):

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
{
    byte ownerId = MyRoles.LinkOwnerOf(ctx.Player); // owner for both members of the pair
    return ownerId != byte.MaxValue
        ? new VoiceChannelResult($"link:{ownerId}")
        : null;
});

Muffled "swallowed" link at reduced volume:

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
    MyRoles.SwallowedBy(ctx.Player) is byte predator
        ? new VoiceChannelResult($"belly:{predator}", Shape: VoiceAudioShape.Muffle, Volume: 0.7f)
        : null);

One-way overhear (a spy hears a room's occupants, not vice versa):

PerfectCommsApi.RegisterVoiceChannel("com.me.mymod", ctx =>
    MyRoles.SpyChannelFor(ctx.Player) is string room
        ? new VoiceChannelResult(room, TwoWay: false)
        : null);

Notes

  • The channel route is evaluated after mutes - a player muted by a Gate stays muted even if they share a channel.
  • Channels apply in both meeting and task phases (mirroring the built-in team radio), before built-in radio resolves.
  • Like every primitive, channel resolution is local and fail-closed - a throwing callback yields null (no channel).

Next

Clone this wiki locally