1use thiserror::Error;
3
4use crate::block::simple::*;
5use crate::block::*;
6use crate::content;
7use crate::data::dynamic::DynType;
8use crate::data::renderer::load;
9use crate::fluid;
10use crate::utils::ImageUtils;
11
12make_simple!(ConduitBlock, |_,
13 name,
14 _,
15 ctx: Option<&RenderingContext>,
16 rot,
17 s| {
18 let ctx = ctx.unwrap();
19
20 let mask = mask(ctx, rot, name);
21 mask2tile(mask, rot, name, s)
23});
24
25pub struct FluidBlock {
26 size: u8,
27 symmetric: bool,
28 build_cost: BuildCost,
29}
30
31impl FluidBlock {
32 #[must_use]
33 pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
34 assert!(size != 0, "invalid size");
35 Self {
36 size,
37 symmetric,
38 build_cost,
39 }
40 }
41
42 state_impl!(pub Option<fluid::Type>);
43}
44
45impl BlockLogic for FluidBlock {
46 impl_block!();
47
48 fn data_from_i32(&self, config: i32, _: GridPos) -> Result<DynData, DataConvertError> {
49 if config < 0 || config > i32::from(u16::MAX) {
50 return Err(DataConvertError::Custom(Box::new(FluidConvertError(
51 config,
52 ))));
53 }
54 Ok(DynData::Content(content::Type::Fluid, config as u16))
55 }
56
57 fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
58 match data {
59 DynData::Empty => Ok(Some(Self::create_state(None))),
60 DynData::Content(content::Type::Fluid, id) => Ok(Some(Self::create_state(Some(
61 FluidDeserializeError::forward(fluid::Type::try_from(id))?,
62 )))),
63 DynData::Content(have, ..) => Err(DeserializeError::Custom(Box::new(
64 FluidDeserializeError::ContentType(have),
65 ))),
66 _ => Err(DeserializeError::InvalidType {
67 have: data.get_type(),
68 expect: DynType::Content,
69 }),
70 }
71 }
72
73 fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
74 Ok(Self::get_state(state)
75 .as_ref()
76 .map_or(DynData::Empty, |&fluid| {
77 DynData::Content(content::Type::Fluid, fluid.into())
78 }))
79 }
80
81 fn draw(
82 &self,
83 _: &str,
84 state: Option<&State>,
85 _: Option<&RenderingContext>,
86 _: Rotation,
87 s: Scale,
88 ) -> ImageHolder<4> {
89 let mut p = load!("liquid-source", s);
90 if let Some(state) = state
91 && let Some(liq) = Self::get_state(state)
92 {
93 let mut top = load!("center", s);
94 unsafe { p.overlay(top.tint(liq.color())) };
95 return p;
96 }
97 p
98 }
99
100 fn read(&self, b: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
103 let f = buff.read_u16()?;
104 b.state = Some(Self::create_state(fluid::Type::try_from(f).ok()));
105 Ok(())
106 }
107}
108
109#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
110#[error("invalid config ({0}) for fluid")]
111pub struct FluidConvertError(pub i32);
112
113#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
114pub enum FluidDeserializeError {
115 #[error("expected Fluid but got {0:?}")]
116 ContentType(content::Type),
117 #[error("fluid not found")]
118 NotFound(#[from] fluid::TryFromU16Error),
119}
120
121impl FluidDeserializeError {
122 pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, DeserializeError> {
123 match result {
124 Ok(v) => Ok(v),
125 Err(e) => Err(DeserializeError::Custom(Box::new(e.into()))),
126 }
127 }
128}