114 lines
3.1 KiB
Elixir
114 lines
3.1 KiB
Elixir
defmodule ZombieSurvivor.Game.State do
|
|
alias __MODULE__
|
|
alias ZombieSurvivor.Survivor
|
|
|
|
@type t :: %__MODULE__{
|
|
survivors: %{String.t() => Survivor.t()},
|
|
history: [String.t()]
|
|
}
|
|
@type history_type ::
|
|
:start
|
|
| :new_survivor
|
|
| :new_equipment
|
|
| :wounded
|
|
| :death
|
|
| :levelup
|
|
| :game_level
|
|
| :end
|
|
|
|
defstruct survivors: %{}, history: []
|
|
|
|
@spec new() :: State.t()
|
|
def new(), do: %State{}
|
|
|
|
@spec add_survivor(State.t(), Survivor.t()) :: State.t()
|
|
def add_survivor(game, survivor) do
|
|
name = survivor.name
|
|
|
|
if Map.has_key?(game, name) do
|
|
game
|
|
else
|
|
%{game | survivors: Map.put(game.survivors, name, survivor)}
|
|
|> add_history({:new_survivor, survivor.name})
|
|
end
|
|
end
|
|
|
|
@spec ended?(State.t()) :: boolean
|
|
def ended?(%State{survivors: survivors}) when map_size(survivors) == 0, do: false
|
|
|
|
def ended?(game) do
|
|
Enum.all?(game.survivors, fn {_, survivor} ->
|
|
Survivor.dead?(survivor)
|
|
end)
|
|
end
|
|
|
|
@spec give_equipment(State.t(), Survivor.t(), String.t()) :: State.t()
|
|
def give_equipment(game, survivor, item) do
|
|
name = survivor.name
|
|
|
|
new_survivors = Map.update!(game.survivors, name, &Survivor.add_equipment(&1, item))
|
|
|
|
%{game | survivors: new_survivors}
|
|
|> add_history({:new_equipment, {name, item}})
|
|
end
|
|
|
|
@spec level(State.t()) :: ZombieSurvivor.level()
|
|
def level(game) do
|
|
game.survivors
|
|
|> Enum.reject(fn {_, s} -> Survivor.dead?(s) end)
|
|
|> Enum.reduce(0, fn {_, s}, acc -> max(s.experience, acc) end)
|
|
|> ZombieSurvivor.level()
|
|
end
|
|
|
|
@spec kill_zombies(State.t(), Survivor.t(), non_neg_integer) :: State.t()
|
|
def kill_zombies(game, survivor, count) do
|
|
name = survivor.name
|
|
|
|
old_game_level = level(game)
|
|
|
|
survivors = game.survivors
|
|
|
|
old_survivor_level = Survivor.level(survivors[name])
|
|
new_survivors = Map.update!(survivors, name, &Survivor.kill_zombies(&1, count))
|
|
new_survivor_level = Survivor.level(new_survivors[name])
|
|
|
|
g = %{game | survivors: new_survivors}
|
|
new_game_level = level(g)
|
|
|
|
g
|
|
|> add_history({:levelup, name}, old_survivor_level != new_survivor_level)
|
|
|> add_history({:game_level, new_game_level}, old_game_level != new_game_level)
|
|
end
|
|
|
|
@spec wound_survivor(State.t(), Survivor.t()) :: State.t()
|
|
def wound_survivor(game, survivor) do
|
|
name = survivor.name
|
|
|
|
old_game_level = level(game)
|
|
|
|
survivors = game.survivors
|
|
new_survivors = Map.update!(survivors, name, &Survivor.wound(&1))
|
|
|
|
game = %{game | survivors: new_survivors}
|
|
|
|
new_game_level = level(game)
|
|
|
|
game
|
|
|> add_history({:wounded, name}, !Survivor.dead?(new_survivors[name]))
|
|
|> add_history({:death, name}, Survivor.dead?(new_survivors[name]))
|
|
|> add_history({:game_level, new_game_level}, old_game_level != new_game_level)
|
|
|> add_history({:end, DateTime.utc_now()}, ended?(game))
|
|
end
|
|
|
|
## Private
|
|
|
|
@spec add_history(State.t(), tuple, boolean) :: State.t()
|
|
defp add_history(state, entry, comp \\ true) do
|
|
if comp do
|
|
%{state | history: [entry | state.history]}
|
|
else
|
|
state
|
|
end
|
|
end
|
|
end
|