Skip to content
This repository has been archived by the owner on Aug 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #34 from oestrich/npc-status
Browse files Browse the repository at this point in the history
Basic NPC status
  • Loading branch information
oestrich committed Apr 13, 2018
2 parents 151bfee + 07f0207 commit 86bbc19
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 37 deletions.
37 changes: 37 additions & 0 deletions docs/admin/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,43 @@ The move action takes a `max_distance` that the NPC will stray from their origin

The emote action takes a `message` that the NPC will emote into the room. `chance` is the percent chance that the NPC will do this action on each tick. `wait` is optional and will enforce a delay of `wait` seconds since the last emote. Multiple emote ticks update the same timestamp of when an emote occured.

#### Status change

You can also include a `status` key to change the status of the NPC during the emote.

```
{
"type": "tick",
"action": {
"type": "emote",
"message": "moves about the store",
"chance": 50,
"wait": 15
"status": {
"line": "[name] is moving around the store",
"listen": "[name] is whistling"
}
}
}
```

You can also reset the status to the NPC's default:

```
{
"type": "tick",
"action": {
"type": "emote",
"message": "pauses for a second",
"chance": 50,
"wait": 15
"status": {
"reset": true
}
}
}
```

### say

```
Expand Down
52 changes: 50 additions & 2 deletions lib/data/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ defmodule Data.Event do
%{action | effects: effects}
end

defp load_action(action = %{type: "emote"}) do
case action do
%{status: status} ->
status = for {key, val} <- status, into: %{}, do: {String.to_atom(key), val}
%{action | status: status}
_ ->
action
end
end

defp load_action(action), do: action

@impl Ecto.Type
Expand Down Expand Up @@ -94,7 +104,10 @@ defmodule Data.Event do
end

def starting_event("tick") do
%{type: "tick", action: %{type: "move", max_distance: 3, chance: 25, wait: 10}}
%{
type: "tick",
action: %{type: "move", max_distance: 3, chance: 25, wait: 10},
}
end

@doc """
Expand Down Expand Up @@ -211,6 +224,8 @@ defmodule Data.Event do
iex> Data.Event.valid_action?("tick", %{type: "emote", message: "hi", chance: 50, wait: 10})
true
iex> Data.Event.valid_action?("tick", %{type: "emote", message: "hi", chance: 50, wait: 10, status: %{reset: true}})
true
iex> Data.Event.valid_action?(%{type: "target"})
true
Expand All @@ -230,7 +245,7 @@ defmodule Data.Event do
def valid_action?(event_type \\ nil, action)

def valid_action?(_, action = %{type: "emote", message: string, chance: chance}) do
is_binary(string) && is_integer(chance) && wait?(action)
is_binary(string) && is_integer(chance) && wait?(action) && status?(action)
end

def valid_action?(_, action = %{type: "move", max_distance: max_distance, chance: chance}) do
Expand Down Expand Up @@ -266,6 +281,39 @@ defmodule Data.Event do
defp wait?(%{wait: wait}), do: is_integer(wait)
defp wait?(_), do: false

defp status?(%{status: status}), do: valid_status?(status)
defp status?(_), do: true

@doc """
Validate status changing attributes
"""
@spec valid_status?(map()) :: boolean()
def valid_status?(action) do
case keys(action) do
[:reset] ->
action.reset

keys ->
:key in keys && Enum.all?(action, &validate_status_key_value/1)
end
end

defp validate_status_key_value({key, value}) do
case key do
:key ->
is_binary(value)

:line ->
is_binary(value)

:listen ->
is_binary(value)

_ ->
false
end
end

@doc """
Validate events of the NPC
"""
Expand Down
1 change: 1 addition & 0 deletions lib/data/npc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Data.NPC do
:name,
:tags,
:status_line,
:status_listen,
:description,
:experience_points,
:currency,
Expand Down
4 changes: 4 additions & 0 deletions lib/game/npc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Game.NPC do
alias Game.NPC.Conversation
alias Game.NPC.Events
alias Game.NPC.Repo, as: NPCRepo
alias Game.NPC.Status
alias Game.Zone

defmacro __using__(_opts) do
Expand All @@ -34,6 +35,7 @@ defmodule Game.NPC do
:room_id,
:target,
:last_controlled_at,
:status,
combat: false,
tick_events: [],
conversations: %{},
Expand Down Expand Up @@ -169,6 +171,7 @@ defmodule Game.NPC do

npc = customize_npc(npc_spawner, npc_spawner.npc)
npc = %{npc | stats: Stats.default(npc.stats)}
status = %Status{key: "start", line: npc.status_line, listen: npc.status_listen}

npc_spawner.zone_id |> Zone.npc_online(npc)

Expand All @@ -178,6 +181,7 @@ defmodule Game.NPC do
state
|> Map.put(:npc_spawner, npc_spawner)
|> Map.put(:npc, npc)
|> Map.put(:status, status)
|> Map.put(:room_id, npc_spawner.room_id)

GenServer.cast(self(), :enter)
Expand Down
4 changes: 3 additions & 1 deletion lib/game/npc/actions.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule Game.NPC.Actions do
alias Game.Effect
alias Game.Items
alias Game.NPC.Events
alias Game.NPC.Status

@doc """
Clean up conversation state, after 5 minutes remove the state of the user
Expand All @@ -34,9 +35,10 @@ defmodule Game.NPC.Actions do

def handle_respawn(state = %{npc: npc, npc_spawner: npc_spawner}) do
npc = %{npc | stats: %{npc.stats | health_points: npc.stats.max_health_points}}
status = %Status{key: "start", line: npc.status_line, listen: npc.status_listen}
npc_spawner.room_id |> @room.enter({:npc, npc}, :respawn)
Events.broadcast(npc, "character/respawned")
%{state | npc: npc, room_id: npc_spawner.room_id}
%{state | npc: npc, status: status, room_id: npc_spawner.room_id}
end

@doc """
Expand Down
Loading

0 comments on commit 86bbc19

Please sign in to comment.