Shooting contests and races

The game's archery contest table, bottom left: the player's and up to two opponents' names, the arrows each has left, their points, and a target picture that marks where an arrow struck. The server keeps the score and sends it to the player's game, whose client script shows it:

// server
kcds.core.Events.fromClient('archery:join', (player) => {
  player.emit('archery:start', { rival: 'Jindra', arrows: 5 });
});
// client
const { Hud } = kcds.ui;

kcds.core.Events.on('archery:start', (contest) => {
  Hud.showShootingContest({
    player: { name: 'You', arrows: contest.arrows },
    opponents: [{ name: contest.rival, arrows: contest.arrows }],
  });
});
kcds.core.Events.on('archery:shot', (shot) => {
  Hud.showShootingHit(shot.x, shot.y);
  Hud.updateShootingContest({
    player: { arrows: shot.arrows, points: shot.points },
    opponents: [{ arrows: shot.rivalArrows, points: shot.rivalPoints }],
  });
});
kcds.core.Events.on('archery:end', () => Hud.hideShootingContest());

showShootingHit(x, y) takes the spot on the target from -1 to 1 across and up. Horse races have their own table: showHorseContest({ player, opponent, track }), updateHorseContest(score) and hideHorseContest().

Reference: Hud.