defmodule ExTTRPGDev.RuleSystems.Abilities.Assignment do alias ExTTRPGDev.RuleSystems.Abilities.Assignment alias ExTTRPGDev.Dice @moduledoc """ This module handles the different ways of assigning ability scores """ defstruct [:rolling_methods, :point_buy] defmodule PointBuy do @moduledoc """ PointBuy specification for ability score assignment """ defstruct [:points, :score_costs] defmodule ScoreCost do @moduledoc """ Mapping of specific ability scores to cost for PointBuy ability assignment """ defstruct [:score, :cost] end end defmodule RollingMethod do @moduledoc """ Specificiation for different rolling methods for ability assignment """ defstruct [:name, :dice, :special, :default] end @doc """ Returns the default assignment method for the defined assignment methods ## Examples iex> Assignment.default_assignment() """ def default_assignment(%Assignment{ rolling_methods: [%Assignment.RollingMethod{} = first | _tail] = rolling_methods }) do Enum.find(rolling_methods, first, fn method -> method.default == true end) end @doc """ Generates an ability score using the Assignment.RollingMethod ## Examples # Although not necessary, let's seed the random algorithm iex> :rand.seed(:exsplus, 1337) iex> Assignment.roll_via_method!(%Assignment.RollingMethod{dice: "3d6"}) [6, 2, 3] """ def roll_via_method!(%Assignment.RollingMethod{} = method) do Dice.roll(method.dice) |> Assignment.apply_method_special!(method.special) end @doc """ Applies a method special to the rolled values ## Examples iex> Assignment.apply_method_special!([4, 3, 2, 1], "drop_lowest") [2, 3, 4] """ def apply_method_special!([head | _tail] = rolls, special) when is_integer(head) do case special do "drop_lowest" -> [_lowest | the_rest] = Enum.sort(rolls) the_rest nil -> rolls unhandled -> raise "Special '#{unhandled}' is not handled for rolling methods" end end end