README.md

# Ecstatic

ECStatic is an Entity-Component-Systems framework in Elixir.

## Installation

If \[available in Hex\](<https://hex.pm/docs/publish>), the package can
be installed by adding \`ecstatic\` to your list of dependencies in
\`mix.exs\`:

``` elixir
def deps do
  [{:ecstatic, "~> 0.1.0"}]
end
```

Documentation can be generated with
\[ExDoc\](<https://github.com/elixir-lang/ex_doc>) and published on
\[HexDocs\](<https://hexdocs.pm>). Once published, the docs can be found
at \[<https://hexdocs.pm/ecstatic>\](<https://hexdocs.pm/ecstatic>).

# Vocabulary

Here's a list of Ecstatic words; following will be an example sentence
in English where we can connect each word to something meaningful for
you.

  - `Component` : a collection of properties
  - `Entity` : a collection of components
  - `Aspect` : a filter for entities, based on which components are and
    aren't on that entity
  - `System` : business logic; receives an entity and will do some work
    on it if the entity matches a given aspect.
  - `Watcher` : A hook that connects to a lifecycle event and will call
    a system if a particular predicate is matched.

So if Zaphod is a 33-year-old alien who doesn't have tendonitis and
plays tennis, then his stamina will go down but he won't hurt after the
game.

This could be written as (for example): There's an entity that has a
"social component" with a name of "Zaphod", a "race component" with a
name of "alien", and who does not have the "tendonitis component". When
taken through the TennisGame "system", the entity's "physical component"
will see its stamina reduced. A "watcher" will check for "physical
components" with a stamina going down and pass those to a HealthSystem;
if the entity matches the "aspect" of "has tendonitis component", it
will add a "pain component" to the entity.

# Code samples

``` elixir
defmodule Human do
  use Ecstatic.Entity
  @default_components [Age, Mortal]
end

defmodule Age do
  use Ecstatic.Component
  @default_value %{age: 1, life_expectancy: 80}
end

defmodule Mortal do
  use Ecstatic.Component
  @default_value %{mortal: true}
end

defmodule AgeSystem do
  use Ecstatic.System

  def aspect, do: %Ecstatic.Aspect{with: [Age]}

  def dispatch(entity) do
    age_comp = Entity.find_component(entity, Age)
    new_age_comp = %{age_comp | age: age_comp.age + 1}
    %Ecstatic.Changes{updated: [new_age_comp]}
  end
end

defmodule DeathOfOldAgeSystem do
  use Ecstatic.System

  def aspect, do: %Ecstatic.Aspect{with: [Age, Mortal]}

  def dispatch(entity) do
    if Enum.rand(10_000) > 7000 do
      %Ecstatic.Changes{attached: [Dead]}
    else
      %Ecstatic.Changes{}
    end
  end
end

defmodule Watchers do
  use Ecstatic.Watcher

  watch_component Age, run: AgeSystem, every: 6_000
  watch_component Age, run: DeathOfOldAgeSystem, when: fn(_pre, post) -> post.age > post.life_expectancy end
end
```

# TODO

1.  Stop using the `Ets` store, rely on the generic one