~sjm/kitties_village

421dca140b53fa1be5e37a2a9711fc605670b1c5 — Sam Marshall 4 months ago 7f359bb
add ticking, refactor
M lib/kitty_village.ex => lib/kitty_village.ex +27 -33
@@ 7,38 7,28 @@ defmodule KittyVillage do

  import Ratatouille.View

  alias KittyVillage.Game
  alias KittyVillage.Building
  alias KittyVillage.Resource

  alias KittyVillage.View.BuildingButton

  alias KittyVillage.Building.Field

  alias KittyVillage.Resource.Catnip

  @type building_list :: %{Building.building_name() => number}
  @type resource_list :: %{Resource.resource_name() => number}
  @type state :: %{
          :resources => resource_list,
          :buildings => building_list,
          :stats => %{:max_resources => %{Catnip.name() => number}}
        }

  def init(_context),
    do: %{
      resources: %{
        Catnip.name() => 10.0
      },
      buildings: %{
        Field.name() => 0.0
      },
      stats: %{
        max_resources: %{Catnip.name() => 10.0}
      }
    }
    do: Game.fresh_state()

  def subscribe(_model) do
    Ratatouille.Runtime.Subscription.interval(200, :tick)
  end

  def update(model, msg) do
    case msg do
      {:event, %{ch: ?c}} -> Resource.add_one(Catnip, model)
      {:event, %{ch: ?f}} -> KittyVillage.Building.buy(Field, model)
      {:event, %{ch: ?f}} -> Building.buy(Field, model)
      :tick -> Game.tick(model)
      _ -> model
    end
  end


@@ 50,27 40,31 @@ defmodule KittyVillage do
          [size: 4],
          render_resources(model.resources)
        ),
        column([size: 8], [
          panel([
            if Field.visible(model) do
              render_catnip_field(
                model.buildings.catnip_field,
                KittyVillage.Building.cost(Field, model)
              )
            end
          ])
        ])
        column(size: 8) do
          render_buildings(model)
        end
      ])
    end
  end

  defp render_resources(resources) do
    panel([
      label(content: "Catnip(c): #{Float.round(resources.catnip, 2)}")
      label(content: "catnip: #{Float.round(resources.catnip, 2)}")
    ])
  end

  defp render_catnip_field(count, current_cost) do
    KittyVillage.View.BuildingButton.render("Catnip Field", "f", count, current_cost)
  defp render_buildings(model) do
    panel([
      row do
        column([size: 6], [
          panel do
            label(content: "Gather Catnip(c)")
          end
        ])
      end,
      if Field.visible(model) do
        BuildingButton.render("Catnip Field", "f", Field, model)
      end
    ])
  end
end

M lib/kitty_village/building.ex => lib/kitty_village/building.ex +5 -1
@@ 16,12 16,16 @@ defmodule KittyVillage.Building do
  """
  @callback effect(number) :: [KittyVillage.Effect.t()]

  def count(b, model) do
    model.buildings[b.name()]
  end

  def cost(b, %{buildings: buildings}) do
    cost(b, buildings)
  end

  def cost(b, buildings) do
    b.current_cost(buildings[b.name()])
    b.current_cost(count(b, %{buildings: buildings}))
  end

  @doc """

M lib/kitty_village/effect.ex => lib/kitty_village/effect.ex +8 -0
@@ 3,8 3,16 @@ defmodule KittyVillage.Effect do
  Describe the effect of a building
  """

  alias KittyVillage.Game

  @type t :: increase_resource


  @type increase_resource :: {:increase, KittyVillage.Resource.resource_name, number}

  @spec apply_effect(effect :: t, state :: Game.state) :: Game.state
  def apply_effect({:increase, resource, amount}, %{resources: resources } = state) do
    resources = put_in(resources[resource], resources[resource] + amount)
    %{state | resources: resources}
  end
end

A lib/kitty_village/game.ex => lib/kitty_village/game.ex +53 -0
@@ 0,0 1,53 @@
defmodule KittyVillage.Game do
  @moduledoc """
  This module is the coordination point for much of the game
  """

  alias KittyVillage.Building
  alias KittyVillage.Resource
  alias KittyVillage.Effect

  alias KittyVillage.Building.Field

  alias KittyVillage.Resource.Catnip

  @type building_list :: %{Building.building_name() => number}
  @type resource_list :: %{Resource.resource_name() => number}
  @type state :: %{
          :resources => resource_list,
          :buildings => building_list,
          :stats => %{:max_resources => %{Catnip.name() => number}}
        }

  @spec fresh_state :: state
  def fresh_state(),
    do: %{
      resources: %{
        Catnip.name() => 10.0
      },
      buildings: %{
        Field.name() => 0.0
      },
      stats: %{
        max_resources: %{Catnip.name() => 10.0}
      }
    }

  @doc "calculate and apply the effects on state for a single game tick"
  @spec tick(state) :: state
  def tick(state) do
    # building effects
    effects =
      state.buildings
      |> Map.keys()
      |> Enum.map(&key_to_building/1)
      |> Enum.map(fn b -> b.effect(state.buildings[b.name()]) end)
      |> List.flatten()

    Enum.reduce(effects, state, fn el, acc -> Effect.apply_effect(el, acc) end)
  end

  defp key_to_building(:catnip_field) do
    Field
  end
end

M lib/kitty_village/resource.ex => lib/kitty_village/resource.ex +7 -0
@@ 6,6 6,13 @@ defmodule KittyVillage.Resource do
  """
  @callback name :: resource_name

  alias KittyVillage.Game

  @spec display_resource(Game.state, resource_name) :: String.t
  def display_resource(state, resource_name) do
    "#{resource_name}: #{state.resources[resource_name]}"
  end

  def add_one(resource, %{resources: resources, stats: %{max_resources: max_resources}} = model) do
    resources = Map.put(resources, resource.name(), resources[resource.name()] + 1)
    max_resources = update_max_resources(resource, resources[resource.name()], max_resources)

M lib/kitty_village/view/building_button.ex => lib/kitty_village/view/building_button.ex +20 -6
@@ 1,13 1,27 @@
defmodule KittyVillage.View.BuildingButton do
  import Ratatouille.View

  def render(button_name, activate, count, cost) do
  alias KittyVillage.Building

  def render(button_name, activate, building, model) do
    row do
      column([size: 12], [
               label(content: "#{button_name}(#{activate})"),
               label(content: cost_text(cost)),
               label(content: "built: #{count}")
             ])
      column([size: 6], [
        panel(
          [
            color:
              if model.resources.catnip > Building.cost(building, model) do
                :white
              else
                :yellow
              end
          ],
          [
            label(content: "#{button_name}(#{activate})"),
            label(content: cost_text(Building.cost(building, model))),
            label(content: "built: #{Building.count(building, model)}")
          ]
        )
      ])
    end
  end