diff --git a/korangar/src/main.rs b/korangar/src/main.rs index 908487bc..a03f460e 100644 --- a/korangar/src/main.rs +++ b/korangar/src/main.rs @@ -68,7 +68,7 @@ use korangar_util::texture_atlas::AtlasAllocation; #[cfg(not(feature = "debug"))] use ragnarok_packets::handler::NoPacketCallback; use ragnarok_packets::{ - BuyShopItemsResult, CharacterId, CharacterInformation, CharacterServerInformation, DisappearanceReason, Friend, HotbarSlot, + BuyShopItemsResult, CharacterId, CharacterInformation, CharacterServerInformation, Direction, DisappearanceReason, Friend, HotbarSlot, SellItemsResult, SkillId, SkillType, TilePosition, UnitId, WorldPosition, }; use renderer::InterfaceRenderer; @@ -964,7 +964,7 @@ impl Client { &mut self.path_finder, saved_login_data.account_id, character_information, - WorldPosition { x: 0, y: 0 }, + WorldPosition::origin(), client_tick, ); let player = Entity::Player(player); @@ -1589,6 +1589,7 @@ impl Client { let _ = self.networking_system.player_move(WorldPosition { x: destination.x, y: destination.y, + direction: Direction::N, }); } } @@ -1604,6 +1605,7 @@ impl Client { WorldPosition { x: position.x, y: position.y, + direction: Direction::N, } }), _ => Ok(()), diff --git a/korangar/src/world/animation/mod.rs b/korangar/src/world/animation/mod.rs index 3ac2055e..32762ea2 100644 --- a/korangar/src/world/animation/mod.rs +++ b/korangar/src/world/animation/mod.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use cgmath::{Array, Matrix4, Point3, Transform, Vector2, Vector3, Zero}; use korangar_interface::elements::PrototypeElement; use korangar_util::container::Cacheable; -use ragnarok_packets::EntityId; +use ragnarok_packets::{Direction, EntityId}; #[cfg(feature = "debug")] use crate::graphics::DebugRectangleInstruction; @@ -82,10 +82,10 @@ impl Default for AnimationFramePart { } impl AnimationData { - pub fn get_frame(&self, animation_state: &AnimationState, camera: &dyn Camera, head_direction: usize) -> &AnimationFrame { + pub fn get_frame(&self, animation_state: &AnimationState, camera: &dyn Camera, direction: Direction) -> &AnimationFrame { let camera_direction = camera.camera_direction(); - let direction = (camera_direction + head_direction) % 8; - let animation_action_index = animation_state.action as usize * 8 + direction; + let direction_usize = (camera_direction + usize::from(direction)) & 7; + let animation_action_index = animation_state.action as usize * 8 + direction_usize; let delay_index = animation_action_index % self.delays.len(); let animation_index = animation_action_index % self.animations.len(); @@ -143,9 +143,9 @@ impl AnimationData { entity_id: EntityId, entity_position: Point3, animation_state: &AnimationState, - head_direction: usize, + direction: Direction, ) { - let frame = self.get_frame(animation_state, camera, head_direction); + let frame = self.get_frame(animation_state, camera, direction); let world_matrix = self.calculate_world_matrix(camera, frame, entity_position); for (index, frame_part) in frame.frame_parts.iter().enumerate() { @@ -187,11 +187,11 @@ impl AnimationData { camera: &dyn Camera, entity_position: Point3, animation_state: &AnimationState, - head_direction: usize, + direction: Direction, color_external: Color, color_internal: Color, ) { - let frame = self.get_frame(animation_state, camera, head_direction); + let frame = self.get_frame(animation_state, camera, direction); let world_matrix = self.calculate_world_matrix(camera, frame, entity_position); instructions.push(DebugRectangleInstruction { world: world_matrix, diff --git a/korangar/src/world/entity/mod.rs b/korangar/src/world/entity/mod.rs index b9e37727..c22c5466 100644 --- a/korangar/src/world/entity/mod.rs +++ b/korangar/src/world/entity/mod.rs @@ -11,7 +11,7 @@ use korangar_networking::EntityData; use korangar_util::pathing::{PathFinder, MAX_WALK_PATH_SIZE}; #[cfg(feature = "debug")] use korangar_util::texture_atlas::AtlasAllocation; -use ragnarok_packets::{AccountId, CharacterInformation, ClientTick, EntityId, Sex, StatusType, WorldPosition}; +use ragnarok_packets::{AccountId, CharacterInformation, ClientTick, Direction, EntityId, Sex, StatusType, WorldPosition}; #[cfg(feature = "debug")] use wgpu::{BufferUsages, Device, Queue}; @@ -115,6 +115,7 @@ pub struct Common { pub health_points: usize, pub maximum_health_points: usize, pub movement_speed: usize, + pub direction: Direction, pub head_direction: usize, pub sex: Sex, @@ -345,6 +346,7 @@ impl Common { let grid_position = Vector2::new(grid_position.x, grid_position.y); let position = map.get_world_position(grid_position); let head_direction = entity_data.head_direction; + let direction = entity_data.position.direction; let movement_speed = entity_data.movement_speed as usize; let health_points = entity_data.health_points as usize; @@ -375,6 +377,7 @@ impl Common { position, entity_id, job_id, + direction, head_direction, sex, active_movement, @@ -414,7 +417,7 @@ impl Common { self.update_movement(map, client_tick); self.animation_state.update(client_tick); - let frame = self.animation_data.get_frame(&self.animation_state, camera, self.head_direction); + let frame = self.animation_data.get_frame(&self.animation_state, camera, self.direction); match frame.event { Some(ActionEvent::Sound { key }) => { self.sound_state.update(audio_engine, self.position, key, client_tick); @@ -449,17 +452,7 @@ impl Common { let array = last_step_position - next_step_position; let array: &[isize; 2] = array.as_ref(); - self.head_direction = match array { - [0, 1] => 0, - [1, 1] => 1, - [1, 0] => 2, - [1, -1] => 3, - [0, -1] => 4, - [-1, -1] => 5, - [-1, 0] => 6, - [-1, 1] => 7, - _ => panic!("impossible step"), - }; + self.direction = (*array).into(); let last_step_position = map.get_world_position(last_step.arrival_position).to_vec(); let next_step_position = map.get_world_position(next_step.arrival_position).to_vec(); @@ -762,7 +755,7 @@ impl Common { self.entity_id, self.position, &self.animation_state, - self.head_direction, + self.direction, ); } @@ -773,7 +766,7 @@ impl Common { camera, self.position, &self.animation_state, - self.head_direction, + self.direction, Color::rgb_u8(255, 0, 0), Color::rgb_u8(0, 255, 0), ); diff --git a/ragnarok_packets/src/lib.rs b/ragnarok_packets/src/lib.rs index fc98408c..0d3309ff 100644 --- a/ragnarok_packets/src/lib.rs +++ b/ragnarok_packets/src/lib.rs @@ -11,7 +11,7 @@ pub use ragnarok_procedural::{CharacterServer, ClientPacket, LoginServer, MapSer #[cfg(not(feature = "derive"))] use ragnarok_procedural::{CharacterServer, ClientPacket, LoginServer, MapServer, Packet, ServerPacket}; -pub use self::position::{WorldPosition, WorldPosition2}; +pub use self::position::{Direction, WorldPosition, WorldPosition2}; // To make proc macros work in korangar_interface. extern crate self as ragnarok_packets; diff --git a/ragnarok_packets/src/position.rs b/ragnarok_packets/src/position.rs index 93f696ab..14d8cdd4 100644 --- a/ragnarok_packets/src/position.rs +++ b/ragnarok_packets/src/position.rs @@ -1,15 +1,77 @@ -use ragnarok_bytes::{ByteReader, ConversionResult, FromBytes, ToBytes}; +use ragnarok_bytes::{ByteConvertable, ByteReader, ConversionResult, FromBytes, ToBytes}; + +#[derive(Debug, Copy, Clone, ByteConvertable)] +#[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] +pub enum Direction { + N = 0, + NE = 1, + E = 2, + SE = 3, + S = 4, + SW = 5, + W = 6, + NW = 7, +} + +impl From for usize { + fn from(value: Direction) -> Self { + value as usize + } +} + +impl From for Direction { + fn from(value: usize) -> Self { + let value = value & 7; + + match value { + 0 => Direction::N, + 1 => Direction::NE, + 2 => Direction::E, + 3 => Direction::SE, + 4 => Direction::S, + 5 => Direction::SW, + 6 => Direction::W, + 7 => Direction::NW, + _ => unreachable!(), + } + } +} + +impl From<[isize; 2]> for Direction { + fn from(value: [isize; 2]) -> Self { + match value { + [0, 1] => Direction::N, + [1, 1] => Direction::NE, + [1, 0] => Direction::E, + [1, -1] => Direction::SE, + [0, -1] => Direction::S, + [-1, -1] => Direction::SW, + [-1, 0] => Direction::W, + [-1, 1] => Direction::NW, + _ => panic!("impossible direction"), + } + } +} #[derive(Debug, Clone, Copy)] #[cfg_attr(feature = "interface", derive(korangar_interface::elements::PrototypeElement))] pub struct WorldPosition { pub x: usize, pub y: usize, + pub direction: Direction, } impl WorldPosition { - pub fn new(x: usize, y: usize) -> Self { - Self { x, y } + pub fn new(x: usize, y: usize, direction: Direction) -> Self { + Self { x, y, direction } + } + + pub fn origin() -> Self { + Self { + x: 0, + y: 0, + direction: Direction::N, + } } } @@ -19,19 +81,25 @@ impl FromBytes for WorldPosition { let x = (coordinates[1] >> 6) | (coordinates[0] << 2); let y = (coordinates[2] >> 4) | ((coordinates[1] & 0b111111) << 4); - //let direction = ... + let mut direction = coordinates[2] & 0b1111; + direction = (8 - direction + 4) & 7; - Ok(Self { x, y }) + Ok(Self { + x, + y, + direction: direction.into(), + }) } } impl ToBytes for WorldPosition { fn to_bytes(&self) -> ConversionResult> { let mut coordinates = vec![0, 0, 0]; + let direction = (8 - usize::from(self.direction) + 4) & 7; coordinates[0] = (self.x >> 2) as u8; coordinates[1] = ((self.x << 6) as u8) | (((self.y >> 4) & 0x3F) as u8); - coordinates[2] = (self.y << 4) as u8; + coordinates[2] = (self.y << 4) as u8 | (direction & 0xF) as u8; Ok(coordinates) } @@ -44,18 +112,33 @@ pub struct WorldPosition2 { pub y1: usize, pub x2: usize, pub y2: usize, + pub unknown: usize, } impl WorldPosition2 { pub fn new(x1: usize, y1: usize, x2: usize, y2: usize) -> Self { - Self { x1, y1, x2, y2 } + Self { + x1, + y1, + x2, + y2, + unknown: 0, + } } pub fn to_origin_destination(self) -> (WorldPosition, WorldPosition) { - (WorldPosition { x: self.x1, y: self.y1 }, WorldPosition { - x: self.x2, - y: self.y2, - }) + ( + WorldPosition { + x: self.x1, + y: self.y1, + direction: Direction::N, + }, + WorldPosition { + x: self.x2, + y: self.y2, + direction: Direction::N, + }, + ) } } @@ -67,9 +150,9 @@ impl FromBytes for WorldPosition2 { let y1 = (coordinates[2] >> 4) | ((coordinates[1] & 0b111111) << 4); let x2 = (coordinates[3] >> 2) | ((coordinates[2] & 0b1111) << 6); let y2 = coordinates[4] | ((coordinates[3] & 0b11) << 8); - //let direction = ... + let unknown = coordinates[5]; - Ok(Self { x1, y1, x2, y2 }) + Ok(Self { x1, y1, x2, y2, unknown }) } } @@ -82,6 +165,7 @@ impl ToBytes for WorldPosition2 { bytes[2] = ((self.y1 << 4) as u8) | ((self.x2 >> 6) as u8); bytes[3] = ((self.x2 << 2) as u8) | ((self.y2 >> 8) as u8); bytes[4] = self.y2 as u8; + bytes[5] = self.unknown as u8; Ok(bytes) } @@ -95,10 +179,9 @@ mod conversion { #[test] fn world_position() { - // Since we don't save the orientation when deserializing, this is a lossy - // operation. So we construct some test cases that igonore the bits in - // question. - let cases = [[255, 0, 0], [0, 255, 0], [0, 0, 240]]; + // The direction must be between 0 and 7 inclusive. + let direction = [0, 3, 7]; + let cases = [[255, 0, 0 + direction[0]], [0, 255, 0 + direction[1]], [0, 0, 240 + direction[2]]]; for case in cases { let mut byte_reader = ragnarok_bytes::ByteReader::without_metadata(&case); @@ -112,15 +195,13 @@ mod conversion { #[test] fn world_position_2() { - // Since we don't save the orientation when deserializing, this is a lossy - // operation. So we construct some test cases that igonore the bits in - // question. let cases = [ [255, 0, 0, 0, 0, 0], [0, 255, 0, 0, 0, 0], [0, 0, 255, 0, 0, 0], [0, 0, 0, 255, 0, 0], [0, 0, 0, 0, 255, 0], + [0, 0, 0, 0, 0, 255], ]; for case in cases {