# 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 # (x, y, z, vx, vz, jump, step, max_slope_deg, dt): the body on land moved from where it stands at # (vx, vz) across, jump > 0 leaving the ground at that speed up; the ground after it (CHAR_WALK_*), # and walked_x / _y / _z where it is. The game's physics walker; there is no default mover. walk: fn(float, float, float, float, float, float, float, float, float) -> int walked_x: fn() -> float walked_y: fn() -> float walked_z: fn() -> float } 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_knock: float = 0.0 # a knock-back's speed up, handed to the walker on the next step 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_y: float = 0.0 # the seat's top chr_sit_z: float = 0.0 chr_sit_h: float = 0.0 # the seat's top above the feet; 0 on the ground chr_sit_yaw: float = 0.0 chr_leave_x: float = 0.0 # where standing up leaves the seat for chr_leave_z: 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 = chr_fact_q__new() @max(1024) chr_fpool: []CharacterFact = chr_fpool_new() # every fact record made, reused once no drain holds it @max(1024) chr_ffree: []CharacterFact = chr_ffree_new() chr_fnext: int = 0 } 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 { } function chr_fact_q__new() -> Queue { return queue_new("character.facts") }