# `Cauldron2D.Sectors.Move`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/cauldron_2d/lib/cauldron_2d/sectors/move.ex#L1)

Moving a body through a sector map: whether it fits where it is going, what floor and
ceiling it finds there, and sliding along a wall it runs into.

    case Move.try(world, body, {x + dx, y + dy}, step: 24.0) do
      {:ok, moved} -> moved.x
      {:blocked, why} -> why.reason
    end

    {moved, momentum} = Move.slide(world, body, {dx, dy}, step: 24.0)

`world` is `%{map: map, blockmap: blockmap, sectors: sectors}` — the sectors as they are
now, since floors and ceilings move. A body is `%{x:, y:, z:, radius:, height:}` with
`z` its feet; its box, `radius` each way from its centre, is what collides.

A body fits at a point when no line it may not pass crosses its box, no body it may not
pass overlaps it, the opening there is as tall as it is, its head is below the lowest
ceiling, the floor is no more than `:step` above its feet, and — unless it may drop —
no more than `:step` above the lowest floor under its box.

## Options

  * `:step` — how far up a body steps without climbing. Default `24.0`
  * `:dropoff` — whether a body may walk off a ledge. Default `true`
  * `:line_blocks?` — `fn line_index -> boolean` for two-sided lines that stop this body;
    a one-sided line always does. Default: lines flagged `:blocks`
  * `:body_blocks?` — `fn {id, point, radius} -> boolean` for overlapping bodies that
    stop it. Default: every body
  * `:except` — the moving body's own id in the blockmap. Default `nil`

# `body`

```elixir
@type body() :: %{
  x: number(),
  y: number(),
  z: number(),
  radius: number(),
  height: number()
}
```

# `place`

```elixir
@type place() :: %{
  sector: non_neg_integer(),
  floor: float(),
  ceiling: float(),
  dropoff: float(),
  blocked: nil | {:line, non_neg_integer()} | {:body, term()},
  lines: [non_neg_integer()],
  bodies: [Cauldron2D.Sectors.Blockmap.body()]
}
```

# `world`

```elixir
@type world() :: %{
  map: Cauldron2D.Sectors.Map.t(),
  blockmap: Cauldron2D.Sectors.Blockmap.t(),
  sectors: tuple()
}
```

# `check`

```elixir
@spec check(world(), body(), {number(), number()}, keyword()) :: place()
```

What a body would find at `point`, without moving it. See `t:place/0`.

# `crosses_box?`

```elixir
@spec crosses_box?(
  Cauldron2D.Sectors.Map.t(),
  non_neg_integer(),
  {number(), number()},
  number()
) ::
  boolean()
```

Whether line `index` crosses the box of `radius` round `point`.

# `opening`

```elixir
@spec opening(world(), non_neg_integer()) ::
  %{top: float(), bottom: float(), low: float(), range: float()} | nil
```

The gap through two-sided line `index`: `%{top:, bottom:, low:, range:}` — the lower
ceiling, the higher floor, the lower floor and the height between — or `nil` for a
one-sided line.

# `slide`

```elixir
@spec slide(world(), body(), {number(), number()}, keyword()) ::
  {map(), {float(), float()}}
```

Move the body by `momentum`, sliding along whatever wall stops it, as Doom's player
does. Gives `{moved, momentum}`: `moved` as `try/4`'s success, or the body where it was
with `crossed: []`, and the momentum left after sliding.

# `try`

```elixir
@spec try(world(), body(), {number(), number()}, keyword()) ::
  {:ok, map()} | {:blocked, map()}
```

Move the body to `point` if it fits there: `{:ok, %{x:, y:, floor:, ceiling:, dropoff:,
sector:, crossed:, lines:, bodies:}}`, where `crossed` lists the lines whose side the
body's centre changed, `lines` the two-sided lines its box touches and `bodies` the bodies
it overlaps without being stopped, or
`{:blocked, %{reason:, place:}}` with `reason` one of `:line`, `:body`, `:tight`,
`:ceiling`, `:step` and `:dropoff`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
