Skip to content

Commit f300855

Browse files
authored
feat: bows 🏹 (#666)
Please someone fix Velocity, Yaw, and Pitch. It was working before systems so im not sure what could've broken it.
1 parent 1fc87e2 commit f300855

File tree

11 files changed

+269
-55
lines changed

11 files changed

+269
-55
lines changed

crates/hyperion/src/egress/sync_entity_state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl Module for EntityStateSyncModule {
237237
entity_id,
238238
#[allow(clippy::cast_possible_truncation)]
239239
delta: (position_delta * 4096.0).to_array().map(|x| x as i16),
240-
on_ground: false,
240+
on_ground: velocity.velocity == Vec3::ZERO,
241241
};
242242

243243
compose

crates/hyperion/src/simulation/bow.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use std::time::{Duration, SystemTime};
2+
3+
use flecs_ecs::prelude::*;
4+
5+
#[derive(Component, Debug)]
6+
pub struct BowCharging {
7+
pub start_time: SystemTime,
8+
}
9+
10+
impl Default for BowCharging {
11+
fn default() -> Self {
12+
Self {
13+
start_time: SystemTime::now(),
14+
}
15+
}
16+
}
17+
18+
impl BowCharging {
19+
#[must_use]
20+
pub fn new() -> Self {
21+
Self::default()
22+
}
23+
24+
#[must_use]
25+
pub fn get_charge(&self) -> f32 {
26+
let elapsed = self.start_time.elapsed().unwrap_or(Duration::ZERO);
27+
let secs = elapsed.as_secs_f32();
28+
// Minecraft bow charge mechanics:
29+
// - Takes 1 second to fully charge
30+
// - Minimum charge is 0.0
31+
// - Maximum charge is 1.0
32+
secs.min(1.0)
33+
}
34+
}

crates/hyperion/src/simulation/event.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use flecs_ecs::{core::Entity, macros::Component};
55
use glam::{IVec3, Vec3};
66
use valence_generated::block::BlockState;
77
use valence_protocol::Hand;
8-
use valence_server::entity::item_frame::ItemStack;
8+
use valence_server::{ItemKind, entity::item_frame::ItemStack};
99

1010
use crate::simulation::skin::PlayerSkin;
1111

@@ -77,6 +77,12 @@ pub struct SwingArm {
7777
pub hand: Hand,
7878
}
7979

80+
#[derive(Copy, Clone, Debug)]
81+
pub struct ReleaseUseItem {
82+
pub from: Entity,
83+
pub item: ItemKind,
84+
}
85+
8086
pub struct PluginMessage<'a> {
8187
pub channel: &'a str,
8288
pub data: &'a [u8],

crates/hyperion/src/simulation/handlers.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::borrow::Cow;
44

55
use anyhow::{Context, bail};
6-
use flecs_ecs::core::{Entity, EntityView, World};
6+
use flecs_ecs::core::{Entity, EntityView, EntityViewGet, World};
77
use geometry::aabb::Aabb;
88
use glam::{IVec3, Vec3};
99
use hyperion_utils::EntityExt;
@@ -27,6 +27,7 @@ use super::{
2727
animation::{self, ActiveAnimation},
2828
block_bounds,
2929
blocks::Blocks,
30+
bow::BowCharging,
3031
};
3132
use crate::{
3233
net::{Compose, ConnectionId, decoder::BorrowedPacketFrame},
@@ -276,6 +277,14 @@ fn player_action(mut data: &[u8], query: &PacketSwitchQuery<'_>) -> anyhow::Resu
276277

277278
query.events.push(event, query.world);
278279
}
280+
PlayerAction::ReleaseUseItem => {
281+
let event = event::ReleaseUseItem {
282+
from: query.id,
283+
item: query.inventory.get_cursor().item,
284+
};
285+
286+
query.events.push(event, query.world);
287+
}
279288
action => bail!("unimplemented {action:?}"),
280289
}
281290

@@ -328,10 +337,20 @@ pub fn player_interact_item(
328337

329338
let cursor = query.inventory.get_cursor();
330339

331-
if !cursor.is_empty() && cursor.item == ItemKind::WrittenBook {
332-
let packet = play::OpenWrittenBookS2c { hand };
333-
334-
query.compose.unicast(&packet, query.io_ref, query.system)?;
340+
if !cursor.is_empty() {
341+
if cursor.item == ItemKind::WrittenBook {
342+
let packet = play::OpenWrittenBookS2c { hand };
343+
query.compose.unicast(&packet, query.io_ref, query.system)?;
344+
} else if cursor.item == ItemKind::Bow {
345+
// Start charging bow
346+
let entity = query.world.entity_from_id(query.id);
347+
entity.get::<Option<&BowCharging>>(|charging| {
348+
if charging.is_some() {
349+
return;
350+
}
351+
entity.set(BowCharging::new());
352+
});
353+
}
335354
}
336355

337356
query.handlers.interact.trigger_all(query, &event);

crates/hyperion/src/simulation/mod.rs

Lines changed: 51 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{borrow::Borrow, collections::HashMap, hash::Hash, num::TryFromIntError
22

33
use anyhow::Context;
44
use blocks::Blocks;
5+
use bow::BowCharging;
56
use bytemuck::{Pod, Zeroable};
67
use derive_more::{Constructor, Deref, DerefMut, Display, From};
78
use flecs_ecs::prelude::*;
@@ -29,6 +30,7 @@ use crate::{
2930

3031
pub mod animation;
3132
pub mod blocks;
33+
pub mod bow;
3234
pub mod command;
3335
pub mod entity_kind;
3436
pub mod event;
@@ -634,6 +636,8 @@ impl Module for SimModule {
634636

635637
world.component::<hyperion_inventory::PlayerInventory>();
636638

639+
world.component::<BowCharging>();
640+
637641
observer!(
638642
world,
639643
Spawn,
@@ -709,49 +713,53 @@ impl Module for SimModule {
709713
.kind::<flecs::pipeline::OnStore>()
710714
.with_enum_wildcard::<EntityKind>()
711715
.each_entity(|entity, (position, yaw, pitch, velocity)| {
712-
if velocity.velocity != Vec3::ZERO {
713-
// Update position based on velocity with delta time
714-
position.x += velocity.velocity.x;
715-
position.y += velocity.velocity.y;
716-
position.z += velocity.velocity.z;
717-
718-
// re calculate yaw and pitch based on velocity
719-
let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.velocity);
720-
*yaw = Yaw::new(new_yaw);
721-
*pitch = Pitch::new(new_pitch);
722-
723-
let ray = entity.get::<(&Position, &Yaw, &Pitch)>(|(position, yaw, pitch)| {
724-
let center = **position;
725-
726-
let direction = get_direction_from_rotation(**yaw, **pitch);
727-
728-
geometry::ray::Ray::new(center, direction)
729-
});
730-
731-
entity.world().get::<&mut Blocks>(|blocks| {
732-
// calculate distance limit based on velocity
733-
let distance_limit = velocity.velocity.length();
734-
let Some(collision) = blocks.first_collision(ray, distance_limit) else {
735-
velocity.velocity.x *= 0.99;
736-
velocity.velocity.z *= 0.99;
737-
738-
velocity.velocity.y -= 0.005;
739-
return;
740-
};
741-
debug!("distance_limit = {}", distance_limit);
742-
743-
debug!("collision = {collision:?}");
744-
745-
velocity.velocity = Vec3::ZERO;
746-
747-
// Set arrow position to the collision location
748-
**position = collision.normal;
749-
750-
blocks
751-
.set_block(collision.location, BlockState::DIRT)
752-
.unwrap();
753-
});
754-
}
716+
entity.get::<&EntityKind>(|kind| {
717+
if kind == &EntityKind::Arrow && velocity.velocity != Vec3::ZERO {
718+
// Update position based on velocity with delta time
719+
position.x += velocity.velocity.x;
720+
position.y += velocity.velocity.y;
721+
position.z += velocity.velocity.z;
722+
723+
// re calculate yaw and pitch based on velocity
724+
let (new_yaw, new_pitch) = get_rotation_from_velocity(velocity.velocity);
725+
*yaw = Yaw::new(new_yaw);
726+
*pitch = Pitch::new(new_pitch);
727+
728+
let ray = entity.get::<(&Position, &Yaw, &Pitch)>(|(position, yaw, pitch)| {
729+
let center = **position;
730+
731+
let direction = get_direction_from_rotation(**yaw, **pitch);
732+
733+
geometry::ray::Ray::new(center, direction)
734+
});
735+
736+
#[allow(clippy::excessive_nesting)]
737+
entity.world().get::<&mut Blocks>(|blocks| {
738+
// calculate distance limit based on velocity
739+
let distance_limit = velocity.velocity.length();
740+
let Some(collision) = blocks.first_collision(ray, distance_limit) else {
741+
// i think this velocity calculations are all wrong someone redo please
742+
velocity.velocity.x *= 0.99;
743+
velocity.velocity.z *= 0.99;
744+
745+
velocity.velocity.y -= 0.05;
746+
return;
747+
};
748+
debug!("distance_limit = {}", distance_limit);
749+
750+
debug!("collision = {collision:?}");
751+
752+
velocity.velocity = Vec3::ZERO;
753+
754+
// Set arrow position to the collision location
755+
**position = collision.normal;
756+
757+
blocks
758+
.set_block(collision.location, BlockState::DIRT)
759+
.unwrap();
760+
});
761+
}
762+
});
755763
});
756764
}
757765
}

crates/hyperion/src/storage/event/queue/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ define_events! {
5656
event::PluginMessage<'static>,
5757
event::PostureUpdate,
5858
event::SwingArm,
59-
event::ToggleDoor
59+
event::ToggleDoor,
60+
event::ReleaseUseItem,
6061
}
6162

6263
pub trait ReducedLifetime {

events/tag/src/command.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use flecs_ecs::core::World;
22
use hyperion_clap::{MinecraftCommand, hyperion_command::CommandRegistry};
33

44
use crate::command::{
5-
class::ClassCommand, fly::FlyCommand, gui::GuiCommand, raycast::RaycastCommand,
6-
replace::ReplaceCommand, shoot::ShootCommand, spawn::SpawnCommand, speed::SpeedCommand,
7-
xp::XpCommand,
5+
bow::BowCommand, class::ClassCommand, fly::FlyCommand, gui::GuiCommand,
6+
raycast::RaycastCommand, replace::ReplaceCommand, shoot::ShootCommand, spawn::SpawnCommand,
7+
speed::SpeedCommand, xp::XpCommand,
88
};
99

10+
mod bow;
1011
mod class;
1112
mod fly;
1213
mod gui;
@@ -18,6 +19,7 @@ mod speed;
1819
mod xp;
1920

2021
pub fn register(registry: &mut CommandRegistry, world: &World) {
22+
BowCommand::register(registry, world);
2123
ClassCommand::register(registry, world);
2224
FlyCommand::register(registry, world);
2325
RaycastCommand::register(registry, world);

events/tag/src/command/bow.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use clap::Parser;
2+
use flecs_ecs::core::{Entity, EntityView, EntityViewGet, WorldProvider};
3+
use hyperion::{ItemKind, ItemStack};
4+
use hyperion_clap::{CommandPermission, MinecraftCommand};
5+
use hyperion_inventory::PlayerInventory;
6+
7+
#[derive(Parser, CommandPermission, Debug)]
8+
#[command(name = "bow")]
9+
#[command_permission(group = "Normal")]
10+
pub struct BowCommand;
11+
12+
impl MinecraftCommand for BowCommand {
13+
fn execute(self, system: EntityView<'_>, caller: Entity) {
14+
let world = system.world();
15+
16+
caller
17+
.entity_view(world)
18+
.get::<&mut PlayerInventory>(|inventory| {
19+
inventory.try_add_item(ItemStack {
20+
item: ItemKind::Bow,
21+
count: 1,
22+
nbt: None,
23+
});
24+
25+
inventory.try_add_item(ItemStack {
26+
item: ItemKind::Arrow,
27+
count: 64,
28+
nbt: None,
29+
});
30+
});
31+
}
32+
}

events/tag/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use spatial::SpatialIndex;
2727
use tracing::debug;
2828

2929
use crate::{
30-
module::{chat::ChatModule, spawn::SpawnModule, stats::StatsModule},
30+
module::{bow::BowModule, chat::ChatModule, spawn::SpawnModule, stats::StatsModule},
3131
skin::SkinModule,
3232
};
3333

@@ -91,6 +91,7 @@ impl Module for TagModule {
9191
world.import::<BlockModule>();
9292
world.import::<AttackModule>();
9393
world.import::<LevelModule>();
94+
world.import::<BowModule>();
9495
world.import::<RegenerationModule>();
9596
world.import::<hyperion_permission::PermissionModule>();
9697
world.import::<hyperion_utils::HyperionUtilsModule>();

events/tag/src/module.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod attack;
22
pub mod block;
3+
pub mod bow;
34
pub mod chat;
45
pub mod level;
56
pub mod regeneration;

0 commit comments

Comments
 (0)