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
use parse_macro_input;
/// Generate implementation `YoetzSuggestion` together with the companion types required for it.
///
/// The generated companion types are:
///
/// * The key `enum` - with its name being the suggestion type's name concatenated with the "Key"
/// suffix. An `enum` containing each variant of the suggestion enum, but with only the fields
/// marked as `#[yoetz(key)]` included.
///
/// * A strategy `struct` for each variant - with their names being the suggestion type's name
/// concatenated with the variant's name. These structs act as Bevy `Component`s which will be
/// added to the entity when the suggested variant is chosen, and can be used by action systems
/// to enact the behaviors they represent.
///
/// * For internal usage only - an omni-query `struct`.
///
/// This macro must decorate an `enum`, and each variant of the `enum` must be either a unit
/// variant or a struct variant (tuple variants are not allowed). Each field of a struct variant
/// must be annotated with a `#[yoetz(...)]` attribute that specifies its role:
///
/// * Key fields (annotated with `#[yoetz(key)]`) can discern between different suggestions. If the
/// same variant is suggested but with a difference in the key fields, it will be considered as a
/// different suggestion, which means the `consistency_bonus` will not be applied and that
/// components will be re-created.
///
/// Key fields **must** be [`Clone`] and [`PartialEq`], because they get into the key enum.
///
/// * Input fields (annotated with `#[yoetz(input)]`) always get updated from the suggestion, even
/// if the suggestion itself (and therefore the components) do not change.
///
/// * State fields (annotated with `#[yoetz(state)]`) only get initialized from the suggestion when
/// the suggestion itself changes. When it doesn't (the variant and the key fields remain the
/// same) the state fields from the suggestion are discarded, which means that the action systems
/// can use them to maintain their own state.
///
/// The `enum` itself may be annotated with its own `#[yoetz(...)] attribute:
///
/// - `#[yoetz(key_enum(...))]` - for customizing the generated key `enum`.
///
/// - `#[yoetz(strategy_structs(...))]` - for customizing the generated strategy `struct`s.
///
/// Attributes that customize generated types support the following settings:
///
/// - `#[yoetz(...(derive(...)))]` - for applying derive macros on the generated structs.
///
/// ```no_run
/// # use bevy::prelude::*;
/// # use bevy_yoetz::prelude::*;
/// #[derive(YoetzSuggestion)]
/// #[yoetz(
/// // We want to be able to print both the key enum `AiBehaviorKey` and the strategy structs
/// // `AiBehaviorDoNothing` and `AiBehaviorAttack`, so we make them all Debug.
/// key_enum(derive(Debug)),
/// strategy_structs(derive(Debug),
/// ))]
/// enum AiBehavior {
/// // Unit variants are allowed.
/// DoNothing,
/// // Struct variants are allowed. But not tuple variants.
/// Attack {
/// // This is a key field, because attacking a different target should be considered a
/// // different suggestion.
/// #[yoetz(key)]
/// target_to_attack: Entity,
///
/// // This is an input field so that the system that suggests the Attack can also tell the
/// // attacker AI where the target is, since it already has that information.
/// #[yoetz(input)]
/// target_position: Vec3,
///
/// // This is a state field because the system that suggests the Attack will create the
/// // timer, but once the attack is started we want to advance the timer and not just
/// // set it again over and over each frame.
/// #[yoetz(state)]
/// time_left_for_the_attack: Timer,
/// },
/// }
/// ```