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. Default24.0:dropoff— whether a body may walk off a ledge. Defaulttrue:line_blocks?—fn line_index -> booleanfor two-sided lines that stop this body; a one-sided line always does. Default: lines flagged:blocks:body_blocks?—fn {id, point, radius} -> booleanfor overlapping bodies that stop it. Default: every body:except— the moving body's own id in the blockmap. Defaultnil
Summary
Functions
What a body would find at point, without moving it. See place/0.
Whether line index crosses the box of radius round point.
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.
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.
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.
Types
@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()] }
@type world() :: %{ map: Cauldron2D.Sectors.Map.t(), blockmap: Cauldron2D.Sectors.Blockmap.t(), sectors: tuple() }
Functions
What a body would find at point, without moving it. See place/0.
@spec crosses_box?( Cauldron2D.Sectors.Map.t(), non_neg_integer(), {number(), number()}, number() ) :: boolean()
Whether line index crosses the box of radius round point.
@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.
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.
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.