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
//! [`Failure`] component, fraction of a zone's capability that remains after damage.
//! Written by the game's hit/damage system; read by domain systems independently.
use crate*;
use Scalar;
use ;
/// Fraction of a zone's nominal capability that remains after damage or failure.
///
/// This component is **cross-cutting**: it is written by one system (your
/// projectile / collision damage handler) and read independently by multiple
/// domain systems:
///
/// - `compute_aerodynamics`, scales coefficients and adds structural drag.
/// - `compute_propulsion`, scales engine thrust.
/// - `DetachPlugin` (optional), detaches the entity from the Bevy hierarchy
/// when `remaining` reaches `0.0`.
///
/// The name `Failure` describes the *state* of the zone, not the *cause*.
/// Future typed failure modes (`SurfaceBuckle`, `CylinderLoss`, ...) will sit
/// alongside this component; a resolver system will combine them into domain
/// state structs. For now this scalar covers the common case.
///
/// # Semantics
/// - `1.0`, fully intact; no performance loss.
/// - `0.0`, completely failed / detached from the airframe.
/// Domain systems must treat `0.0` as **absent**: zero aerodynamic
/// contribution, zero thrust, not maximum drag.
/// - `(0.0, 1.0)`, partial failure; outputs are scaled by `remaining`.
/// - An `AeroZone` at `0.4` produces 40 % of its nominal lift/drag.
/// - An `EngineZone` at `0.4` produces 40 % of its nominal thrust.
///
/// # Example
/// ```rust
/// use avian_fdm::components::Failure;
///
/// // Zone at full capability, the default state.
/// let f = Failure::default();
/// assert_eq!(f.remaining, 1.0);
///
/// // Zone at zero remaining capability contributes nothing.
/// let failed = Failure { remaining: 0.0 };
/// assert_eq!(failed.remaining, 0.0);
/// ```
/// Extract `Failure::remaining` from an optional component, defaulting to `1.0`
/// (fully intact) when the component is absent.
///
/// This is the standard pattern used by all FDM domain systems: a zone without
/// a `Failure` component is treated as undamaged.
///
/// ```rust
/// use avian_fdm::components::{Failure, get_remaining};
/// assert_eq!(get_remaining(None), 1.0);
/// let f = Failure { remaining: 0.4 };
/// assert_eq!(get_remaining(Some(&f)), 0.4);
/// ```