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
97
98
99
use bevy::prelude::*;

use crate::{flags::Flags, state::AppState};

pub(crate) struct VisibilityPlugin;

impl Plugin for VisibilityPlugin {
    fn build(&self, app: &mut App) {
        app.add_systems(
            PostUpdate,
            update
                .run_if(in_state(AppState::InGame))
                .in_set(VisibilitySet::Update),
        );
    }
}

#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemSet)]
pub enum VisibilitySet {
    Update,
}

/// This represents visibility flags. An object is visible if at least one
/// "visible" flag is set to true and none of "invisible" flag is true. The
/// individual flags can be controlled independently.
///
/// The system [`VisibilitySet::Update`] executed during [`PostUpdate`]
/// automatically updates [`bevy::render::prelude::Visibility`] of entities
/// with this component.
#[derive(Component, Default)]
pub struct VisibilityFlags {
    visible: Flags,
    invisible: Flags,
}

impl VisibilityFlags {
    pub fn update_visible(&mut self, bit: u32, value: bool) {
        self.visible.set(bit, value);
    }

    pub fn update_invisible(&mut self, bit: u32, value: bool) {
        self.invisible.set(bit, value);
    }

    /// Returns value of a specific "visible" flag.
    pub fn visible_value(&self, bit: u32) -> bool {
        self.visible.get(bit)
    }

    /// Returns value of a specific "invisible" flag.
    pub fn invisible_value(&self, bit: u32) -> bool {
        self.invisible.get(bit)
    }

    pub fn visible(&self) -> bool {
        !self.invisible.any() && self.visible.any()
    }
}

fn update(mut entities: Query<(&VisibilityFlags, &mut Visibility), Changed<VisibilityFlags>>) {
    for (flags, mut visibility) in entities.iter_mut() {
        *visibility = if flags.visible() {
            Visibility::Inherited
        } else {
            Visibility::Hidden
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_visibility_flags() {
        let mut flags = VisibilityFlags::default();
        assert!(!flags.visible());

        flags.update_visible(1, true);
        assert!(flags.visible());
        flags.update_visible(3, true);
        assert!(flags.visible());
        flags.update_visible(1, false);
        assert!(flags.visible());
        flags.update_visible(3, false);
        assert!(!flags.visible());

        assert!(!flags.invisible_value(1));
        flags.update_invisible(1, true);
        assert!(!flags.visible());
        assert!(flags.invisible_value(1));
        flags.update_visible(1, true);
        assert!(!flags.visible());
        assert!(flags.invisible_value(1));
        flags.update_invisible(1, false);
        assert!(flags.visible());
        assert!(!flags.invisible_value(1));
    }
}