kimi gone wild
This commit is contained in:
252
lib/odinsea/game/reactor_stats.ex
Normal file
252
lib/odinsea/game/reactor_stats.ex
Normal file
@@ -0,0 +1,252 @@
|
||||
defmodule Odinsea.Game.ReactorStats do
|
||||
@moduledoc """
|
||||
Represents reactor template stats (state machine data).
|
||||
|
||||
Contains the state definitions for a reactor type.
|
||||
Each state defines: type, next state, required item, timeout, touch mode.
|
||||
|
||||
Ported from Java: src/server/maps/MapleReactorStats.java
|
||||
"""
|
||||
|
||||
defmodule Point do
|
||||
@moduledoc "Simple 2D point for area bounds"
|
||||
@type t :: %__MODULE__{x: integer(), y: integer()}
|
||||
defstruct [:x, :y]
|
||||
end
|
||||
|
||||
defmodule StateData do
|
||||
@moduledoc "State definition for a reactor"
|
||||
@type t :: %__MODULE__{
|
||||
type: integer(), # State type (determines behavior)
|
||||
next_state: integer(), # Next state index (-1 = end)
|
||||
react_item: {integer(), integer()} | nil, # {item_id, quantity} required
|
||||
timeout: integer(), # Timeout in ms before auto-advance (-1 = none)
|
||||
can_touch: integer() # 0 = hit only, 1 = click/touch, 2 = touch only
|
||||
}
|
||||
defstruct [
|
||||
:type,
|
||||
:next_state,
|
||||
:react_item,
|
||||
timeout: -1,
|
||||
can_touch: 0
|
||||
]
|
||||
end
|
||||
|
||||
@typedoc "Reactor stats struct"
|
||||
@type t :: %__MODULE__{
|
||||
tl: Point.t() | nil, # Top-left corner of area (for item-triggered)
|
||||
br: Point.t() | nil, # Bottom-right corner of area
|
||||
states: %{integer() => StateData.t()}, # State definitions by state number
|
||||
activate_by_touch: boolean() # Whether reactor activates by touch
|
||||
}
|
||||
|
||||
defstruct [
|
||||
:tl,
|
||||
:br,
|
||||
states: %{},
|
||||
activate_by_touch: false
|
||||
]
|
||||
|
||||
@doc """
|
||||
Creates a new empty reactor stats.
|
||||
"""
|
||||
@spec new() :: t()
|
||||
def new do
|
||||
%__MODULE__{}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets the top-left point of the area.
|
||||
"""
|
||||
@spec set_tl(t(), integer(), integer()) :: t()
|
||||
def set_tl(stats, x, y) do
|
||||
%{stats | tl: %Point{x: x, y: y}}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets the bottom-right point of the area.
|
||||
"""
|
||||
@spec set_br(t(), integer(), integer()) :: t()
|
||||
def set_br(stats, x, y) do
|
||||
%{stats | br: %Point{x: x, y: y}}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Sets whether reactor activates by touch.
|
||||
"""
|
||||
@spec set_activate_by_touch(t(), boolean()) :: t()
|
||||
def set_activate_by_touch(stats, activate) do
|
||||
%{stats | activate_by_touch: activate}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Adds a state definition.
|
||||
|
||||
## Parameters
|
||||
- stats: the reactor stats struct
|
||||
- state_num: the state number (byte value)
|
||||
- type: the state type (determines behavior)
|
||||
- react_item: {item_id, quantity} or nil
|
||||
- next_state: the next state number (-1 for end)
|
||||
- timeout: timeout in ms (-1 for none)
|
||||
- can_touch: 0 = hit only, 1 = click, 2 = touch only
|
||||
"""
|
||||
@spec add_state(
|
||||
t(),
|
||||
integer(),
|
||||
integer(),
|
||||
{integer(), integer()} | nil,
|
||||
integer(),
|
||||
integer(),
|
||||
integer()
|
||||
) :: t()
|
||||
def add_state(stats, state_num, type, react_item, next_state, timeout, can_touch) do
|
||||
state_data = %StateData{
|
||||
type: type,
|
||||
react_item: react_item,
|
||||
next_state: next_state,
|
||||
timeout: timeout,
|
||||
can_touch: can_touch
|
||||
}
|
||||
|
||||
%{stats | states: Map.put(stats.states, state_num, state_data)}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the next state for a given current state.
|
||||
Returns -1 if not found.
|
||||
"""
|
||||
@spec get_next_state(t(), integer()) :: integer()
|
||||
def get_next_state(stats, state) do
|
||||
case Map.get(stats.states, state) do
|
||||
nil -> -1
|
||||
state_data -> state_data.next_state
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the type for a given state.
|
||||
Returns -1 if not found.
|
||||
"""
|
||||
@spec get_type(t(), integer()) :: integer()
|
||||
def get_type(stats, state) do
|
||||
case Map.get(stats.states, state) do
|
||||
nil -> -1
|
||||
state_data -> state_data.type
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the react item for a given state.
|
||||
Returns nil if not found.
|
||||
"""
|
||||
@spec get_react_item(t(), integer()) :: {integer(), integer()} | nil
|
||||
def get_react_item(stats, state) do
|
||||
case Map.get(stats.states, state) do
|
||||
nil -> nil
|
||||
state_data -> state_data.react_item
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the timeout for a given state.
|
||||
Returns -1 if not found.
|
||||
"""
|
||||
@spec get_timeout(t(), integer()) :: integer()
|
||||
def get_timeout(stats, state) do
|
||||
case Map.get(stats.states, state) do
|
||||
nil -> -1
|
||||
state_data -> state_data.timeout
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the touch mode for a given state.
|
||||
Returns 0 if not found.
|
||||
|
||||
Modes:
|
||||
- 0: Hit only (weapon attack)
|
||||
- 1: Click/touch (interact button)
|
||||
- 2: Touch only (walk into)
|
||||
"""
|
||||
@spec can_touch(t(), integer()) :: integer()
|
||||
def can_touch(stats, state) do
|
||||
case Map.get(stats.states, state) do
|
||||
nil -> 0
|
||||
state_data -> state_data.can_touch
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets all state numbers defined for this reactor.
|
||||
"""
|
||||
@spec get_state_numbers(t()) :: [integer()]
|
||||
def get_state_numbers(stats) do
|
||||
Map.keys(stats.states) |> Enum.sort()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if a state exists.
|
||||
"""
|
||||
@spec has_state?(t(), integer()) :: boolean()
|
||||
def has_state?(stats, state) do
|
||||
Map.has_key?(stats.states, state)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the state data for a given state number.
|
||||
"""
|
||||
@spec get_state_data(t(), integer()) :: StateData.t() | nil
|
||||
def get_state_data(stats, state) do
|
||||
Map.get(stats.states, state)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Builds reactor stats from JSON data.
|
||||
"""
|
||||
@spec from_json(map()) :: t()
|
||||
def from_json(data) do
|
||||
stats = new()
|
||||
|
||||
# Set activate by touch
|
||||
stats = set_activate_by_touch(stats, data["activate_by_touch"] == true)
|
||||
|
||||
# Set area bounds if present
|
||||
stats =
|
||||
if data["tl"] do
|
||||
set_tl(stats, data["tl"]["x"] || 0, data["tl"]["y"] || 0)
|
||||
else
|
||||
stats
|
||||
end
|
||||
|
||||
stats =
|
||||
if data["br"] do
|
||||
set_br(stats, data["br"]["x"] || 0, data["br"]["y"] || 0)
|
||||
else
|
||||
stats
|
||||
end
|
||||
|
||||
# Add states
|
||||
states = data["states"] || %{}
|
||||
|
||||
Enum.reduce(states, stats, fn {state_num_str, state_data}, acc_stats ->
|
||||
state_num = String.to_integer(state_num_str)
|
||||
|
||||
type = state_data["type"] || 999
|
||||
next_state = state_data["next_state"] || -1
|
||||
timeout = state_data["timeout"] || -1
|
||||
can_touch = state_data["can_touch"] || 0
|
||||
|
||||
react_item =
|
||||
if state_data["react_item"] do
|
||||
item_id = state_data["react_item"]["item_id"]
|
||||
quantity = state_data["react_item"]["quantity"] || 1
|
||||
if item_id, do: {item_id, quantity}, else: nil
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
add_state(acc_stats, state_num, type, react_item, next_state, timeout, can_touch)
|
||||
end)
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user