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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
use std::marker::PhantomData;

pub use crate::internal::component::{
    query::{
        change_query, despawn_query, query, spawn_query, ChangeQuery, EventQuery, GeneralQuery,
        GeneralQueryBuilder, QueryEvent, UntrackedChangeQuery,
    },
    Component, ComponentOptionValue, ComponentValue, ComponentVecValue, ComponentsTuple, Entity,
    EnumComponent, SupportedValue, UntypedComponent, __internal_get_component,
};

use ambient_shared_types::ComponentIndex;

#[doc(hidden)]
pub use crate::internal::wit::component::Value as WitComponentValue;
use crate::prelude::EntityId;

/// Concepts are defined in the package manifest, and are used to define a collection of components that correspond to some concept in the game world.
///
/// For example, a `Camera` concept might describe a camera in the game world, and have a `near` and `projection` component.
pub trait Concept {
    /// Creates an entity with the components defined by this concept.
    fn make(self) -> Entity;

    /// Spawns this concept into the world. If you want to modify state before spawning, use `make` instead.
    fn spawn(self) -> EntityId
    where
        Self: Sized,
    {
        self.make().spawn()
    }

    /// If the entity with `id` exists and has the components defined by this concept, returns this concept with all of the values of the components in the entity.
    ///
    /// # Examples
    /// ```
    /// if let Some(camera) = Camera::get_spawned(id) {
    ///    println!("{}", camera.near);
    /// }
    /// ```
    fn get_spawned(id: EntityId) -> Option<Self>
    where
        Self: Sized;
    /// If the `entity` has the components defined by this concept, returns this concept with all of the values of the components in the entity.
    ///
    /// # Examples
    /// ```
    /// if let Some(camera) = Camera::get_unspawned(ent) {
    ///    println!("{}", camera.near);
    /// }
    /// ```
    fn get_unspawned(entity: &Entity) -> Option<Self>
    where
        Self: Sized;

    /// Returns true if `id` exists and contains the components defined by this concept.
    ///
    /// # Examples
    /// ```
    /// if Camera::contained_by_spawned(id) {
    ///    // ...
    /// }
    /// ```
    fn contained_by_spawned(id: EntityId) -> bool;
    /// Returns true if contains the components defined by this concept.
    ///
    /// # Examples
    /// ```
    /// if Camera::contained_by_unspawned(ent) {
    ///    // ...
    /// }
    /// ```
    fn contained_by_unspawned(entity: &Entity) -> bool;
}
impl<T: Concept + Sized> From<T> for Entity {
    fn from(concept: T) -> Self {
        concept.make()
    }
}
/// Provides a helper method to get an instance of this concept with all of the fields
/// filled in with suggested values.
///
/// This trait is only implemented if all fields in a concept have a suggested value.
pub trait ConceptSuggested: Concept {
    /// Returns this concept with all of its fields filled in with suggested values.
    ///
    /// The optional field, if present, will be defaulted/have all of its fields be `None`.
    fn suggested() -> Self;
}
/// Provides component tuples for this concept.
pub trait ConceptComponents: Concept {
    /// A tuple of the required components for this concept.
    type Required: ComponentsTuple + Copy + 'static;
    /// A tuple of the optional components for this concept.
    type Optional: ComponentsTuple + Copy + 'static;

    /// Returns a tuple of the required components for this concept.
    fn required() -> Self::Required;
    /// Returns a tuple of the optional components for this concept.
    fn optional() -> Self::Optional;
    /// Converts a tuple of data back to a concept.
    fn from_required_data(required: <Self::Required as ComponentsTuple>::Data) -> Self;

    /// Creates a [`ConceptQuery`] that can be passed into queries.
    ///
    /// Note that this will only get the required components of the concept, and not the optional
    /// components!
    fn as_query() -> ConceptQuery<Self>
    where
        Self: Sized,
    {
        ConceptQuery(PhantomData)
    }
}

/// Helper that lets you pass in concepts where component tuples are expected.
///
/// Note that this will only get the required components of the concept, and not the optional
/// components!
// TODO: See if we can revise the APIs to remove this.
#[derive(Default, Debug)]
pub struct ConceptQuery<C: ConceptComponents>(PhantomData<C>);
impl<C: ConceptComponents> Copy for ConceptQuery<C> {}
impl<C: ConceptComponents> Clone for ConceptQuery<C> {
    fn clone(&self) -> Self {
        *self
    }
}
/// Helper blanket implementation that allows you to use concepts where component tuples are expected.
impl<C: ConceptComponents> ComponentsTuple for ConceptQuery<C> {
    type Data = C;

    fn as_indices(&self) -> Vec<ComponentIndex> {
        C::required().as_indices()
    }

    fn from_component_types(
        component_types: Vec<crate::internal::wit::component::Value>,
    ) -> Option<Self::Data> {
        Some(C::from_required_data(
            <C as ConceptComponents>::Required::from_component_types(component_types)?,
        ))
    }
}