130 lines
3.6 KiB
Elixir
130 lines
3.6 KiB
Elixir
defmodule ExSpeedGame.Game.Menu do
|
|
use GenServer
|
|
require Logger
|
|
alias ExSpeedGame.Game.{Types, ButtonInput, ShowIP, Lights}
|
|
|
|
alias ExSpeedGame.Game.Modes.{
|
|
Speed
|
|
}
|
|
|
|
@menu {
|
|
{"SpeedGame", Speed,
|
|
%Speed.Options{
|
|
initial_score: 0,
|
|
initial_delay: Application.get_env(:ex_speed_game, :delay_start)
|
|
}},
|
|
{"SpeedGame Pro", Speed,
|
|
%Speed.Options{
|
|
initial_score: Application.get_env(:ex_speed_game, :score_pro),
|
|
initial_delay: Application.get_env(:ex_speed_game, :delay_pro)
|
|
}},
|
|
{"MemoryGame", Speed, %{}},
|
|
{"Show IP", ShowIP, %{}}
|
|
}
|
|
@menu_size :erlang.tuple_size(@menu)
|
|
|
|
defmodule Options do
|
|
@type t :: %__MODULE__{
|
|
name: GenServer.name()
|
|
}
|
|
@enforce_keys [:name]
|
|
defstruct [:name]
|
|
end
|
|
|
|
defmodule State do
|
|
@type mode :: :menu | :ingame
|
|
@type t :: %__MODULE__{
|
|
index: integer(),
|
|
button_pins: Types.pins(),
|
|
mode: mode(),
|
|
game: pid(),
|
|
game_ref: reference()
|
|
}
|
|
@enforce_keys [:button_pins]
|
|
defstruct [:button_pins, :game, :game_ref, index: 0, mode: :menu]
|
|
end
|
|
|
|
@spec start_link(Options.t()) :: :ignore | {:error, any} | {:ok, pid}
|
|
def start_link(%Options{} = opts) do
|
|
GenServer.start_link(
|
|
__MODULE__,
|
|
%{},
|
|
name: opts.name
|
|
)
|
|
end
|
|
|
|
@impl true
|
|
@spec init(any) :: {:ok, State.t()}
|
|
def init(_) do
|
|
state = %State{button_pins: Application.get_env(:ex_speed_game, :button_pins)}
|
|
ButtonInput.acquire(ButtonInput)
|
|
display_index(state.index)
|
|
{:ok, state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info(msg, state)
|
|
|
|
@spec handle_info({:input, Types.pin()}, State.t()) :: {:noreply, State.t()}
|
|
def handle_info(
|
|
{:input, btn_pin},
|
|
%State{index: index, button_pins: {pin1, _, _, pin4}, mode: :menu} = state
|
|
)
|
|
when btn_pin in [pin1, pin4] do
|
|
is_first_button = btn_pin == pin1
|
|
is_last_button = btn_pin == pin4
|
|
|
|
new_index =
|
|
cond do
|
|
is_first_button and index > 0 -> index - 1
|
|
is_last_button and index < @menu_size - 1 -> index + 1
|
|
true -> index
|
|
end
|
|
|
|
display_index(new_index)
|
|
{:noreply, %State{state | index: new_index}}
|
|
end
|
|
|
|
@spec handle_info({:input, Types.pin()}, State.t()) :: {:noreply, State.t()}
|
|
def handle_info({:input, _pin}, %State{index: index, mode: :menu} = state) do
|
|
ButtonInput.release(ButtonInput)
|
|
|
|
{_, module, init_arg} = elem(@menu, index)
|
|
|
|
{:ok, pid} =
|
|
DynamicSupervisor.start_child(ExSpeedGame.Game.Supervisor, module.child_spec(init_arg))
|
|
|
|
ref = Process.monitor(pid)
|
|
|
|
{:noreply, %State{state | mode: :ingame, game: pid, game_ref: ref}}
|
|
end
|
|
|
|
@spec handle_info({:DOWN, reference(), :process, any, any}, State.t()) :: {:noreply, State.t()}
|
|
def handle_info(
|
|
{:DOWN, ref, :process, _, reason},
|
|
%State{
|
|
index: index,
|
|
mode: :ingame,
|
|
game_ref: game_ref
|
|
} = state
|
|
)
|
|
when ref == game_ref do
|
|
# Ensure input is released in case process crashed violently
|
|
ButtonInput.release(ButtonInput)
|
|
|
|
# If crashed, show crash pattern for a moment
|
|
if reason != :normal do
|
|
Lights.set(Lights, Application.get_env(:ex_speed_game, :crash_pattern))
|
|
Process.sleep(Application.get_env(:ex_speed_game, :crash_pattern_delay))
|
|
end
|
|
|
|
ButtonInput.acquire(ButtonInput)
|
|
display_index(index)
|
|
{:noreply, %State{state | mode: :menu, game: nil, game_ref: nil}}
|
|
end
|
|
|
|
defp display_index(index) do
|
|
Logger.debug("Menu viewing: #{@menu |> elem(index) |> elem(0)}")
|
|
Lights.show_binary(Lights, index + 1)
|
|
end
|
|
end
|