defmodule Odinsea.Game.Movement do @moduledoc """ Movement parsing and validation for players, mobs, pets, summons, and dragons. Ported from Java MovementParse.java. Movement types (kind): - 1: Player - 2: Mob - 3: Pet - 4: Summon - 5: Dragon This is a SIMPLIFIED implementation for now. The full Java version has complex parsing for different movement command types. We'll expand this as needed. """ require Logger alias Odinsea.Net.Packet.In alias Odinsea.Game.Character.Position @doc """ Parses movement data from a packet. Returns {:ok, movements} or {:error, reason}. For now, this returns a simplified structure. The full implementation would parse all movement fragment types. """ def parse_movement(packet, _kind) do num_commands = In.decode_byte(packet) # For now, just skip through the movement data and extract final position # TODO: Implement full movement parsing with all command types case extract_final_position(packet, num_commands) do {:ok, position} -> {:ok, %{num_commands: num_commands, final_position: position}} :error -> {:error, :invalid_movement} end end @doc """ Updates an entity's position from movement data. """ def update_position(_movements, character_id) do # TODO: Implement position update logic # For now, just return ok Logger.debug("Update position for character #{character_id}") :ok end # ============================================================================ # Private Functions # ============================================================================ # Extract the final position from movement data # This is a TEMPORARY simplification - we just read through the movement # commands and try to extract the last absolute position defp extract_final_position(packet, num_commands) do try do final_pos = parse_commands(packet, num_commands, nil) {:ok, final_pos || %{x: 0, y: 0, stance: 0, foothold: 0}} rescue _ -> :error end end defp parse_commands(_packet, 0, last_position) do last_position end defp parse_commands(packet, remaining, last_position) do command = In.decode_byte(packet) new_position = case command do # Absolute movement commands - extract position cmd when cmd in [0, 37, 38, 39, 40, 41, 42] -> x = In.decode_short(packet) y = In.decode_short(packet) _xwobble = In.decode_short(packet) _ywobble = In.decode_short(packet) _unk = In.decode_short(packet) _xoffset = In.decode_short(packet) _yoffset = In.decode_short(packet) stance = In.decode_byte(packet) _duration = In.decode_short(packet) %{x: x, y: y, stance: stance, foothold: 0} # Relative movement - skip for now cmd when cmd in [1, 2, 33, 34, 36] -> _xmod = In.decode_short(packet) _ymod = In.decode_short(packet) _stance = In.decode_byte(packet) _duration = In.decode_short(packet) last_position # Teleport movement cmd when cmd in [3, 4, 8, 100, 101] -> x = In.decode_short(packet) y = In.decode_short(packet) _xwobble = In.decode_short(packet) _ywobble = In.decode_short(packet) stance = In.decode_byte(packet) %{x: x, y: y, stance: stance, foothold: 0} # Chair movement cmd when cmd in [9, 12] -> x = In.decode_short(packet) y = In.decode_short(packet) _unk = In.decode_short(packet) stance = In.decode_byte(packet) _duration = In.decode_short(packet) %{x: x, y: y, stance: stance, foothold: 0} # Aran combat step cmd when cmd in [21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 35] -> _stance = In.decode_byte(packet) _unk = In.decode_short(packet) last_position # Jump down cmd when cmd in [13, 14] -> # Simplified - just skip the data x = In.decode_short(packet) y = In.decode_short(packet) _xwobble = In.decode_short(packet) _ywobble = In.decode_short(packet) _unk = In.decode_short(packet) _fh = In.decode_short(packet) _xoffset = In.decode_short(packet) _yoffset = In.decode_short(packet) stance = In.decode_byte(packet) _duration = In.decode_short(packet) %{x: x, y: y, stance: stance, foothold: 0} # Unknown/unhandled - log and skip _ -> Logger.warning("Unhandled movement command: #{command}") last_position end parse_commands(packet, remaining - 1, new_position || last_position) end end