# SharedSplit

> Talos next · Since 0.7

{@link ViewsHandle.shared}'s `split` options: when the shared camera splits into one view per target, and when it merges back.

Absent or invalid numbers keep the default.

## Signature

```ts
export interface SharedSplit {
  /**
   * How two targets split the window: `"auto"` (the default) is `"left-right"` in a window wider than
   * it is tall and `"top-bottom"` otherwise. Three targets always split as `"three"`, four as `"grid"`.
   */
  layout?: "auto" | "left-right" | "top-bottom"
  /**
   * Split once the targets' spread (the widest distance between two of them, metres) is beyond this.
   * Default: the spread at which the shared camera reaches `maxDistance` (at `maxFov`, if given).
   */
  beyond?: number
  /** Merge once the spread is back under this, metres. Default: three quarters of `beyond`; never above it. */
  back?: number
  /** Seconds of game time a split's or a merge's condition must hold, unbroken, before it happens. Default 0.5. */
  hold?: number
}
```

Import: `import type { SharedSplit } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `layout` | `layout?: "auto" \| "left-right" \| "top-bottom"` | How two targets split the window: `"auto"` (the default) is `"left-right"` in a window wider than it is tall and `"top-bottom"` otherwise. Three targets always split as `"three"`, four as `"grid"`. |
| `beyond` | `beyond?: number` | Split once the targets' spread (the widest distance between two of them, metres) is beyond this. Default: the spread at which the shared camera reaches `maxDistance` (at `maxFov`, if given). |
| `back` | `back?: number` | Merge once the spread is back under this, metres. Default: three quarters of `beyond`; never above it. |
| `hold` | `hold?: number` | Seconds of game time a split's or a merge's condition must hold, unbroken, before it happens. Default 0.5. |

## Example

The tested fixture `fixtures/recipes/game-over-restart/scenes/behaviors/game-over-restart.ts`.

```ts
import type { Behavior } from "@argus/api"

/**
 * A round that ends on a results screen, whose Retry button restarts the scene.
 *
 * Space scores until the clock runs out; then the results go up with the score written into them.
 *
 * Two rules decide the shape. `push`'s props are the *reason* a screen went up, not what it
 * draws — a screen is described once, at load — so the values it shows are written after the
 * push with `ctx.ui.set("<screen>:<id>", "text", …)`. And `ctx.session.restart()` rebuilds the
 * scene at the end of the tick: every behaviour's state, the HUD and the screen stack go back to
 * how the scene starts, so nothing here has to be reset by hand.
 */
interface State {
  score: number
  left: number
  over: boolean
}

interface Props {
  /** Length of a round in seconds. Default 5. */
  roundSeconds?: number
}

const RESULTS = "results"

export default {
  init(ctx) {
    if (typeof ctx.state.score !== "number") ctx.state.score = 0
    if (typeof ctx.state.left !== "number") ctx.state.left = ctx.props.roundSeconds ?? 5
    if (typeof ctx.state.over !== "boolean") ctx.state.over = false
    ctx.events.on("ui-activated", (event) => {
      if (event.data.screen === RESULTS && event.data.id === "retry" && ctx.state.over) ctx.session.restart()
    })
  },

  update(ctx, dt) {
    if (ctx.state.over) return
    if (ctx.input !== null && ctx.input.pressed("score")) ctx.state.score += 1
    ctx.state.left = Math.max(0, ctx.state.left - dt)
    ctx.hud.score(ctx.state.score)
    ctx.hud.timer(ctx.state.left)
    if (ctx.state.left > 0) return

    ctx.state.over = true
    ctx.ui.push(RESULTS, { reason: "round-over" })
    ctx.ui.set("results:score", "text", `${ctx.state.score}`)
    ctx.events.emit("round-over", { score: ctx.state.score })
  },
} satisfies Behavior<State, Props>
```
