ludic/packages/ludic.character/ports.ludic
2026-09-25 14:02:21 +03:00

118 lines
5.9 KiB
Text

# ports.ludic - what the body asks the game. Unbound, the world is a dry plain with no colliders,
# the boots manage 1.05 m a metre, the body is fresh and nothing holds or carries it.
export port CharacterGround {
height: fn(float, float) -> float # the world's own ground: pure, never the body's
# (x, z, r, from_y, step, base): the top of a collider within `step` above from_y, else base
top_at: fn(float, float, float, float, float, float) -> float = fn chr__top_plain
bed: fn(float, float) -> float = fn chr__bed_plain # the bed under water (unbound: the ground)
water: fn(float, float) -> float = fn chr__dry # the surface over a point
# (x, z, r, y0, y1): true when a collider takes a body of radius r there; pushed_x / _z say where to
push: fn(float, float, float, float, float) -> bool = fn chr__no_push
pushed_x: fn() -> float = fn chr__asked_x
pushed_z: fn() -> float = fn chr__asked_z
slope: fn(float, float) -> float = fn chr__slope_plain # 0 flat .. 1 a wall
slope_limit: fn() -> float = fn chr__boots # the rise a metre the boots take
current: fn(float, float, float, float) -> float = fn chr__still # (x, z, fx, fz): 0..1 of a stroke lost
}
export port CharacterBody {
pace: fn() -> float = fn chr__one # the walk and run, times (gear, a tired body)
sneaking: fn() -> bool = fn chr__no
breath: fn() -> float = fn chr__one # the lungs, times
riding: fn() -> bool = fn chr__no # something else carries the body
ride: fn(float, float, bool, float) -> void = fn chr__not_carried # (ix, iz, run, dt) while riding
holding: fn() -> bool = fn chr__no # at work on something: face it
hold_x: fn() -> float = fn chr__zero
hold_z: fn() -> float = fn chr__zero
eyes_ok: fn() -> bool = fn chr__yes # first person may be used now
}
# the stick, once a frame: move is in the camera's frame (+z ahead), look is radians this frame
export port CharacterInput {
move_x: fn() -> float = fn chr__zero
move_z: fn() -> float = fn chr__zero
run: fn() -> bool = fn chr__no
jump: fn() -> bool = fn chr__no
look_yaw: fn() -> float = fn chr__zero
look_pitch: fn() -> float = fn chr__zero
zoom: fn() -> float = fn chr__zero # wheel notches, in toward the body
look_always: fn() -> bool = fn chr__no # the look is held: never settle behind
}
export state CharacterState {
chr_out_x: float = 0.0
chr_out_z: float = 0.0
chr_ask_x: float = 0.0
chr_ask_z: float = 0.0
chr_x: float = 0.0
chr_y: float = 0.0
chr_z: float = 0.0
chr_yaw: float = 0.0 # facing, radians; 0 faces -z
chr_speed: float = 0.0 # m/s along the facing
chr_phase: float = 0.0 # the gait: one cycle is two steps, advanced by ground covered
chr_move: float = 0.0 # 0 standing .. 1 moving
chr_run: float = 0.0 # 0 walking .. 1 running
chr_time: float = 0.0
chr_water: float = -10000.0 # the water line under the body, every update
chr_walk_speed: float = 3.2
chr_run_speed: float = 6.8
chr_vy: float = 0.0
chr_air: bool = false
chr_air_vx: float = 0.0
chr_air_vz: float = 0.0
chr_land: float = 0.0 # seconds of the landing's crouch left
chr_steep: float = 0.0 # seconds of "that was too steep" left to say
chr_can_run: bool = true
chr_step: float = 0.0 # metres covered this frame
chr_sit: bool = false
chr_sit_mode: int = 0
chr_sit_t: float = 0.0 # 0 standing .. 1 sat, eased over a quarter second
chr_sit_x: float = 0.0 # the seat's point, walked to over the blend
chr_sit_z: float = 0.0
chr_sit_yaw: float = 0.0
chr_swim: bool = false
chr_dive: bool = false
chr_depth: float = 0.0 # metres below the surface
chr_breath: float = 0.0
chr_float: float = 0.0 # 0 swimming .. 1 floating
chr_swim_phase: float = 0.0
chr_edge_said: float = 0.0
chr_holds: int = CHAR_HOLD_SCREEN
chr_cam_yaw: float = 0.0
chr_cam_pitch: float = -0.157
chr_cam_dist: float = 4.2
chr_lock: bool = false # the orbit never settles behind on its own
chr_fps: bool = false
chr_fps_bob: float = 0.0
chr_piv_x: float = 0.0
chr_piv_y: float = 0.0
chr_piv_z: float = 0.0
chr_eye_x: float = 0.0 # where the camera is and where it looks, once computed
chr_eye_y: float = 0.0
chr_eye_z: float = 0.0
chr_eye_yaw: float = 0.0
chr_eye_pitch: float = 0.0
chr_eyes_now: bool = false # the last camera was the eyes: the figure is hidden
chr_safe_x: float = 0.0
chr_safe_z: float = 0.0
chr_fact_q: Queue<CharacterFact> = null
}
function chr__top_plain(x: float, z: float, r: float, from_y: float, step: float, base: float) -> float { return base }
function chr__bed_plain(x: float, z: float) -> float { return CharacterGround.height(x, z) }
function chr__dry(x: float, z: float) -> float { return -10000.0 }
function chr__no_push(x: float, z: float, r: float, y0: float, y1: float) -> bool { return false }
function chr__asked_x(character_st: CharacterState) -> float { return character_st.chr_ask_x }
function chr__asked_z(character_st: CharacterState) -> float { return character_st.chr_ask_z }
function chr__slope_plain(x: float, z: float) -> float {
let e = 2.0
let dx = CharacterGround.height(x + e, z) - CharacterGround.height(x - e, z)
let dz = CharacterGround.height(x, z + e) - CharacterGround.height(x, z - e)
return 1.0 - 4.0 / Math.sqrt(dx * dx + dz * dz + 16.0)
}
function chr__boots() -> float { return 1.05 }
function chr__still(x: float, z: float, fx: float, fz: float) -> float { return 0.0 }
function chr__one() -> float { return 1.0 }
function chr__zero() -> float { return 0.0 }
function chr__no() -> bool { return false }
function chr__yes() -> bool { return true }
function chr__not_carried(ix: float, iz: float, run: bool, dt: float) -> void { }