kimi gone wild

This commit is contained in:
ra
2026-02-14 23:12:33 -07:00
parent bbd205ecbe
commit 0222be36c5
98 changed files with 39726 additions and 309 deletions

View File

@@ -0,0 +1,39 @@
defmodule Odinsea.Game.Movement.Absolute do
@moduledoc """
Absolute life movement - normal walking, flying, etc.
Ported from Java AbsoluteLifeMovement.java
This is the most common movement type for:
- Normal walking (command 0)
- Flying (commands 37-42)
- Rush skills (when not instant)
Contains position, velocity, and offset information.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (0, 37-42)
x: integer(), # Target X position
y: integer(), # Target Y position
vx: integer(), # X velocity (pixels per second)
vy: integer(), # Y velocity (pixels per second)
unk: integer(), # Unknown short value
offset_x: integer(), # X offset
offset_y: integer(), # Y offset
stance: integer(), # New stance/move action
duration: integer() # Movement duration in ms
}
defstruct [
:command,
:x,
:y,
:vx,
:vy,
:unk,
:offset_x,
:offset_y,
:stance,
:duration
]
end

View File

@@ -0,0 +1,24 @@
defmodule Odinsea.Game.Movement.Aran do
@moduledoc """
Aran movement - Aran class combat step movements.
Ported from Java AranMovement.java
Used for:
- Aran combat steps (commands 21-31)
- Special Aran skills (command 35)
Note: Position is not used for this movement type.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (21-31, 35)
stance: integer(), # New stance/move action
unk: integer() # Unknown short value
}
defstruct [
:command,
:stance,
:unk
]
end

View File

@@ -0,0 +1,31 @@
defmodule Odinsea.Game.Movement.Bounce do
@moduledoc """
Bounce movement - bouncing off surfaces.
Ported from Java BounceMovement.java
Used for:
- Bouncing (command -1)
- Wall bouncing (commands 18, 19)
- Platform bouncing (commands 5-7)
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (-1, 5-7, 18, 19)
x: integer(), # Bounce X position
y: integer(), # Bounce Y position
unk: integer(), # Unknown short value
foothold: integer(), # Foothold after bounce
stance: integer(), # New stance/move action
duration: integer() # Movement duration in ms
}
defstruct [
:command,
:x,
:y,
:unk,
:foothold,
:stance,
:duration
]
end

View File

@@ -0,0 +1,29 @@
defmodule Odinsea.Game.Movement.Chair do
@moduledoc """
Chair movement - sitting on chairs/mounts.
Ported from Java ChairMovement.java
Used for:
- Sitting on chairs (commands 9, 12)
- Mount riding (command 13 in GMS)
- Special seating
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (9, 10, 11, 12, 13)
x: integer(), # Chair X position
y: integer(), # Chair Y position
unk: integer(), # Unknown short value
stance: integer(), # New stance/move action
duration: integer() # Movement duration in ms
}
defstruct [
:command,
:x,
:y,
:unk,
:stance,
:duration
]
end

View File

@@ -0,0 +1,22 @@
defmodule Odinsea.Game.Movement.ChangeEquip do
@moduledoc """
Change equip special awesome - equipment change during movement.
Ported from Java ChangeEquipSpecialAwesome.java
Used for:
- Changing equipment mid-movement (commands 10, 11)
- Quick gear switching
Note: Position is always 0,0 for this fragment type.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (10, 11)
wui: integer() # Weapon upgrade index or similar
}
defstruct [
:command,
:wui
]
end

View File

@@ -0,0 +1,40 @@
defmodule Odinsea.Game.Movement.JumpDown do
@moduledoc """
Jump down movement - falling through platforms.
Ported from Java JumpDownMovement.java
Used for:
- Jumping down through platforms (commands 13, 14)
- Controlled falling
Contains foothold information for landing detection.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (13, 14)
x: integer(), # Target X position
y: integer(), # Target Y position
vx: integer(), # X velocity
vy: integer(), # Y velocity
unk: integer(), # Unknown short value
foothold: integer(), # Target foothold ID
offset_x: integer(), # X offset
offset_y: integer(), # Y offset
stance: integer(), # New stance/move action
duration: integer() # Movement duration in ms
}
defstruct [
:command,
:x,
:y,
:vx,
:vy,
:unk,
:foothold,
:offset_x,
:offset_y,
:stance,
:duration
]
end

