1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
use crate::block;
use crate::math::Rgb;
use crate::universe;

/// Data for [`Modifier::Quote`](block::Modifier::Quote).
/// Suppresses all behaviors of the [`Block`](block::Block) that might affect the space
/// around it, (or itself).
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct Quote {
    /// If true, also suppress light and sound effects.
    pub suppress_ambient: bool,
}

impl Quote {
    /// Construct an instance of [`Quote`], the same as [`Quote::default()`].
    pub fn new() -> Self {
        Self::default()
    }

    pub(in crate::block) fn evaluate(
        &self,
        mut value: block::MinEval,
        filter: &block::EvalFilter,
    ) -> Result<block::MinEval, block::InEvalError> {
        let &Quote { suppress_ambient } = self;

        value.attributes.tick_action = None;
        if suppress_ambient && !filter.skip_eval {
            block::Budget::decrement_voxels(&filter.budget, value.voxels.count())?;
            for voxel in value.voxels.as_vol_mut().as_linear_mut().iter_mut() {
                voxel.emission = Rgb::ZERO;
            }
        }

        Ok(value)
    }

    // The evaluation implementation is simple enough that it's in `Modifier::evaluate`
    // directly.
}

impl From<Quote> for block::Modifier {
    fn from(value: Quote) -> Self {
        block::Modifier::Quote(value)
    }
}

impl universe::VisitHandles for Quote {
    fn visit_handles(&self, _visitor: &mut dyn universe::HandleVisitor) {
        let Quote {
            suppress_ambient: _,
        } = self;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::block::{Block, Modifier};
    use crate::math::Rgba;
    use pretty_assertions::assert_eq;

    #[test]
    fn quote_evaluation() {
        let l = Rgb::new(1.0, 2.0, 3.0);
        let mut block = Block::builder()
            .color(Rgba::WHITE)
            .light_emission(l)
            .build();
        assert_eq!(
            block
                .evaluate()
                .unwrap()
                .voxels
                .single_voxel()
                .unwrap()
                .emission,
            l
        );
        block.modifiers_mut().push(Modifier::Quote(Quote {
            suppress_ambient: true,
        }));
        assert_eq!(
            block
                .evaluate()
                .unwrap()
                .voxels
                .single_voxel()
                .unwrap()
                .emission,
            Rgb::ZERO
        );
    }
}