Sounds and game files

Resources can play the game's sounds, play their own .ogg files as speech, and add or replace game files such as models, textures, sounds and animations.

Playing the game's sounds

kcds.world.Audio is client-only and plays sounds on that one machine. For a sound everyone should hear, have the server tell all clients to play it:

// server
kcds.core.Events.broadcast('bells:ring', { x: 1998, y: 2498.75, z: 116 });
// client
kcds.core.Events.on('bells:ring', (spot) => {
  kcds.world.Audio.playAt('a_o_bell_kkut_kostelni', spot);
});
CallWhat it does
play(trigger)plays at the local player
playAt(trigger, position)plays at a point
playFrom(trigger, entityId)plays on an entity and follows it
stopFrom(trigger, entityId)stops that sound
setSoundParameter(entityId, name, value)sets one of the game's audio parameters
setSoundSwitch(entityId, name, value)sets one of the game's audio switches
triggerExists(trigger)true when the game's audio data has this name
// client
const { Audio } = kcds.world;

Audio.play('v_horse_whistle');
Audio.setSoundParameter(horseId, 'horse_speed', 0.5);
Audio.setSoundSwitch(playerId, 'sword_mat', 'mat_metal');

Each call returns whether the game accepted it. You get false before a world has loaded, for a name the game does not have, and for an entity that is not there. Names can be up to 256 bytes, and positions must be within 16 km of the map's origin.

Speech from a file

Audio.playFile(file, entityId, purpose?) plays an .ogg on an entity like a spoken line. It comes from that position, fades with distance and follows the player's voice volume.

// client: your file is stream/sounds/kcds/guards/halt.ogg
kcds.world.Audio.playFile('sounds/kcds/guards/halt', guardId, '20');

file is a game path, with or without .ogg. It can be a file you ship, where stream/sounds/kcds/<resource>/line.ogg becomes sounds/kcds/<resource>/line, or one of the game's lines under Localization/dialog/.... A path with a backslash, a drive, a leading / or .. throws. A file the game cannot find plays nothing.

purpose sets how it sounds:

ValueMeaning
inneran inner voice
10, 20, 35, 50, 100, 200heard up to that many metres; default is 50
sfp_dialog, sfp_cutscene, sfp_musicthe game's own sound categories

One file per entity can start every 0.1 s.

Voice chat

KCDS has no voice chat yet, so players talk over Discord or a similar program. It is planned as its own milestone.

Adding game files: stream/

Your resource's stream/ folder holds new files at their game paths, in a folder named after your resource: stream/<top folder>/kcds/<resource>/.... Each player running the resource receives them, and the game loads them by path like its own.

resources/
  furniture/
    package.json
    server.js
    stream/
      objects/kcds/furniture/chair.cgf
      objects/kcds/furniture/chair.cgfm
      sounds/kcds/furniture/creak.ogg
// server: spawn the chair by its full path
kcds.world.Prop.spawn('objects/kcds/furniture/chair.cgf', new kcds.math.Vector3(1650, 2150, 37));
// client: check whether this game has it yet
if (kcds.core.Assets.has('objects/kcds/furniture/chair.cgf')) {
  console.log('the chair is here');
}
  • stream/ and replace/ always reach clients, whatever files says.
  • A file outside <top folder>/kcds/<resource>/ is refused, so a streamed file cannot take the place of a game file or another resource's file.
  • To play a .caf animation you ship, use player.playAnimation('', { clip }); see Animations.

Replacing game files: replace/

A file at replace/<game path> is used in place of the game's file at that path, for every client running the resource:

replace/objects/manmade/task_specific_props/household/cooking_eating/eating_tools/wooden_scoop.cgf

Keep in mind:

  • A model the game loaded before your resource started stays as it was until the game loads it again.
  • kcds.core.Assets.has does not count replaced files.
  • Every player who joins downloads these files, so replace only what your server needs.

Reference: Audio, Assets.