View File

@@ -0,0 +1,443 @@
defmodule Odinsea.Game.Movement.Path do
@moduledoc """
MovePath for mob movement (newer movement system).
Ported from Java MovePath.java
This is an alternative movement system used by mobs in newer
versions of MapleStory. It uses a more compact encoding.
Structure:
- Initial position (x, y, vx, vy)
- List of movement elements
- Optional passive data (keypad states, movement rect)
"""
import Bitwise
alias Odinsea.Net.Packet.In
defstruct [
:x, # Initial X position
:y, # Initial Y position
:vx, # Initial X velocity
:vy, # Initial Y velocity
elements: [], # List of MoveElem
key_pad_states: [], # Keypad states (passive mode)
move_rect: nil # Movement rectangle (passive mode)
]
@type t :: %__MODULE__{
x: integer() | nil,
y: integer() | nil,
vx: integer() | nil,
vy: integer() | nil,
elements: list(MoveElem.t()),
key_pad_states: list(integer()),
move_rect: map() | nil
}
defmodule MoveElem do
@moduledoc """
Individual movement element within a MovePath.
"""
@type t :: %__MODULE__{
attribute: integer(), # Movement type/attribute
x: integer(), # X position
y: integer(), # Y position
vx: integer(), # X velocity
vy: integer(), # Y velocity
fh: integer(), # Foothold
fall_start: integer(), # Fall start position
offset_x: integer(), # X offset
offset_y: integer(), # Y offset
sn: integer(), # Skill/stat number
move_action: integer(), # Move action/stance
elapse: integer() # Elapsed time
}
defstruct [
:attribute,
:x,
:y,
:vx,
:vy,
:fh,
:fall_start,
:offset_x,
:offset_y,
:sn,
:move_action,
:elapse
]
end
@doc """
Decodes a MovePath from a packet.
## Parameters
- packet: The incoming packet
- passive: Whether to decode passive data (keypad, rect)
## Returns
%MovePath{} struct with decoded data
"""
def decode(packet, passive \\ false) do
old_x = In.decode_short(packet)
old_y = In.decode_short(packet)
old_vx = In.decode_short(packet)
old_vy = In.decode_short(packet)
count = In.decode_byte(packet)
{elements, final_x, final_y, final_vx, final_vy, _fh_last} =
decode_elements(packet, count, old_x, old_y, old_vx, old_vy, [])
path = %__MODULE__{
x: old_x,
y: old_y,
vx: old_vx,
vy: old_vy,
elements: Enum.reverse(elements)
}
if passive do
{key_pad_states, move_rect} = decode_passive_data(packet)
%{path |
x: final_x,
y: final_y,
vx: final_vx,
vy: final_vy,
key_pad_states: key_pad_states,
move_rect: move_rect
}
else
%{path |
x: final_x,
y: final_y,
vx: final_vx,
vy: final_vy
}
end
end
@doc """
Encodes a MovePath to binary for packet output.
"""
def encode(%__MODULE__{} = path, _passive \\ false) do
elements_data = Enum.map_join(path.elements, &encode_element/1)
<<path.x::16-little, path.y::16-little,
path.vx::16-little, path.vy::16-little,
length(path.elements)::8,
elements_data::binary>>
end
@doc """
Gets the final position from the MovePath.
"""
def get_final_position(%__MODULE__{} = path) do
case List.last(path.elements) do
nil -> %{x: path.x, y: path.y}
elem -> %{x: elem.x, y: elem.y}
end
end
@doc """
Gets the final move action/stance from the MovePath.
"""
def get_final_action(%__MODULE__{} = path) do
case List.last(path.elements) do
nil -> 0
elem -> elem.move_action
end
end
@doc """
Gets the final foothold from the MovePath.
"""
def get_final_foothold(%__MODULE__{} = path) do
case List.last(path.elements) do
nil -> 0
elem -> elem.fh
end
end
# ============================================================================
# Private Functions
# ============================================================================
defp decode_elements(_packet, 0, old_x, old_y, old_vx, old_vy, acc),
do: {acc, old_x, old_y, old_vx, old_vy, 0}
defp decode_elements(packet, count, old_x, old_y, old_vx, old_vy, acc) do
attr = In.decode_byte(packet)
{elem, new_x, new_y, new_vx, new_vy, _fh_last} =
case attr do
# Absolute with foothold
a when a in [0, 6, 13, 15, 37, 38] ->
x = In.decode_short(packet)
y = In.decode_short(packet)
vx = In.decode_short(packet)
vy = In.decode_short(packet)
fh = In.decode_short(packet)
fall_start = if attr == 13, do: In.decode_short(packet), else: 0
offset_x = In.decode_short(packet)
offset_y = In.decode_short(packet)
elem = %MoveElem{
attribute: attr,
x: x,
y: y,
vx: vx,
vy: vy,
fh: fh,
fall_start: fall_start,
offset_x: offset_x,
offset_y: offset_y
}
{elem, x, y, vx, vy, fh}
# Velocity only
a when a in [1, 2, 14, 17, 19, 32, 33, 34, 35] ->
vx = In.decode_short(packet)
vy = In.decode_short(packet)
elem = %MoveElem{
attribute: attr,
x: old_x,
y: old_y,
vx: vx,
vy: vy,
fh: 0
}
{elem, old_x, old_y, vx, vy, 0}
# Position with foothold
a when a in [3, 4, 5, 7, 8, 9, 11] ->
x = In.decode_short(packet)
y = In.decode_short(packet)
fh = In.decode_short(packet)
elem = %MoveElem{
attribute: attr,
x: x,
y: y,
vx: 0,
vy: 0,
fh: fh
}
{elem, x, y, 0, 0, fh}
# Stat change
10 ->
sn = In.decode_byte(packet)
elem = %MoveElem{
attribute: attr,
sn: sn,
x: old_x,
y: old_y,
vx: 0,
vy: 0,
fh: 0,
elapse: 0,
move_action: 0
}
{elem, old_x, old_y, 0, 0, 0}
# Start fall down
12 ->
vx = In.decode_short(packet)
vy = In.decode_short(packet)
fall_start = In.decode_short(packet)
elem = %MoveElem{
attribute: attr,
x: old_x,
y: old_y,
vx: vx,
vy: vy,
fh: 0,
fall_start: fall_start
}
{elem, old_x, old_y, vx, vy, 0}
# Flying block
18 ->
x = In.decode_short(packet)
y = In.decode_short(packet)
vx = In.decode_short(packet)
vy = In.decode_short(packet)
elem = %MoveElem{
attribute: attr,
x: x,
y: y,
vx: vx,
vy: vy,
fh: 0
}
{elem, x, y, vx, vy, 0}
# No change (21-31)
a when a in [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] ->
elem = %MoveElem{
attribute: attr,
x: old_x,
y: old_y,
vx: old_vx,
vy: old_vy,
fh: 0
}
{elem, old_x, old_y, old_vx, old_vy, 0}
# Special case 36
36 ->
x = In.decode_short(packet)
y = In.decode_short(packet)
vx = In.decode_short(packet)
vy = In.decode_short(packet)
fh = In.decode_short(packet)
elem = %MoveElem{
attribute: attr,
x: x,
y: y,
vx: vx,
vy: vy,
fh: fh
}
{elem, x, y, vx, vy, fh}
# Unknown attribute - skip gracefully
_unknown ->
elem = %MoveElem{
attribute: attr,
x: old_x,
y: old_y,
vx: old_vx,
vy: old_vy,
fh: 0
}
{elem, old_x, old_y, old_vx, old_vy, 0}
end
# Read move action and elapse (except for stat change)
{elem, new_x, new_y, new_vx, new_vy} =
if attr != 10 do
move_action = In.decode_byte(packet)
elapse = In.decode_short(packet)
{%{elem |
move_action: move_action,
elapse: elapse
}, elem.x, elem.y, elem.vx, elem.vy}
else
{elem, new_x, new_y, new_vx, new_vy}
end
decode_elements(
packet,
count - 1,
new_x,
new_y,
new_vx,
new_vy,
[elem | acc]
)
end
defp decode_passive_data(packet) do
keys = In.decode_byte(packet)
key_pad_states =
if keys > 0 do
decode_keypad_states(packet, keys, 0, [])
else
[]
end
move_rect = %{
left: In.decode_short(packet),
top: In.decode_short(packet),
right: In.decode_short(packet),
bottom: In.decode_short(packet)
}
{Enum.reverse(key_pad_states), move_rect}
end
defp decode_keypad_states(_packet, 0, _value, acc), do: acc
defp decode_keypad_states(packet, remaining, value, acc) do
{new_value, decoded} =
if rem(length(acc), 2) != 0 do
{bsr(value, 4), band(value, 0x0F)}
else
v = In.decode_byte(packet)
{v, band(v, 0x0F)}
end
decode_keypad_states(packet, remaining - 1, new_value, [decoded | acc])
end
defp encode_element(%MoveElem{} = elem) do
attr = elem.attribute
base = <<attr::8>>
data =
case attr do
a when a in [0, 6, 13, 15, 37, 38] ->
<<elem.x::16-little, elem.y::16-little,
elem.vx::16-little, elem.vy::16-little,
elem.fh::16-little>> <>
if attr == 13 do
<<elem.fall_start::16-little>>
else
<<>>
end <>
<<elem.offset_x::16-little, elem.offset_y::16-little>>
a when a in [1, 2, 14, 17, 19, 32, 33, 34, 35] ->
<<elem.vx::16-little, elem.vy::16-little>>
a when a in [3, 4, 5, 7, 8, 9, 11] ->
<<elem.x::16-little, elem.y::16-little,
elem.fh::16-little>>
10 ->
<<elem.sn::8>>
12 ->
<<elem.vx::16-little, elem.vy::16-little,
elem.fall_start::16-little>>
18 ->
<<elem.x::16-little, elem.y::16-little,
elem.vx::16-little, elem.vy::16-little>>
a when a in [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] ->
<<>>
36 ->
<<elem.x::16-little, elem.y::16-little,
elem.vx::16-little, elem.vy::16-little,
elem.fh::16-little>>
_ ->
<<>>
end
footer =
if attr != 10 do
<<elem.move_action::8, elem.elapse::16-little>>
else
<<>>
end
base <> data <> footer
end
end

