Skip to main content

mindus/block/
liquid.rs

1//! liquid related things
2use 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    // TODO caps. stopped trying bcz too complex
22    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    /// format:
101    /// - fluid: [`u16`] as [`Fluid`](fluid::Type)
102    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}