kimi gone wild
This commit is contained in:
309
lib/odinsea/game/monster_status.ex
Normal file
309
lib/odinsea/game/monster_status.ex
Normal file
@@ -0,0 +1,309 @@
|
||||
defmodule Odinsea.Game.MonsterStatus do
|
||||
@moduledoc """
|
||||
Monster status effects (buffs/debuffs) for MapleStory.
|
||||
|
||||
Ported from Java: client/status/MobStat.java
|
||||
|
||||
MonsterStatus represents status effects that can be applied to monsters:
|
||||
- PAD (Physical Attack Damage)
|
||||
- PDD (Physical Defense)
|
||||
- MAD (Magic Attack Damage)
|
||||
- MDD (Magic Defense)
|
||||
- ACC (Accuracy)
|
||||
- EVA (Evasion)
|
||||
- Speed
|
||||
- Stun
|
||||
- Freeze
|
||||
- Poison
|
||||
- Seal
|
||||
- And more...
|
||||
"""
|
||||
|
||||
import Bitwise
|
||||
|
||||
@type t ::
|
||||
:pad
|
||||
| :pdd
|
||||
| :mad
|
||||
| :mdd
|
||||
| :acc
|
||||
| :eva
|
||||
| :speed
|
||||
| :stun
|
||||
| :freeze
|
||||
| :poison
|
||||
| :seal
|
||||
| :darkness
|
||||
| :power_up
|
||||
| :magic_up
|
||||
| :p_guard_up
|
||||
| :m_guard_up
|
||||
| :doom
|
||||
| :web
|
||||
| :p_immune
|
||||
| :m_immune
|
||||
| :showdown
|
||||
| :hard_skin
|
||||
| :ambush
|
||||
| :damaged_elem_attr
|
||||
| :venom
|
||||
| :blind
|
||||
| :seal_skill
|
||||
| :burned
|
||||
| :dazzle
|
||||
| :p_counter
|
||||
| :m_counter
|
||||
| :disable
|
||||
| :rise_by_toss
|
||||
| :body_pressure
|
||||
| :weakness
|
||||
| :time_bomb
|
||||
| :magic_crash
|
||||
| :exchange_attack
|
||||
| :heal_by_damage
|
||||
| :invincible
|
||||
|
||||
@doc """
|
||||
All monster status effects with their bit values and positions.
|
||||
Format: {status_name, bit_value, position}
|
||||
Position 1 = first int, Position 2 = second int
|
||||
"""
|
||||
def all_statuses do
|
||||
[
|
||||
# Position 1 (first int)
|
||||
{:pad, 0x1, 1},
|
||||
{:pdd, 0x2, 1},
|
||||
{:mad, 0x4, 1},
|
||||
{:mdd, 0x8, 1},
|
||||
{:acc, 0x10, 1},
|
||||
{:eva, 0x20, 1},
|
||||
{:speed, 0x40, 1},
|
||||
{:stun, 0x80, 1},
|
||||
{:freeze, 0x100, 1},
|
||||
{:poison, 0x200, 1},
|
||||
{:seal, 0x400, 1},
|
||||
{:darkness, 0x800, 1},
|
||||
{:power_up, 0x1000, 1},
|
||||
{:magic_up, 0x2000, 1},
|
||||
{:p_guard_up, 0x4000, 1},
|
||||
{:m_guard_up, 0x8000, 1},
|
||||
{:doom, 0x10000, 1},
|
||||
{:web, 0x20000, 1},
|
||||
{:p_immune, 0x40000, 1},
|
||||
{:m_immune, 0x80000, 1},
|
||||
{:showdown, 0x100000, 1},
|
||||
{:hard_skin, 0x200000, 1},
|
||||
{:ambush, 0x400000, 1},
|
||||
{:damaged_elem_attr, 0x800000, 1},
|
||||
{:venom, 0x1000000, 1},
|
||||
{:blind, 0x2000000, 1},
|
||||
{:seal_skill, 0x4000000, 1},
|
||||
{:burned, 0x8000000, 1},
|
||||
{:dazzle, 0x10000000, 1},
|
||||
{:p_counter, 0x20000000, 1},
|
||||
{:m_counter, 0x40000000, 1},
|
||||
{:disable, 0x80000000, 1},
|
||||
|
||||
# Position 2 (second int)
|
||||
{:rise_by_toss, 0x1, 2},
|
||||
{:body_pressure, 0x2, 2},
|
||||
{:weakness, 0x4, 2},
|
||||
{:time_bomb, 0x8, 2},
|
||||
{:magic_crash, 0x10, 2},
|
||||
{:exchange_attack, 0x20, 2},
|
||||
{:heal_by_damage, 0x40, 2},
|
||||
{:invincible, 0x80, 2}
|
||||
]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the bit value for a status effect.
|
||||
"""
|
||||
@spec get_bit(t()) :: integer()
|
||||
def get_bit(status) do
|
||||
case List.keyfind(all_statuses(), status, 0) do
|
||||
{_, bit, _} -> bit
|
||||
nil -> 0
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the position (1 or 2) for a status effect.
|
||||
"""
|
||||
@spec get_position(t()) :: integer()
|
||||
def get_position(status) do
|
||||
case List.keyfind(all_statuses(), status, 0) do
|
||||
{_, _, pos} -> pos
|
||||
nil -> 1
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if a status is in position 1.
|
||||
"""
|
||||
@spec position_1?(t()) :: boolean()
|
||||
def position_1?(status) do
|
||||
get_position(status) == 1
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if a status is in position 2.
|
||||
"""
|
||||
@spec position_2?(t()) :: boolean()
|
||||
def position_2?(status) do
|
||||
get_position(status) == 2
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets all statuses in position 1.
|
||||
"""
|
||||
@spec position_1_statuses() :: [t()]
|
||||
def position_1_statuses do
|
||||
all_statuses()
|
||||
|> Enum.filter(fn {_, _, pos} -> pos == 1 end)
|
||||
|> Enum.map(fn {status, _, _} -> status end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets all statuses in position 2.
|
||||
"""
|
||||
@spec position_2_statuses() :: [t()]
|
||||
def position_2_statuses do
|
||||
all_statuses()
|
||||
|> Enum.filter(fn {_, _, pos} -> pos == 2 end)
|
||||
|> Enum.map(fn {status, _, _} -> status end)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Encodes a map of status effects to bitmasks.
|
||||
Returns {mask1, mask2} where each is an integer bitmask.
|
||||
"""
|
||||
@spec encode_statuses(%{t() => integer()}) :: {integer(), integer()}
|
||||
def encode_statuses(statuses) when is_map(statuses) do
|
||||
mask1 =
|
||||
statuses
|
||||
|> Enum.filter(fn {status, _} -> position_1?(status) end)
|
||||
|> Enum.reduce(0, fn {status, _}, acc -> acc ||| get_bit(status) end)
|
||||
|
||||
mask2 =
|
||||
statuses
|
||||
|> Enum.filter(fn {status, _} -> position_2?(status) end)
|
||||
|> Enum.reduce(0, fn {status, _}, acc -> acc ||| get_bit(status) end)
|
||||
|
||||
{mask1, mask2}
|
||||
end
|
||||
|
||||
def encode_statuses(_), do: {0, 0}
|
||||
|
||||
@doc """
|
||||
Decodes bitmasks to a list of status effects.
|
||||
"""
|
||||
@spec decode_statuses(integer(), integer()) :: [t()]
|
||||
def decode_statuses(mask1, mask2) do
|
||||
pos1 =
|
||||
position_1_statuses()
|
||||
|> Enum.filter(fn status -> (mask1 &&& get_bit(status)) != 0 end)
|
||||
|
||||
pos2 =
|
||||
position_2_statuses()
|
||||
|> Enum.filter(fn status -> (mask2 &&& get_bit(status)) != 0 end)
|
||||
|
||||
pos1 ++ pos2
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the linked disease for a monster status.
|
||||
Used when converting monster debuffs to player diseases.
|
||||
"""
|
||||
@spec get_linked_disease(t()) :: atom() | nil
|
||||
def get_linked_disease(status) do
|
||||
case status do
|
||||
:stun -> :stun
|
||||
:web -> :stun
|
||||
:poison -> :poison
|
||||
:venom -> :poison
|
||||
:seal -> :seal
|
||||
:magic_crash -> :seal
|
||||
:freeze -> :freeze
|
||||
:blind -> :darkness
|
||||
:speed -> :slow
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the monster status from a Pokemon-style skill ID.
|
||||
Used for familiar/capture card mechanics.
|
||||
"""
|
||||
@spec from_pokemon_skill(integer()) :: t() | nil
|
||||
def from_pokemon_skill(skill_id) do
|
||||
case skill_id do
|
||||
120 -> :seal
|
||||
121 -> :blind
|
||||
123 -> :stun
|
||||
125 -> :poison
|
||||
126 -> :speed
|
||||
137 -> :freeze
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the status is a stun effect (prevents movement).
|
||||
"""
|
||||
@spec is_stun?(t()) :: boolean()
|
||||
def is_stun?(status) do
|
||||
status in [:stun, :freeze]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the status is a damage over time effect.
|
||||
"""
|
||||
@spec is_dot?(t()) :: boolean()
|
||||
def is_dot?(status) do
|
||||
status in [:poison, :venom, :burned]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the status is a debuff (negative effect).
|
||||
"""
|
||||
@spec is_debuff?(t()) :: boolean()
|
||||
def is_debuff?(status) do
|
||||
status in [
|
||||
:stun, :freeze, :poison, :seal, :darkness, :doom, :web,
|
||||
:blind, :seal_skill, :burned, :dazzle, :speed, :weakness,
|
||||
:time_bomb, :magic_crash, :disable, :rise_by_toss
|
||||
]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Checks if the status is a buff (positive effect).
|
||||
"""
|
||||
@spec is_buff?(t()) :: boolean()
|
||||
def is_buff?(status) do
|
||||
status in [
|
||||
:pad, :pdd, :mad, :mdd, :acc, :eva, :power_up, :magic_up,
|
||||
:p_guard_up, :m_guard_up, :hard_skin, :invincible, :heal_by_damage
|
||||
]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the default duration for a status effect in milliseconds.
|
||||
"""
|
||||
@spec default_duration(t()) :: integer()
|
||||
def default_duration(status) do
|
||||
case status do
|
||||
:stun -> 3000
|
||||
:freeze -> 5000
|
||||
:poison -> 8000
|
||||
:seal -> 5000
|
||||
:darkness -> 8000
|
||||
:doom -> 10000
|
||||
:web -> 8000
|
||||
:blind -> 8000
|
||||
:speed -> 8000
|
||||
:weakness -> 8000
|
||||
_ -> 5000
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user