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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
use df_ls_core::{ArgN, Choose, Clamp, Reference, ReferenceTo, Referenceable};
use df_ls_diagnostics::{hash_map, DiagnosticsInfo};
use df_ls_syntax_analysis::{Token, TokenDeserialize, TryFromArgumentGroup};
use serde::{Deserialize, Serialize};
use crate::{BpCriteriaTokenArg, MaterialToken, TissueToken};
#[derive(
Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct BodyDetailPlanToken {
/// Argument 1 of `[BODY_DETAIL_PLAN:...]`
#[token_de(token = "BODY_DETAIL_PLAN", on_duplicate_to_parent, primary_token)]
#[referenceable(self_reference)]
pub reference: Option<ReferenceTo<Self>>,
/// Adds a new material to the creature based on the specified template and assigned to the
/// specified identifier.
#[token_de(token = "ADD_MATERIAL")]
pub add_material: Vec<(Reference, ReferenceTo<MaterialToken>)>,
/// Adds a new tissue to the creature based on the specified template and assigned to the
/// specified identifier.
#[token_de(token = "ADD_TISSUE")]
pub add_tissue: Vec<(Reference, ReferenceTo<TissueToken>)>,
/// Defines a series of tissue layers composing the specified body parts. Alternatively to
/// specifying a tissue, variable arguments can be entered (numbered arbitrarily to a max of 5)
/// to be filled with tissues when the plan is called in the creature entry. The `SELECT_TISSUE`
/// creature token with `TL_RELATIVE_THICKNESS` can change tissue thickness, but tissue layering
/// is hard to do without a new detail plan.
#[token_de(token = "BP_LAYERS")]
pub bp_layers: Vec<(BpCriteriaTokenArg, Vec<BpLayerTokenArg>)>,
/// Defines a series of tissue layers over the specified body parts. Alternatively to specifying
/// a tissue, variable arguments can be entered (numbered arbitrarily to a max of 5) to be
/// filled with tissues when the plan is called in the creature entry. The `SELECT_TISSUE`
/// creature token with `TL_RELATIVE_THICKNESS` can change tissue thickness, but tissue layering
/// is hard to do without a new detail plan.
#[token_de(token = "BP_LAYERS_OVER")]
pub bp_layers_over: Vec<(BpCriteriaTokenArg, Vec<BpLayerTokenArg>)>,
/// Defines a series of tissue layers under the specified body parts. Alternatively to
/// specifying a tissue, variable arguments can be entered (numbered arbitrarily to a max of 5)
/// to be filled with tissues when the plan is called in the creature entry. The `SELECT_TISSUE`
/// creature token with `TL_RELATIVE_THICKNESS` can change tissue thickness, but tissue layering
/// is hard to do without a new detail plan.
#[token_de(token = "BP_LAYERS_UNDER")]
pub bp_layers_under: Vec<(BpCriteriaTokenArg, Vec<BpLayerTokenArg>)>,
/// Defines a position for the specified body part (the nose is assigned the position `FRONT`,
/// as it's on the front of the face). This has some effects on combat, attacks and the like.
///
/// The position token `SIDES` is of unverified validity.
#[token_de(token = "BP_POSITION")]
pub bp_position: Vec<(BpCriteriaTokenArg, PositionEnum)>,
/// Defines a positional relationship between one body part and another (for example, the right
/// eyelid is `AROUND` the right eye with coverage 50, as it only partially covers the eye).
/// This has some effects on combat, attacks and the like.
#[token_de(token = "BP_RELATION")]
pub bp_relation: Vec<(
BpCriteriaTokenArg,
BpRelationEnum,
BpCriteriaTokenArg,
Option<u8>,
)>,
/// Defines a relsize for the selected body part for the current body detail plan.
#[token_de(token = "BP_RELSIZE")]
pub bp_relsize: Vec<(BpCriteriaTokenArg, u32)>,
}
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum PositionEnum {
#[token_de(token = "FRONT")]
Front,
#[token_de(token = "BACK")]
Back,
#[token_de(token = "LEFT")]
Left,
#[token_de(token = "RIGHT")]
Right,
#[token_de(token = "TOP")]
Top,
#[token_de(token = "BOTTOM")]
Bottom,
}
impl Default for PositionEnum {
fn default() -> Self {
Self::Front
}
}
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum BpRelationEnum {
/// Used to specify that the previously defined body part surrounds the following body part.
#[token_de(token = "AROUND")]
Around,
/// Used to specify that the previously defined body part is surrounded by the following body part.
#[token_de(token = "SURROUNDED_BY")]
SurroundedBy,
/// Used to specify that the previously defined body part is above the following body part.
#[token_de(token = "ABOVE")]
Above,
/// Used to specify that the previously defined body part is below the following body part.
#[token_de(token = "BELOW")]
Below,
/// Used to specify that the previously defined body part is in front of the following body part.
#[token_de(token = "IN_FRONT")]
InFront,
/// Used to specify that the previously defined body part is behind the following body part.
#[token_de(token = "BEHIND")]
Behind,
/// Used to specify a part that is cleaned by the previously defined part
/// (eg, an eyelid cleans an eye).
#[token_de(token = "CLEANS")]
Cleans,
/// Used to specify a part that cleans the previously defined part
/// (eg, an eye is cleaned by an eyelid).
#[token_de(token = "CLEANED_BY")]
CleanedBy,
}
impl Default for BpRelationEnum {
fn default() -> Self {
Self::Around
}
}
// region: BP_LAYERS args =========================================================================
// TODO: research; can you have more than 5 of these total in one BP_LAYERS?
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct BpLayerTokenArg {
pub tissue: (
// TODO: research; are multiple uses of the same arg allowed?
Choose<Clamp<ArgN, 1, 5>, Reference>,
// TODO: research; find out if this is actually a percentage
u32,
),
pub position_or_relation:
Option<Choose<PositionEnum, (BpRelationEnum, BpCriteriaTokenArg, Option<u8>)>>,
}
// Deserialize a token with following pattern: `[REF:bp_layer_token_arg:...]`
df_ls_syntax_analysis::token_deserialize_unary_token!(BpLayerTokenArg);
impl TryFromArgumentGroup for BpLayerTokenArg {
fn try_from_argument_group(
token: &mut Token,
source: &str,
diagnostics: &mut DiagnosticsInfo,
add_diagnostics_on_err: bool,
) -> Result<Self, ()> {
let mut result = Self {
tissue: <(Choose<Clamp<ArgN, 1, 5>, Reference>, u32)>::try_from_argument_group(
token,
source,
diagnostics,
add_diagnostics_on_err,
)?,
..Default::default()
};
// Now that the actual tissue is sorted out, see if it has the optional arguments;
// if not, finish this BpLayerTokenArg and thus move to the next.
let mut token_clone1 = token.clone();
let mut token_clone2 = token.clone();
// Check which option finished without errors
let option1 =
PositionEnum::try_from_argument_group(&mut token_clone1, source, diagnostics, false);
let option2 =
BpRelationEnum::try_from_argument_group(&mut token_clone2, source, diagnostics, false);
if option1.is_ok() {
// Use option 1, even if option2 is valid
// Add potential error messages
let position = PositionEnum::try_from_argument_group(
token,
source,
diagnostics,
add_diagnostics_on_err,
)?;
result.position_or_relation = Some(Choose::Choice1(position));
} else if option2.is_ok() {
// Add potential error messages
let relationship =
<(BpRelationEnum, BpCriteriaTokenArg, Option<u8>)>::try_from_argument_group(
token,
source,
diagnostics,
add_diagnostics_on_err,
)?;
result.position_or_relation = Some(Choose::Choice2(relationship));
} else {
// It is a Ref but not one we expect, so do not consume and move on
}
Ok(result)
}
}
// endregion ======================================================================================