mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
start adding HitResultComponent
This commit is contained in:
parent
c5237daf51
commit
0468379e59
20 changed files with 354 additions and 122 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -316,6 +316,7 @@ dependencies = [
|
|||
"azalea-nbt",
|
||||
"azalea-registry",
|
||||
"bevy_ecs",
|
||||
"num-traits",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
|
|
|
@ -404,7 +404,10 @@ pub fn make_block_states(input: TokenStream) -> TokenStream {
|
|||
&to_pascal_case(&block.name.to_string()),
|
||||
proc_macro2::Span::call_site(),
|
||||
);
|
||||
let block_struct_name = Ident::new(&block_name_pascal_case.to_string(), proc_macro2::Span::call_site());
|
||||
let block_struct_name = Ident::new(
|
||||
&block_name_pascal_case.to_string(),
|
||||
proc_macro2::Span::call_site(),
|
||||
);
|
||||
|
||||
let mut from_block_to_state_match_inner = quote! {};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent,
|
||||
LocalPlayer, PhysicsState, SendPacketEvent,
|
||||
},
|
||||
movement::PlayerMovePlugin,
|
||||
movement::{LastSentLookDirection, PlayerMovePlugin},
|
||||
packet_handling::{self, PacketHandlerPlugin, PacketReceiver},
|
||||
player::retroactively_add_game_profile_component,
|
||||
task_pool::TaskPoolPlugin,
|
||||
|
@ -100,7 +100,16 @@ pub struct Client {
|
|||
pub struct ClientInformation(ServerboundClientInformationPacket);
|
||||
|
||||
/// A component that contains a map of player UUIDs to their information in the
|
||||
/// tab list
|
||||
/// tab list.
|
||||
///
|
||||
/// ```
|
||||
/// # fn example(client: &azalea_client::Client) {
|
||||
/// let tab_list = client.component::<TabList>();
|
||||
/// println!("Online players:");
|
||||
/// for (uuid, player_info) in tab_list {
|
||||
/// println!("- {} ({}ms)", player_info.profile.name, player_info.latency);
|
||||
/// }
|
||||
/// # }
|
||||
#[derive(Component, Clone, Debug, Deref, DerefMut, Default)]
|
||||
pub struct TabList(HashMap<Uuid, PlayerInfo>);
|
||||
|
||||
|
@ -253,6 +262,7 @@ impl Client {
|
|||
client_information: ClientInformation::default(),
|
||||
tab_list: TabList::default(),
|
||||
current_sequence_number: CurrentSequenceNumber::default(),
|
||||
last_sent_direction: LastSentLookDirection::default(),
|
||||
_local: Local,
|
||||
});
|
||||
|
||||
|
@ -496,6 +506,7 @@ pub struct JoinedClientBundle {
|
|||
pub client_information: ClientInformation,
|
||||
pub tab_list: TabList,
|
||||
pub current_sequence_number: CurrentSequenceNumber,
|
||||
pub last_sent_direction: LastSentLookDirection,
|
||||
pub _local: Local,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use azalea_core::{BlockPos, Direction};
|
||||
use azalea_core::{BlockPos, Direction, GameMode};
|
||||
use azalea_protocol::packets::game::{
|
||||
serverbound_interact_packet::InteractionHand,
|
||||
serverbound_use_item_on_packet::{BlockHitResult, ServerboundUseItemOnPacket},
|
||||
};
|
||||
use azalea_world::entity::{view_vector, EyeHeight, LookDirection, Position};
|
||||
use bevy_app::{App, Plugin};
|
||||
use bevy_ecs::{component::Component, entity::Entity, event::EventReader, system::Query};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use log::warn;
|
||||
|
||||
use crate::{Client, LocalPlayer};
|
||||
use crate::{local_player::LocalGameMode, Client, LocalPlayer, PlayerInfo};
|
||||
|
||||
/// A plugin that allows clients to interact with blocks in the world.
|
||||
pub struct InteractPlugin;
|
||||
impl Plugin for InteractPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_event::<BlockInteractEvent>()
|
||||
.add_system(handle_block_interact_event);
|
||||
.add_systems((handle_block_interact_event, update_hit_result_component));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,10 +45,14 @@ pub struct BlockInteractEvent {
|
|||
pub position: BlockPos,
|
||||
}
|
||||
|
||||
/// The number of changes this client has made to blocks.
|
||||
/// A component that contains the number of changes this client has made to
|
||||
/// blocks.
|
||||
#[derive(Component, Copy, Clone, Debug, Default, Deref, DerefMut)]
|
||||
pub struct CurrentSequenceNumber(u32);
|
||||
|
||||
#[derive(Component, Clone, Debug, Deref, DerefMut)]
|
||||
pub struct HitResultComponent(BlockHitResult);
|
||||
|
||||
fn handle_block_interact_event(
|
||||
mut events: EventReader<BlockInteractEvent>,
|
||||
mut query: Query<(&LocalPlayer, &mut CurrentSequenceNumber)>,
|
||||
|
@ -80,3 +85,23 @@ fn handle_block_interact_event(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn update_hit_result_component(
|
||||
mut query: Query<(
|
||||
&mut HitResultComponent,
|
||||
&LocalGameMode,
|
||||
&Position,
|
||||
&EyeHeight,
|
||||
&LookDirection,
|
||||
)>,
|
||||
) {
|
||||
for (hit_result, game_mode, position, eye_height, look_direction) in &mut query {
|
||||
let pick_range = if game_mode.current == GameMode::Creative {
|
||||
6.
|
||||
} else {
|
||||
4.5
|
||||
};
|
||||
let view_vector = view_vector(look_direction);
|
||||
let end_position = **position + view_vector * pick_range;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::{io, sync::Arc};
|
||||
|
||||
use azalea_auth::game_profile::GameProfile;
|
||||
use azalea_core::ChunkPos;
|
||||
use azalea_core::{ChunkPos, GameMode};
|
||||
use azalea_protocol::packets::game::ServerboundGamePacket;
|
||||
use azalea_world::{
|
||||
entity::{self, Dead},
|
||||
|
@ -74,9 +74,16 @@ pub struct GameProfileComponent(pub GameProfile);
|
|||
|
||||
/// Marks a [`LocalPlayer`] that's in a loaded chunk. This is updated at the
|
||||
/// beginning of every tick.
|
||||
#[derive(Component)]
|
||||
#[derive(Component, Clone, Debug, Copy)]
|
||||
pub struct LocalPlayerInLoadedChunk;
|
||||
|
||||
/// The gamemode of a local player. For a non-local player, you can look up the
|
||||
/// player in the [`TabList`].
|
||||
#[derive(Component, Clone, Debug, Copy)]
|
||||
pub struct LocalGameMode {
|
||||
pub current: GameMode,
|
||||
}
|
||||
|
||||
impl LocalPlayer {
|
||||
/// Create a new `LocalPlayer`.
|
||||
pub fn new(
|
||||
|
|
|
@ -16,6 +16,7 @@ use azalea_world::{
|
|||
};
|
||||
use bevy_app::{App, CoreSchedule, IntoSystemAppConfigs, Plugin};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
event::EventReader,
|
||||
query::With,
|
||||
|
@ -84,18 +85,26 @@ impl Client {
|
|||
**jumping_ref
|
||||
}
|
||||
|
||||
/// Sets your rotation. `y_rot` is yaw (looking to the side), `x_rot` is
|
||||
/// pitch (looking up and down). You can get these numbers from the vanilla
|
||||
/// f3 screen.
|
||||
/// Sets the direction the client is looking. `y_rot` is yaw (looking to the
|
||||
/// side), `x_rot` is pitch (looking up and down). You can get these
|
||||
/// numbers from the vanilla f3 screen.
|
||||
/// `y_rot` goes from -180 to 180, and `x_rot` goes from -90 to 90.
|
||||
pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) {
|
||||
pub fn set_direction(&mut self, y_rot: f32, x_rot: f32) {
|
||||
let mut ecs = self.ecs.lock();
|
||||
let mut physics = self.query::<&mut entity::Physics>(&mut ecs);
|
||||
let mut look_direction = self.query::<&mut entity::LookDirection>(&mut ecs);
|
||||
|
||||
entity::set_rotation(&mut physics, y_rot, x_rot);
|
||||
(look_direction.y_rot, look_direction.x_rot) = (y_rot, x_rot);
|
||||
}
|
||||
}
|
||||
|
||||
/// A component that contains the look direction that was last sent over the
|
||||
/// network.
|
||||
#[derive(Debug, Component, Clone, Default)]
|
||||
pub struct LastSentLookDirection {
|
||||
pub x_rot: f32,
|
||||
pub y_rot: f32,
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn send_position(
|
||||
mut query: Query<
|
||||
|
@ -106,6 +115,8 @@ pub(crate) fn send_position(
|
|||
&entity::Position,
|
||||
&mut entity::LastSentPosition,
|
||||
&mut entity::Physics,
|
||||
&entity::LookDirection,
|
||||
&mut LastSentLookDirection,
|
||||
&entity::metadata::Sprinting,
|
||||
),
|
||||
&LocalPlayerInLoadedChunk,
|
||||
|
@ -118,6 +129,8 @@ pub(crate) fn send_position(
|
|||
position,
|
||||
mut last_sent_position,
|
||||
mut physics,
|
||||
direction,
|
||||
mut last_direction,
|
||||
sprinting,
|
||||
) in query.iter_mut()
|
||||
{
|
||||
|
@ -130,8 +143,8 @@ pub(crate) fn send_position(
|
|||
let x_delta = position.x - last_sent_position.x;
|
||||
let y_delta = position.y - last_sent_position.y;
|
||||
let z_delta = position.z - last_sent_position.z;
|
||||
let y_rot_delta = (physics.y_rot - physics.y_rot_last) as f64;
|
||||
let x_rot_delta = (physics.x_rot - physics.x_rot_last) as f64;
|
||||
let y_rot_delta = (direction.y_rot - last_direction.y_rot) as f64;
|
||||
let x_rot_delta = (direction.x_rot - last_direction.x_rot) as f64;
|
||||
|
||||
physics_state.position_remainder += 1;
|
||||
|
||||
|
@ -140,19 +153,19 @@ pub(crate) fn send_position(
|
|||
let sending_position = ((x_delta.powi(2) + y_delta.powi(2) + z_delta.powi(2))
|
||||
> 2.0e-4f64.powi(2))
|
||||
|| physics_state.position_remainder >= 20;
|
||||
let sending_rotation = y_rot_delta != 0.0 || x_rot_delta != 0.0;
|
||||
let sending_direction = y_rot_delta != 0.0 || x_rot_delta != 0.0;
|
||||
|
||||
// if self.is_passenger() {
|
||||
// TODO: posrot packet for being a passenger
|
||||
// }
|
||||
let packet = if sending_position && sending_rotation {
|
||||
let packet = if sending_position && sending_direction {
|
||||
Some(
|
||||
ServerboundMovePlayerPosRotPacket {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
z: position.z,
|
||||
x_rot: physics.x_rot,
|
||||
y_rot: physics.y_rot,
|
||||
x_rot: direction.x_rot,
|
||||
y_rot: direction.y_rot,
|
||||
on_ground: physics.on_ground,
|
||||
}
|
||||
.get(),
|
||||
|
@ -167,11 +180,11 @@ pub(crate) fn send_position(
|
|||
}
|
||||
.get(),
|
||||
)
|
||||
} else if sending_rotation {
|
||||
} else if sending_direction {
|
||||
Some(
|
||||
ServerboundMovePlayerRotPacket {
|
||||
x_rot: physics.x_rot,
|
||||
y_rot: physics.y_rot,
|
||||
x_rot: direction.x_rot,
|
||||
y_rot: direction.y_rot,
|
||||
on_ground: physics.on_ground,
|
||||
}
|
||||
.get(),
|
||||
|
@ -191,9 +204,9 @@ pub(crate) fn send_position(
|
|||
**last_sent_position = **position;
|
||||
physics_state.position_remainder = 0;
|
||||
}
|
||||
if sending_rotation {
|
||||
physics.y_rot_last = physics.y_rot;
|
||||
physics.x_rot_last = physics.x_rot;
|
||||
if sending_direction {
|
||||
last_direction.y_rot = direction.y_rot;
|
||||
last_direction.x_rot = direction.x_rot;
|
||||
}
|
||||
|
||||
physics.last_on_ground = physics.on_ground;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashSet, io::Cursor, sync::Arc};
|
||||
|
||||
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
|
||||
use azalea_core::{ChunkPos, GameMode, ResourceLocation, Vec3};
|
||||
use azalea_protocol::{
|
||||
connect::{ReadConnection, WriteConnection},
|
||||
packets::game::{
|
||||
|
@ -16,7 +16,7 @@ use azalea_protocol::{
|
|||
use azalea_world::{
|
||||
entity::{
|
||||
metadata::{apply_metadata, Health, PlayerMetadataBundle},
|
||||
set_rotation, Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition,
|
||||
Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition, LookDirection,
|
||||
MinecraftEntityId, Physics, PlayerBundle, Position, WorldName,
|
||||
},
|
||||
entity::{LoadedBy, RelativeEntityUpdate},
|
||||
|
@ -40,7 +40,7 @@ use crate::{
|
|||
client::TabList,
|
||||
disconnect::DisconnectEvent,
|
||||
inventory::{ClientSideCloseContainerEvent, InventoryComponent},
|
||||
local_player::{GameProfileComponent, LocalPlayer},
|
||||
local_player::{GameProfileComponent, LocalGameMode, LocalPlayer},
|
||||
ClientInformation, PlayerInfo,
|
||||
};
|
||||
|
||||
|
@ -362,12 +362,13 @@ fn process_packet_events(ecs: &mut World) {
|
|||
Query<(
|
||||
&mut LocalPlayer,
|
||||
&mut Physics,
|
||||
&mut LookDirection,
|
||||
&mut Position,
|
||||
&mut LastSentPosition,
|
||||
)>,
|
||||
> = SystemState::new(ecs);
|
||||
let mut query = system_state.get_mut(ecs);
|
||||
let Ok(( local_player, mut physics, mut position, mut last_sent_position)) =
|
||||
let Ok((local_player, mut physics, mut direction, mut position, mut last_sent_position)) =
|
||||
query.get_mut(player_entity) else {
|
||||
continue;
|
||||
};
|
||||
|
@ -403,10 +404,10 @@ fn process_packet_events(ecs: &mut World) {
|
|||
let mut y_rot = p.y_rot;
|
||||
let mut x_rot = p.x_rot;
|
||||
if p.relative_arguments.x_rot {
|
||||
x_rot += physics.x_rot;
|
||||
x_rot += direction.x_rot;
|
||||
}
|
||||
if p.relative_arguments.y_rot {
|
||||
y_rot += physics.y_rot;
|
||||
y_rot += direction.y_rot;
|
||||
}
|
||||
|
||||
physics.delta = Vec3 {
|
||||
|
@ -417,7 +418,7 @@ fn process_packet_events(ecs: &mut World) {
|
|||
// we call a function instead of setting the fields ourself since the
|
||||
// function makes sure the rotations stay in their
|
||||
// ranges
|
||||
set_rotation(&mut physics, y_rot, x_rot);
|
||||
(direction.y_rot, direction.x_rot) = (y_rot, x_rot);
|
||||
// TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means
|
||||
// so investigate that ig
|
||||
let new_pos = Vec3 {
|
||||
|
@ -866,7 +867,22 @@ fn process_packet_events(ecs: &mut World) {
|
|||
}
|
||||
}
|
||||
ClientboundGamePacket::GameEvent(p) => {
|
||||
use azalea_protocol::packets::game::clientbound_game_event_packet::EventType;
|
||||
|
||||
debug!("Got game event packet {:?}", p);
|
||||
|
||||
match p.event {
|
||||
EventType::ChangeGameMode => {
|
||||
let mut system_state: SystemState<Query<&mut LocalGameMode>> =
|
||||
SystemState::new(ecs);
|
||||
let mut query = system_state.get_mut(ecs);
|
||||
let mut local_game_mode = query.get_mut(player_entity).unwrap();
|
||||
if let Some(new_game_mode) = GameMode::from_id(p.param as u8) {
|
||||
local_game_mode.current = new_game_mode;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ClientboundGamePacket::LevelParticles(p) => {
|
||||
debug!("Got level particles packet {:?}", p);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use azalea_auth::game_profile::GameProfile;
|
||||
use azalea_chat::FormattedText;
|
||||
use azalea_core::GameType;
|
||||
use azalea_core::GameMode;
|
||||
use azalea_world::entity::EntityInfos;
|
||||
use bevy_ecs::{
|
||||
event::EventReader,
|
||||
|
@ -18,7 +18,10 @@ pub struct PlayerInfo {
|
|||
pub profile: GameProfile,
|
||||
/// The player's UUID.
|
||||
pub uuid: Uuid,
|
||||
pub gamemode: GameType,
|
||||
/// The current gamemode of the player, like survival or creative.
|
||||
pub gamemode: GameMode,
|
||||
/// The player's latency in milliseconds. The bars in the tab screen depend
|
||||
/// on this.
|
||||
pub latency: i32,
|
||||
/// The player's display name in the tab list, but only if it's different
|
||||
/// from the player's normal username. Use `player_info.profile.name` to get
|
||||
|
|
|
@ -14,6 +14,7 @@ azalea-chat = { path = "../azalea-chat", version = "^0.6.0" }
|
|||
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
|
||||
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
|
||||
bevy_ecs = { version = "0.10.0", default-features = false, optional = true }
|
||||
num-traits = "0.2.15"
|
||||
uuid = "^1.1.2"
|
||||
|
||||
[features]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use azalea_buf::{BufReadError, McBufReadable, McBufWritable};
|
||||
use std::io::{Cursor, Write};
|
||||
|
||||
#[derive(Hash, Copy, Clone, Debug, Default)]
|
||||
pub enum GameType {
|
||||
/// A Minecraft gamemode, like survival or creative.
|
||||
#[derive(Hash, Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub enum GameMode {
|
||||
#[default]
|
||||
Survival,
|
||||
Creative,
|
||||
|
@ -10,30 +11,30 @@ pub enum GameType {
|
|||
Spectator,
|
||||
}
|
||||
|
||||
impl GameType {
|
||||
impl GameMode {
|
||||
pub fn to_id(&self) -> u8 {
|
||||
match self {
|
||||
GameType::Survival => 0,
|
||||
GameType::Creative => 1,
|
||||
GameType::Adventure => 2,
|
||||
GameType::Spectator => 3,
|
||||
GameMode::Survival => 0,
|
||||
GameMode::Creative => 1,
|
||||
GameMode::Adventure => 2,
|
||||
GameMode::Spectator => 3,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the id of the game type, but return -1 if the game type is invalid.
|
||||
pub fn to_optional_id<T: Into<Option<GameType>>>(game_type: T) -> i8 {
|
||||
pub fn to_optional_id<T: Into<Option<GameMode>>>(game_type: T) -> i8 {
|
||||
match game_type.into() {
|
||||
Some(game_type) => game_type.to_id() as i8,
|
||||
None => -1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_id(id: u8) -> Option<GameType> {
|
||||
pub fn from_id(id: u8) -> Option<GameMode> {
|
||||
Some(match id {
|
||||
0 => GameType::Survival,
|
||||
1 => GameType::Creative,
|
||||
2 => GameType::Adventure,
|
||||
3 => GameType::Spectator,
|
||||
0 => GameMode::Survival,
|
||||
1 => GameMode::Creative,
|
||||
2 => GameMode::Adventure,
|
||||
3 => GameMode::Spectator,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -42,7 +43,7 @@ impl GameType {
|
|||
Some(
|
||||
match id {
|
||||
-1 => None,
|
||||
id => Some(GameType::from_id(id as u8)?),
|
||||
id => Some(GameMode::from_id(id as u8)?),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
|
@ -52,10 +53,10 @@ impl GameType {
|
|||
// TODO: these should be translated
|
||||
// TranslatableComponent("selectWorld.gameMode." + string2)
|
||||
match self {
|
||||
GameType::Survival => "Survival",
|
||||
GameType::Creative => "Creative",
|
||||
GameType::Adventure => "Adventure",
|
||||
GameType::Spectator => "Spectator",
|
||||
GameMode::Survival => "Survival",
|
||||
GameMode::Creative => "Creative",
|
||||
GameMode::Adventure => "Adventure",
|
||||
GameMode::Spectator => "Spectator",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,32 +64,32 @@ impl GameType {
|
|||
// TODO: These should be translated TranslatableComponent("gameMode." +
|
||||
// string2);
|
||||
match self {
|
||||
GameType::Survival => "Survival Mode",
|
||||
GameType::Creative => "Creative Mode",
|
||||
GameType::Adventure => "Adventure Mode",
|
||||
GameType::Spectator => "Spectator Mode",
|
||||
GameMode::Survival => "Survival Mode",
|
||||
GameMode::Creative => "Creative Mode",
|
||||
GameMode::Adventure => "Adventure Mode",
|
||||
GameMode::Spectator => "Spectator Mode",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_name(name: &str) -> GameType {
|
||||
pub fn from_name(name: &str) -> GameMode {
|
||||
match name {
|
||||
"survival" => GameType::Survival,
|
||||
"creative" => GameType::Creative,
|
||||
"adventure" => GameType::Adventure,
|
||||
"spectator" => GameType::Spectator,
|
||||
"survival" => GameMode::Survival,
|
||||
"creative" => GameMode::Creative,
|
||||
"adventure" => GameMode::Adventure,
|
||||
"spectator" => GameMode::Spectator,
|
||||
_ => panic!("Unknown game type name: {name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufReadable for GameType {
|
||||
impl McBufReadable for GameMode {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let id = u8::read_from(buf)?;
|
||||
GameType::from_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
|
||||
GameMode::from_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for GameType {
|
||||
impl McBufWritable for GameMode {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
u8::write_into(&self.to_id(), buf)
|
||||
}
|
||||
|
@ -97,15 +98,15 @@ impl McBufWritable for GameType {
|
|||
/// Rust doesn't let us `impl McBufReadable for Option<GameType>` so we have to
|
||||
/// make a new type :(
|
||||
#[derive(Hash, Copy, Clone, Debug)]
|
||||
pub struct OptionalGameType(pub Option<GameType>);
|
||||
pub struct OptionalGameType(pub Option<GameMode>);
|
||||
|
||||
impl From<Option<GameType>> for OptionalGameType {
|
||||
fn from(game_type: Option<GameType>) -> Self {
|
||||
impl From<Option<GameMode>> for OptionalGameType {
|
||||
fn from(game_type: Option<GameMode>) -> Self {
|
||||
OptionalGameType(game_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OptionalGameType> for Option<GameType> {
|
||||
impl From<OptionalGameType> for Option<GameMode> {
|
||||
fn from(optional_game_type: OptionalGameType) -> Self {
|
||||
optional_game_type.0
|
||||
}
|
||||
|
@ -114,12 +115,12 @@ impl From<OptionalGameType> for Option<GameType> {
|
|||
impl McBufReadable for OptionalGameType {
|
||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||
let id = i8::read_from(buf)?;
|
||||
GameType::from_optional_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
|
||||
GameMode::from_optional_id(id).ok_or(BufReadError::UnexpectedEnumVariant { id: id as i32 })
|
||||
}
|
||||
}
|
||||
|
||||
impl McBufWritable for OptionalGameType {
|
||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||
GameType::to_optional_id(*self).write_into(buf)
|
||||
GameMode::to_optional_id(*self).write_into(buf)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ pub use aabb::*;
|
|||
mod block_hit_result;
|
||||
pub use block_hit_result::*;
|
||||
|
||||
// some random math things used in minecraft are defined down here
|
||||
|
||||
// TODO: make this generic
|
||||
pub fn binary_search(mut min: i32, max: i32, predicate: &dyn Fn(i32) -> bool) -> i32 {
|
||||
let mut diff = max - min;
|
||||
|
@ -69,6 +71,10 @@ pub fn gcd(mut a: u32, mut b: u32) -> u32 {
|
|||
a
|
||||
}
|
||||
|
||||
pub fn lerp<T: num_traits::Float>(amount: T, a: T, b: T) -> T {
|
||||
a + amount * (b - a)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
97
azalea-physics/src/clip.rs
Normal file
97
azalea-physics/src/clip.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use azalea_core::{BlockPos, Vec3};
|
||||
|
||||
// static <T, C> T traverseBlocks(Vec3 from, Vec3 to, C context,
|
||||
// BiFunction<C, BlockPos, T> getHitResult, Function<C, T> getMissResult) {
|
||||
// if (from.equals(to)) {
|
||||
// return getMissResult.apply(context);
|
||||
// } else {
|
||||
// double rightAfterEndX = Mth.lerp(-1.0E-7D, to.x, from.x); // var5
|
||||
// double rightAfterEndY = Mth.lerp(-1.0E-7D, to.y, from.y); // var7
|
||||
// double rightAfterEndZ = Mth.lerp(-1.0E-7D, to.z, from.z); // var9
|
||||
// double rightBeforeStartX = Mth.lerp(-1.0E-7D, from.x, to.x); // var11
|
||||
// double rightBeforeStartY = Mth.lerp(-1.0E-7D, from.y, to.y); // var13
|
||||
// double rightBeforeStartZ = Mth.lerp(-1.0E-7D, from.z, to.z); // var15
|
||||
// int currentBlockX = Mth.floor(rightBeforeStartX); // var17
|
||||
// int currentBlockY = Mth.floor(rightBeforeStartY); // var18
|
||||
// int currentBlockZ = Mth.floor(rightBeforeStartZ); // var19
|
||||
// BlockPos.MutableBlockPos blockPos = new
|
||||
// BlockPos.MutableBlockPos(currentBlockX, currentBlockY, currentBlockZ);
|
||||
// Object data = getHitResult.apply(context, blockPos);
|
||||
// if (data != null) {
|
||||
// return data;
|
||||
// } else {
|
||||
// double vectorX = rightAfterEndX - rightBeforeStartX; // var22
|
||||
// double vectorY = rightAfterEndY - rightBeforeStartX; // var24
|
||||
// double vectorZ = rightAfterEndZ - rightBeforeStartX; // var26
|
||||
// int vectorXSign = Mth.sign(vectorX); // var28
|
||||
// int vectorYSign = Mth.sign(vectorY); // var29
|
||||
// int vectorZSign = Mth.sign(vectorZ); // var30
|
||||
// double percentageStepX = vectorXSign == 0 ? 1.7976931348623157E308D :
|
||||
// ((double) vectorXSign / vectorX); // var31 double percentageStepY =
|
||||
// vectorYSign == 0 ? 1.7976931348623157E308D : ((double) vectorYSign /
|
||||
// vectorY); // var33 double percentageStepZ = vectorZSign == 0 ?
|
||||
// 1.7976931348623157E308D : ((double) vectorZSign / vectorZ); // var35
|
||||
// double xPercentage = percentageStepX * (vectorXSign > 0 ? 1.0D -
|
||||
// Mth.frac(rightBeforeStartX) : Mth.frac(rightBeforeStartY)); // var37
|
||||
// double yPercentage = percentageStepY * (vectorYSign > 0 ? 1.0D -
|
||||
// Mth.frac(rightBeforeStartY) : Mth.frac(rightBeforeStartX)); // var39
|
||||
// double zPercentage = percentageStepZ * (vectorZSign > 0 ? 1.0D -
|
||||
// Mth.frac(rightBeforeStartZ) : Mth.frac(rightBeforeStartZ)); // var41
|
||||
|
||||
// Object data;
|
||||
// do {
|
||||
// if (xPercentage > 1.0D && yPercentage > 1.0D && zPercentage > 1.0D)
|
||||
// { return getMissResult.apply(context);
|
||||
// }
|
||||
|
||||
// if (xPercentage < yPercentage) {
|
||||
// if (xPercentage < zPercentage) {
|
||||
// currentBlockX += vectorXSign;
|
||||
// xPercentage += percentageStepX;
|
||||
// } else {
|
||||
// currentBlockZ += vectorZSign;
|
||||
// zPercentage += percentageStepZ;
|
||||
// }
|
||||
// } else if (posY < posZ) {
|
||||
// currentBlockY += vectorYSign;
|
||||
// yPercentage += percentageStepY;
|
||||
// } else {
|
||||
// currentBlockZ += vectorZSign;
|
||||
// zPercentage += percentageStepZ;
|
||||
// }
|
||||
|
||||
// data = getHitResult.apply(context, blockPos.set(currentBlockX,
|
||||
// currentBlockY, currentBlockZ)); } while (data == null);
|
||||
|
||||
// return data;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// static <T, C> T traverseBlocks(Vec3 from, Vec3 to, C context,
|
||||
// BiFunction<C, BlockPos, T> getHitResult, Function<C, T> getMissResult) {
|
||||
pub fn traverse_blocks<C, T>(
|
||||
from: Vec3,
|
||||
to: Vec3,
|
||||
context: C,
|
||||
get_hit_result: fn(C, BlockPos) -> T,
|
||||
get_miss_result: fn(C) -> T,
|
||||
) {
|
||||
// if (from.equals(to)) {
|
||||
// return getHitResult.apply(context);
|
||||
// } else {
|
||||
// double rightAfterEndX = Mth.lerp(-1.0E-7D, to.x, from.x); // var5
|
||||
// double rightAfterEndY = Mth.lerp(-1.0E-7D, to.y, from.y); // var7
|
||||
// double rightAfterEndZ = Mth.lerp(-1.0E-7D, to.z, from.z); // var9
|
||||
// double rightBeforeStartX = Mth.lerp(-1.0E-7D, from.x, to.x); // var11
|
||||
// double rightBeforeStartY = Mth.lerp(-1.0E-7D, from.y, to.y); // var13
|
||||
// double rightBeforeStartZ = Mth.lerp(-1.0E-7D, from.z, to.z); // var15
|
||||
|
||||
if from == to {
|
||||
return get_miss_result(context);
|
||||
}
|
||||
|
||||
let right_after_end = Vec3 {
|
||||
|
||||
};
|
||||
}
|
|
@ -5,10 +5,7 @@ mod shape;
|
|||
mod world_collisions;
|
||||
|
||||
use azalea_core::{Axis, Vec3, AABB, EPSILON};
|
||||
use azalea_world::{
|
||||
entity::{self},
|
||||
Instance, MoveEntityError,
|
||||
};
|
||||
use azalea_world::{entity, Instance, MoveEntityError};
|
||||
pub use blocks::BlockWithShape;
|
||||
pub use discrete_voxel_shape::*;
|
||||
pub use shape::*;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![feature(trait_alias)]
|
||||
|
||||
pub mod clip;
|
||||
pub mod collision;
|
||||
|
||||
use azalea_block::{Block, BlockState};
|
||||
use azalea_core::{BlockPos, Vec3};
|
||||
use azalea_world::{
|
||||
entity::{
|
||||
metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position,
|
||||
WorldName,
|
||||
metadata::Sprinting, move_relative, Attributes, Jumping, Local, LookDirection, Physics,
|
||||
Position, WorldName,
|
||||
},
|
||||
Instance, WorldContainer,
|
||||
};
|
||||
|
@ -43,10 +44,19 @@ impl Plugin for PhysicsPlugin {
|
|||
/// Move the entity with the given acceleration while handling friction,
|
||||
/// gravity, collisions, and some other stuff.
|
||||
fn travel(
|
||||
mut query: Query<(&mut Physics, &mut Position, &Attributes, &WorldName), With<Local>>,
|
||||
mut query: Query<
|
||||
(
|
||||
&mut Physics,
|
||||
&mut LookDirection,
|
||||
&mut Position,
|
||||
&Attributes,
|
||||
&WorldName,
|
||||
),
|
||||
With<Local>,
|
||||
>,
|
||||
world_container: Res<WorldContainer>,
|
||||
) {
|
||||
for (mut physics, mut position, attributes, world_name) in &mut query {
|
||||
for (mut physics, direction, mut position, attributes, world_name) in &mut query {
|
||||
let world_lock = world_container
|
||||
.get(world_name)
|
||||
.expect("All entities should be in a valid world");
|
||||
|
@ -85,6 +95,7 @@ fn travel(
|
|||
block_friction,
|
||||
&world,
|
||||
&mut physics,
|
||||
&direction,
|
||||
&mut position,
|
||||
attributes,
|
||||
);
|
||||
|
@ -158,12 +169,20 @@ pub fn ai_step(
|
|||
pub struct ForceJumpEvent(pub Entity);
|
||||
|
||||
pub fn force_jump_listener(
|
||||
mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>,
|
||||
mut query: Query<(
|
||||
&mut Physics,
|
||||
&Position,
|
||||
&LookDirection,
|
||||
&Sprinting,
|
||||
&WorldName,
|
||||
)>,
|
||||
world_container: Res<WorldContainer>,
|
||||
mut events: EventReader<ForceJumpEvent>,
|
||||
) {
|
||||
for event in events.iter() {
|
||||
if let Ok((mut physics, position, sprinting, world_name)) = query.get_mut(event.0) {
|
||||
if let Ok((mut physics, position, direction, sprinting, world_name)) =
|
||||
query.get_mut(event.0)
|
||||
{
|
||||
let world_lock = world_container
|
||||
.get(world_name)
|
||||
.expect("All entities should be in a valid world");
|
||||
|
@ -178,7 +197,7 @@ pub fn force_jump_listener(
|
|||
};
|
||||
if **sprinting {
|
||||
// sprint jumping gives some extra velocity
|
||||
let y_rot = physics.y_rot * 0.017453292;
|
||||
let y_rot = direction.y_rot * 0.017453292;
|
||||
physics.delta += Vec3 {
|
||||
x: (-f32::sin(y_rot) * 0.2) as f64,
|
||||
y: 0.,
|
||||
|
@ -204,11 +223,13 @@ fn handle_relative_friction_and_calculate_movement(
|
|||
block_friction: f32,
|
||||
world: &Instance,
|
||||
physics: &mut Physics,
|
||||
direction: &LookDirection,
|
||||
position: &mut Position,
|
||||
attributes: &Attributes,
|
||||
) -> Vec3 {
|
||||
move_relative(
|
||||
physics,
|
||||
direction,
|
||||
get_friction_influenced_speed(physics, attributes, block_friction),
|
||||
&Vec3 {
|
||||
x: physics.xxa as f64,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_core::{GameType, GlobalPos, OptionalGameType, ResourceLocation};
|
||||
use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
|
||||
pub struct ClientboundLoginPacket {
|
||||
pub player_id: u32,
|
||||
pub hardcore: bool,
|
||||
pub game_type: GameType,
|
||||
pub game_type: GameMode,
|
||||
pub previous_game_type: OptionalGameType,
|
||||
pub levels: Vec<ResourceLocation>,
|
||||
pub registry_holder: azalea_nbt::Tag,
|
||||
|
|
|
@ -3,7 +3,7 @@ use azalea_buf::{
|
|||
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
|
||||
};
|
||||
use azalea_chat::FormattedText;
|
||||
use azalea_core::{FixedBitSet, GameType};
|
||||
use azalea_core::{FixedBitSet, GameMode};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
|
@ -24,7 +24,7 @@ pub struct PlayerInfoEntry {
|
|||
pub profile: GameProfile,
|
||||
pub listed: bool,
|
||||
pub latency: i32,
|
||||
pub game_mode: GameType,
|
||||
pub game_mode: GameMode,
|
||||
pub display_name: Option<FormattedText>,
|
||||
pub chat_session: Option<RemoteChatSessionData>,
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ pub struct InitializeChatAction {
|
|||
}
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct UpdateGameModeAction {
|
||||
pub game_mode: GameType,
|
||||
pub game_mode: GameMode,
|
||||
}
|
||||
#[derive(Clone, Debug, McBuf)]
|
||||
pub struct UpdateListedAction {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use azalea_buf::McBuf;
|
||||
use azalea_core::{GameType, GlobalPos, OptionalGameType, ResourceLocation};
|
||||
use azalea_core::{GameMode, GlobalPos, OptionalGameType, ResourceLocation};
|
||||
use azalea_protocol_macros::ClientboundGamePacket;
|
||||
|
||||
#[derive(Clone, Debug, McBuf, ClientboundGamePacket)]
|
||||
|
@ -7,7 +7,7 @@ pub struct ClientboundRespawnPacket {
|
|||
pub dimension_type: ResourceLocation,
|
||||
pub dimension: ResourceLocation,
|
||||
pub seed: u64,
|
||||
pub player_game_type: GameType,
|
||||
pub player_game_type: GameMode,
|
||||
pub previous_player_game_type: OptionalGameType,
|
||||
pub is_debug: bool,
|
||||
pub is_flat: bool,
|
||||
|
|
|
@ -29,7 +29,7 @@ use std::{
|
|||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::Local;
|
||||
use super::{Local, LookDirection};
|
||||
|
||||
/// A Bevy [`SystemSet`] for various types of entity updates.
|
||||
#[derive(SystemSet, Debug, Hash, Eq, PartialEq, Clone)]
|
||||
|
@ -75,6 +75,7 @@ impl Plugin for EntityPlugin {
|
|||
debug_detect_updates_received_on_local_entities,
|
||||
add_dead,
|
||||
update_bounding_box,
|
||||
clamp_look_direction,
|
||||
))
|
||||
.init_resource::<EntityInfos>();
|
||||
}
|
||||
|
@ -322,6 +323,13 @@ fn remove_despawned_entities_from_indexes(
|
|||
}
|
||||
}
|
||||
|
||||
fn clamp_look_direction(mut query: Query<&mut LookDirection>) {
|
||||
for mut look_direction in &mut query {
|
||||
look_direction.y_rot = look_direction.y_rot % 360.0;
|
||||
look_direction.x_rot = look_direction.x_rot.clamp(-90.0, 90.0) % 360.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for EntityInfos {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("EntityInfos").finish()
|
||||
|
|
|
@ -38,19 +38,18 @@ impl std::hash::Hash for MinecraftEntityId {
|
|||
}
|
||||
}
|
||||
impl nohash_hasher::IsEnabled for MinecraftEntityId {}
|
||||
pub fn set_rotation(physics: &mut Physics, y_rot: f32, x_rot: f32) {
|
||||
physics.y_rot = y_rot % 360.0;
|
||||
physics.x_rot = x_rot.clamp(-90.0, 90.0) % 360.0;
|
||||
// TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but
|
||||
// idk what they're used for so
|
||||
}
|
||||
|
||||
pub fn move_relative(physics: &mut Physics, speed: f32, acceleration: &Vec3) {
|
||||
let input_vector = input_vector(physics, speed, acceleration);
|
||||
pub fn move_relative(
|
||||
physics: &mut Physics,
|
||||
direction: &LookDirection,
|
||||
speed: f32,
|
||||
acceleration: &Vec3,
|
||||
) {
|
||||
let input_vector = input_vector(direction, speed, acceleration);
|
||||
physics.delta += input_vector;
|
||||
}
|
||||
|
||||
pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> Vec3 {
|
||||
pub fn input_vector(direction: &LookDirection, speed: f32, acceleration: &Vec3) -> Vec3 {
|
||||
let distance = acceleration.length_squared();
|
||||
if distance < 1.0E-7 {
|
||||
return Vec3::default();
|
||||
|
@ -61,8 +60,8 @@ pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> V
|
|||
*acceleration
|
||||
}
|
||||
.scale(speed as f64);
|
||||
let y_rot = f32::sin(physics.y_rot * 0.017453292f32);
|
||||
let x_rot = f32::cos(physics.y_rot * 0.017453292f32);
|
||||
let y_rot = f32::sin(direction.y_rot * 0.017453292f32);
|
||||
let x_rot = f32::cos(direction.y_rot * 0.017453292f32);
|
||||
Vec3 {
|
||||
x: acceleration.x * (x_rot as f64) - acceleration.z * (y_rot as f64),
|
||||
y: acceleration.y,
|
||||
|
@ -70,6 +69,20 @@ pub fn input_vector(physics: &mut Physics, speed: f32, acceleration: &Vec3) -> V
|
|||
}
|
||||
}
|
||||
|
||||
pub fn view_vector(look_direction: &LookDirection) -> Vec3 {
|
||||
let y_rot = look_direction.y_rot * 0.017453292;
|
||||
let x_rot = -look_direction.x_rot * 0.017453292;
|
||||
let x_rot_cos = f32::cos(x_rot);
|
||||
let x_rot_sin = f32::sin(x_rot);
|
||||
let y_rot_cos = f32::cos(y_rot);
|
||||
let y_rot_sin = f32::sin(y_rot);
|
||||
Vec3 {
|
||||
x: (x_rot_sin * y_rot_cos) as f64,
|
||||
y: (-y_rot_sin) as f64,
|
||||
z: (x_rot_cos * y_rot_cos) as f64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the position of the block below the entity, but a little lower.
|
||||
pub fn on_pos_legacy(chunk_storage: &ChunkStorage, position: &Position) -> BlockPos {
|
||||
on_pos(0.2, chunk_storage, position)
|
||||
|
@ -149,7 +162,7 @@ impl From<&Position> for BlockPos {
|
|||
}
|
||||
}
|
||||
|
||||
/// The last position of the entity that was sent to the network.
|
||||
/// The last position of the entity that was sent over the network.
|
||||
#[derive(Component, Clone, Copy, Debug, Default, PartialEq, Deref, DerefMut)]
|
||||
pub struct LastSentPosition(Vec3);
|
||||
impl From<LastSentPosition> for ChunkPos {
|
||||
|
@ -182,9 +195,16 @@ pub struct WorldName(pub ResourceLocation);
|
|||
///
|
||||
/// If this is true, the entity will try to jump every tick. (It's equivalent to
|
||||
/// the space key being held in vanilla.)
|
||||
#[derive(Debug, Component, Deref, DerefMut)]
|
||||
#[derive(Debug, Component, Clone, Deref, DerefMut)]
|
||||
pub struct Jumping(bool);
|
||||
|
||||
/// A component that contains the direction an entity is looking.
|
||||
#[derive(Debug, Component, Clone, Default)]
|
||||
pub struct LookDirection {
|
||||
pub x_rot: f32,
|
||||
pub y_rot: f32,
|
||||
}
|
||||
|
||||
/// The physics data relating to the entity, such as position, velocity, and
|
||||
/// bounding box.
|
||||
#[derive(Debug, Component)]
|
||||
|
@ -198,12 +218,6 @@ pub struct Physics {
|
|||
/// Z acceleration.
|
||||
pub zza: f32,
|
||||
|
||||
pub x_rot: f32,
|
||||
pub y_rot: f32,
|
||||
|
||||
pub x_rot_last: f32,
|
||||
pub y_rot_last: f32,
|
||||
|
||||
pub on_ground: bool,
|
||||
pub last_on_ground: bool,
|
||||
|
||||
|
@ -237,10 +251,18 @@ pub fn add_dead(mut commands: Commands, query: Query<(Entity, &Health), Changed<
|
|||
}
|
||||
}
|
||||
|
||||
/// A component that contains the offset of the entity's eyes from the entity
|
||||
/// coordinates.
|
||||
///
|
||||
/// This is used to calculate the camera position for players, when spectating
|
||||
/// an entity, and when raytracing from the entity.
|
||||
#[derive(Component, Clone, Copy, Debug, PartialEq, Deref, DerefMut)]
|
||||
pub struct EyeHeight(f32);
|
||||
|
||||
/// A component NewType for [`azalea_registry::EntityKind`].
|
||||
///
|
||||
/// Most of the time, you should be using `azalea_registry::EntityKind`
|
||||
/// instead.
|
||||
/// directly instead.
|
||||
#[derive(Component, Clone, Copy, Debug, PartialEq, Deref)]
|
||||
pub struct EntityKind(azalea_registry::EntityKind);
|
||||
|
||||
|
@ -254,6 +276,8 @@ pub struct EntityBundle {
|
|||
pub position: Position,
|
||||
pub last_sent_position: LastSentPosition,
|
||||
pub physics: Physics,
|
||||
pub direction: LookDirection,
|
||||
pub eye_height: EyeHeight,
|
||||
pub attributes: Attributes,
|
||||
pub jumping: Jumping,
|
||||
}
|
||||
|
@ -265,11 +289,12 @@ impl EntityBundle {
|
|||
kind: azalea_registry::EntityKind,
|
||||
world_name: ResourceLocation,
|
||||
) -> Self {
|
||||
// TODO: get correct entity dimensions by having them codegened somewhere
|
||||
// TODO: get correct entity dimensions by having them codegen'd somewhere
|
||||
let dimensions = EntityDimensions {
|
||||
width: 0.6,
|
||||
height: 1.8,
|
||||
};
|
||||
let eye_height = dimensions.height * 0.85;
|
||||
|
||||
Self {
|
||||
kind: EntityKind(kind),
|
||||
|
@ -284,12 +309,6 @@ impl EntityBundle {
|
|||
yya: 0.,
|
||||
zza: 0.,
|
||||
|
||||
x_rot: 0.,
|
||||
y_rot: 0.,
|
||||
|
||||
y_rot_last: 0.,
|
||||
x_rot_last: 0.,
|
||||
|
||||
on_ground: false,
|
||||
last_on_ground: false,
|
||||
|
||||
|
@ -299,6 +318,8 @@ impl EntityBundle {
|
|||
|
||||
has_impulse: false,
|
||||
},
|
||||
eye_height: EyeHeight(eye_height),
|
||||
direction: LookDirection::default(),
|
||||
|
||||
attributes: Attributes {
|
||||
// TODO: do the correct defaults for everything, some
|
||||
|
|
|
@ -9,7 +9,8 @@ use crate::ecs::{
|
|||
};
|
||||
use azalea_core::Vec3;
|
||||
use azalea_physics::{force_jump_listener, PhysicsSet};
|
||||
use azalea_world::entity::{metadata::Player, set_rotation, Jumping, Local, Physics, Position};
|
||||
use azalea_world::entity::LookDirection;
|
||||
use azalea_world::entity::{metadata::Player, Jumping, Local, Position};
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use crate::pathfinder::PathfinderPlugin;
|
||||
|
@ -99,12 +100,12 @@ pub struct LookAtEvent {
|
|||
}
|
||||
fn look_at_listener(
|
||||
mut events: EventReader<LookAtEvent>,
|
||||
mut query: Query<(&Position, &mut Physics)>,
|
||||
mut query: Query<(&Position, &mut LookDirection)>,
|
||||
) {
|
||||
for event in events.iter() {
|
||||
if let Ok((position, mut physics)) = query.get_mut(event.entity) {
|
||||
if let Ok((position, mut look_direction)) = query.get_mut(event.entity) {
|
||||
let (y_rot, x_rot) = direction_looking_at(position, &event.position);
|
||||
set_rotation(&mut physics, y_rot, x_rot);
|
||||
(look_direction.y_rot, look_direction.x_rot) = (y_rot, x_rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue