azalea_protocol/packets/game/
c_merchant_offers.rs

1use std::{
2    any::Any,
3    fmt::{self, Debug},
4    io::{self, Cursor, Write},
5};
6
7use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError};
8use azalea_inventory::{
9    DataComponentPatch, ItemStack, ItemStackData,
10    components::{self, DataComponentUnion},
11};
12use azalea_protocol_macros::ClientboundGamePacket;
13use azalea_registry::{DataComponentKind, Item};
14
15#[derive(Clone, Debug, AzBuf, ClientboundGamePacket)]
16pub struct ClientboundMerchantOffers {
17    #[var]
18    pub container_id: i32,
19    pub offers: Vec<MerchantOffer>,
20    #[var]
21    pub villager_level: u32,
22    #[var]
23    pub villager_xp: u32,
24    pub show_progress: bool,
25    pub can_restock: bool,
26}
27
28#[derive(Clone, Debug, AzBuf)]
29pub struct MerchantOffer {
30    pub base_cost_a: ItemCost,
31    pub result: ItemStack,
32    pub cost_b: Option<ItemCost>,
33    pub out_of_stock: bool,
34    pub uses: i32,
35    pub max_uses: i32,
36    pub xp: i32,
37    pub special_price_diff: i32,
38    pub price_multiplier: f32,
39    pub demand: i32,
40}
41
42/// An item that a merchant can buy.
43///
44/// This can be converted into an [`ItemStackData`] with
45/// [`Self::into_item_stack`].
46#[derive(Clone, Debug, AzBuf)]
47pub struct ItemCost {
48    pub item: Item,
49    #[var]
50    pub count: i32,
51    pub components: DataComponentExactPredicate,
52}
53impl ItemCost {
54    pub fn into_item_stack(self) -> ItemStackData {
55        let mut component_patch = DataComponentPatch::default();
56        for component in self.components.expected {
57            unsafe {
58                component_patch.unchecked_insert_component(component.kind, Some(component.value));
59            }
60        }
61        // TODO: add a fast way to iterate over default components, and insert the ones
62        // that aren't present as None
63
64        ItemStackData {
65            kind: self.item,
66            count: self.count,
67            component_patch,
68        }
69    }
70}
71
72/// Similar to [`DataComponentPatch`], but it's only additive, meaning that
73/// there are no `None` values.
74///
75/// If you got this from [`ItemCost`], consider using
76/// [`ItemCost::into_item_stack`] for a better API instead.
77#[derive(Clone, Debug, AzBuf)]
78pub struct DataComponentExactPredicate {
79    pub expected: Vec<TypedDataComponent>,
80}
81
82pub struct TypedDataComponent {
83    kind: DataComponentKind,
84    value: DataComponentUnion,
85}
86impl TypedDataComponent {
87    pub fn kind(&self) -> &DataComponentKind {
88        &self.kind
89    }
90    pub fn value(&self) -> &DataComponentUnion {
91        &self.value
92    }
93    pub fn as_dyn(&self) -> &dyn components::EncodableDataComponent {
94        // SAFETY: the kind is correct because we got it from azalea_read_as, and the
95        // kind isn't mutable
96        unsafe { self.value.as_kind(self.kind) }
97    }
98    pub fn get<T: components::DataComponentTrait>(&self) -> Option<&T> {
99        let component = self.as_dyn();
100        let component_any = component as &dyn Any;
101        component_any.downcast_ref::<T>()
102    }
103}
104impl AzaleaRead for TypedDataComponent {
105    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
106        let kind = DataComponentKind::azalea_read(buf)?;
107        let value = DataComponentUnion::azalea_read_as(kind, buf)?;
108        Ok(Self { kind, value })
109    }
110}
111impl AzaleaWrite for TypedDataComponent {
112    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
113        self.kind.azalea_write(buf)?;
114        unsafe { self.value.azalea_write_as(self.kind, buf) }
115    }
116}
117impl Debug for TypedDataComponent {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.debug_struct("TypedDataComponent")
120            .field("kind", &self.kind)
121            .finish()
122    }
123}
124impl Clone for TypedDataComponent {
125    fn clone(&self) -> Self {
126        Self {
127            kind: self.kind,
128            value: unsafe { self.value.clone_as(self.kind) },
129        }
130    }
131}