import lustre/effect.{type Effect} import rcade/inputs/controls.{type Controls, type SystemButtons} import rcade/inputs/spinner.{type Spinner} import rcade/player.{type Player, Player1, Player2} /// Combined input state for a player. pub type Player1Inputs { Player1Inputs(controls: Controls, spinner: Spinner) } /// Combined input state for a player. pub type Player2Inputs { Player2Inputs(controls: Controls, spinner: Spinner) } /// Poll all input states at a given interval (in milliseconds). /// /// Returns an effect that builds a msg from the states of both players' /// controls and spinners, as well as the state of the system buttons. pub fn poll( every interval: Int, with to_msg: fn(Player1Inputs, Player2Inputs, SystemButtons) -> msg, ) -> Effect(msg) { effect.from(fn(dispatch) { let _stop = start_polling(interval, fn() { let player1 = Player1Inputs( controls: controls.read_player(Player1), spinner: spinner.read(Player1), ) let player2 = Player2Inputs( controls: controls.read_player(Player2), spinner: spinner.read(Player2), ) let system = controls.read_system() dispatch(to_msg(player1, player2, system)) }) Nil }) } @external(javascript, "./inputs_ffi.mjs", "start_polling") fn start_polling(interval: Int, callback: fn() -> Nil) -> fn() -> Nil /// A button press event from the classic controls. pub type ButtonPress { PlayerButton(player: Player, button: String) SystemButton(String) } /// Subscribe to "classic" control press events. /// /// Each press dispatches a `ButtonPress` with the provided msg constructor. pub fn on_press(with to_msg: fn(ButtonPress) -> msg) -> Effect(msg) { effect.from(fn(dispatch) { let _unsubscribe = controls.subscribe(fn(player_int, button, is_system) { let event = case is_system { False -> { let player = case player_int { 1 -> Player1 _ -> Player2 } PlayerButton(player:, button:) } True -> SystemButton(button) } dispatch(to_msg(event)) }) Nil }) }