parseShot = (shot, systems, { programmingLanguage }, speakerToThangTypeSlugMap = {}) => {
  const setupCommands = parseSetup(shot, systems) || []
  for (const speaker of ['left', 'right', 'center']) {
    const newThangType = shot.shotSetup?.[speaker + 'ThangType']?.thangType
    if (newThangType) {
      speakerToThangTypeSlugMap[speaker] = newThangType.type?.slug || newThangType.type
    }
  }
  const dialogNodes = (shot.dialogNodes || [])
    .filter(node => getLanguageFilter(node) === undefined || getLanguageFilter(node) === programmingLanguage)
    .map(node => parseDialogNode({ dialogNode: node, systems, shot, speakerToThangTypeSlugMap }))
    .filter(dialogCommands => dialogCommands.length > 0)

  // If we have both dialogNodes and some setupCommands we want to
  // have the setup occur just before the first dialogNode.
  if (dialogNodes.length > 0 && setupCommands.length > 0) {
    return [[...setupCommands, new SyncFunction(() => {
      if (systems.autoplay) {
        systems.autoplay.autoplay = true
      } else {
        console.error('No cinematic autoplay module')
      }
    })], ...dialogNodes]
  }
  if (dialogNodes.length === 0) {
    return [setupCommands]
  }
  return dialogNodes
}
Parses a shot and dialog nodes.

Returns a 2d array of command nodes. Each inner array is passed into the
CommandRunner and run until user interuption or conclusion. Then between the
inner arrays the cinematicController waits before running the next command batch.

@param {Shot} shot The shot that is being run.
@param {Object} systems The systems.
@returns {AbstractCommand[][]} A 2d array of commands.