steel_core/behavior/items/
throw_projectile.rs1use glam::DVec3;
4use steel_protocol::packets::game::SoundSource;
5use steel_registry::sound_event::SoundEventRef;
6use steel_registry::stat::vanilla_stat_types;
7
8use crate::behavior::context::UseItemContext;
9use crate::entity::{
10 Entity, Projectile, SharedEntity, ThrowableItemProjectile, spawn_throwable_item_projectile,
11};
12
13const THROW_PITCH_NUMERATOR: f32 = 0.4;
14const THROW_PITCH_DIVISOR_MIN: f32 = 0.8;
15const THROW_PITCH_DIVISOR_MAX: f32 = 1.2;
16
17pub(super) struct ThrowParams {
19 pub sound: SoundEventRef,
21 pub sound_source: SoundSource,
23 pub sound_volume: f32,
25 pub y_offset: f32,
27 pub power: f32,
29 pub uncertainty: f32,
31}
32
33pub(super) fn throw_item_projectile<E>(
35 context: &mut UseItemContext,
36 params: &ThrowParams,
37 create: impl FnOnce(DVec3) -> E,
38) -> Option<SharedEntity>
39where
40 E: Projectile + ThrowableItemProjectile + Entity + 'static,
41{
42 let player = context.player;
43 let world = context.world;
44
45 let pitch = THROW_PITCH_NUMERATOR
46 / rand::random_range(THROW_PITCH_DIVISOR_MIN..THROW_PITCH_DIVISOR_MAX);
47 world.play_sound_at(
48 params.sound,
49 params.sound_source,
50 player.position(),
51 params.sound_volume,
52 pitch,
53 None,
54 );
55
56 let mut thrown_item = context.inv.with_item(|item| item.clone());
57 let projectile = spawn_throwable_item_projectile(
58 world,
59 player,
60 &mut thrown_item,
61 params.y_offset,
62 params.power,
63 params.uncertainty,
64 create,
65 );
66 if projectile.is_none() {
67 log::debug!("world rejected a thrown projectile; still consuming the stack");
68 }
69
70 player.award_stat(&vanilla_stat_types::ITEM_USED, thrown_item.item);
71 let has_infinite_materials = player.has_infinite_materials();
72 context
73 .inv
74 .with_item(|item| item.consume_one(has_infinite_materials));
75
76 projectile
77}