View File

@@ -0,0 +1,29 @@
defmodule Odinsea.Game.Movement.Relative do
@moduledoc """
Relative life movement - small position adjustments.
Ported from Java RelativeLifeMovement.java
Used for:
- Small adjustments (commands 1, 2)
- Float movements (commands 33, 34, 36)
- Fine-tuning position
Contains relative offset from current position.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (1, 2, 33, 34, 36)
x: integer(), # X offset (delta from current)
y: integer(), # Y offset (delta from current)
stance: integer(), # New stance/move action
duration: integer() # Movement duration in ms
}
defstruct [
:command,
:x,
:y,
:stance,
:duration
]
end

View File

@@ -0,0 +1,32 @@
defmodule Odinsea.Game.Movement.Teleport do
@moduledoc """
Teleport movement - instant position change.
Ported from Java TeleportMovement.java
Used for:
- Rush skills (command 3)
- Teleport (command 4)
- Assassinate (command 8)
- Special skills (commands 100, 101)
Note: Duration is always 0 for teleports.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (3, 4, 8, 100, 101)
x: integer(), # Target X position
y: integer(), # Target Y position
vx: integer(), # X velocity (visual effect)
vy: integer(), # Y velocity (visual effect)
stance: integer() # New stance/move action
}
defstruct [
:command,
:x,
:y,
:vx,
:vy,
:stance
]
end

View File

@@ -0,0 +1,36 @@
defmodule Odinsea.Game.Movement.Unknown do
@moduledoc """
Unknown movement type - placeholder for unhandled commands.
Ported from Java UnknownMovement.java
Used for:
- Command 32 (unknown structure)
- Any future/unrecognized movement types
Parses generic structure that may match unknown commands.
"""
@type t :: %__MODULE__{
command: integer(), # Movement command type (32, or unknown)
unk: integer(), # Unknown short value
x: integer(), # X position
y: integer(), # Y position
vx: integer(), # X velocity
vy: integer(), # Y velocity
foothold: integer(), # Foothold
stance: integer(), # New stance/move action
duration: integer() # Movement duration in ms
}
defstruct [
:command,
:unk,
:x,
:y,
:vx,
:vy,
:foothold,
:stance,
:duration
]
end