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
use df_ls_core::Reference;
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};
use df_ls_syntax_analysis::{Token, TokenDeserialize, TryFromArgumentGroup};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum GaitTypeEnum {
/// Used for moving normally over flat ground tiles, and up and down ramps and stairs.
///
/// Walking gaits are land-based gaits which require the creature to be standing up,
/// and have more than half of their `[STANCE]` body parts, e.g. legs, intact and working.
#[token_de(token = "WALK")]
Walk,
/// Used for moving over ground tiles whilst
/// [prone](https://dwarffortresswiki.org/index.php/Status_icon#Non-flashing).
///
/// Unlike walking gaits, crawling gaits do not require either standing up,
/// or `[STANCE]` body parts. They are much slower than walking gaits in general.
///
/// Please note that an uninjured, slithering snake is considered to be using a walking gait,
/// not a crawling gait: its body is its `[STANCE]` body part. If a snake is injured in the body,
/// it will revert to a crawling gait.
#[token_de(token = "CRAWL")]
Crawl,
/// Used for moving through tiles containing water or magma at a depth of at least 4/7.
///
/// In order to swim, a creature needs either the `[SWIMS_INNATE]` tag, or `[SWIMS_LEARNED]`
/// along with `[CAN_LEARN]` and skill in swimming.
#[token_de(token = "SWIM")]
Swim,
/// Used for moving through open space. An "Open space" is a map tile state that indicates there
/// is nothing there. No floor, no walls, no creatures, absolutely nothing.
///
/// In both Dwarf mode and Adventure mode, you can look at the world around you. A description
/// of the tile being examined will be displayed on the right hand side of the screen.
///
/// In order to fly, a creature needs the `[FLIER]` tag, and for enough of its body parts tagged
/// `[FLIER]` (e.g. wings) to be intact, if applicable. Flying does not require a minimum speed
/// to stay airborne, and turning while flying is no more difficult than turning while walking.
#[token_de(token = "FLY")]
Fly,
/// Used for moving whilst [climbing](https://dwarffortresswiki.org/index.php/Climber).
///
/// Climbing gaits are used for moving up and down vertical surfaces, such as trees or walls,
/// as well as for moving horizontally while supporting oneself against a vertical surface.
/// In order to climb, a creature needs intact body parts to climb with: `[GRASP]` body parts
/// by default, or `[STANCE]` body parts if the creature has the `[STANCE_CLIMBER]` token.
/// Stance climbers include cats and giant cave spiders.
#[token_de(token = "CLIMB")]
Climb,
}
impl Default for GaitTypeEnum {
fn default() -> Self {
Self::Walk
}
}
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum NoBuildUpEnum {
/// Can be specified instead of a `<start speed>` value to make the `<max speed>` instantly
/// achievable upon initiating movement (this is equivalent to a `<build up time>` of 0).
///
/// Note that `<build up time>` and `<max turning speed>` are both ignored if specified
/// alongside this (as `NO_BUILD_UP` trumps `<build up time>` and preserves `<max speed>` whilst
/// turning, and `<max turning speed>` cannot exceed `<max speed>`) so they should both be
/// emitted when using `NO_BUILD_UP`.
#[token_de(token = "NO_BUILD_UP")]
NoBuildUp,
}
impl Default for NoBuildUpEnum {
fn default() -> Self {
Self::NoBuildUp
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct GaitFlagTokenArg {
/// Makes `THICKENS_ON_ENERGY_STORAGE` and `THICKENS_ON_STRENGTH` tissue layers slow movement
/// depending on how thick they are. Adding the `STRENGTH` gait flag counteracts the impact of
/// the latter layer.
pub layers_slow: Option<()>,
/// Speeds/slows movement depending on the creature's
/// [Strength](https://dwarffortresswiki.org/index.php/Attribute#Strength) stat.
pub strength: Option<()>,
/// Speeds/slows movement depending on the creature's
/// [Agility](https://dwarffortresswiki.org/index.php/Attribute#Agility) stat.
pub agility: Option<()>,
/// Slows movement by the specified percentage when the creature is
/// [sneaking](https://dwarffortresswiki.org/index.php/Ambusher).
pub stealth_slows: Option<i32>,
}
// Deserialize a token with following pattern: `[REF:gait_flag_token_args:...]`
df_ls_syntax_analysis::token_deserialize_unary_token!(GaitFlagTokenArg);
impl TryFromArgumentGroup for GaitFlagTokenArg {
fn try_from_argument_group(
token: &mut Token,
source: &str,
diagnostics: &mut DiagnosticsInfo,
add_diagnostics_on_err: bool,
) -> Result<Self, ()> {
let mut result = Self::default();
while let Some(arg) = token.clone().get_current_arg_opt() {
// Safe first argument (is not the token_name) for error case
let arg0 = match token.get_current_arg() {
Ok(arg) => Ok(arg.clone()),
Err(err) => Err(err),
};
let next = Reference::try_from_argument_group(
token,
source,
diagnostics,
add_diagnostics_on_err,
)?;
let mut arg_is_duplicate: bool = false;
match next.0.as_ref() {
"LAYERS_SLOW" => {
if result.layers_slow.is_none() {
result.layers_slow = Some(());
} else {
arg_is_duplicate = true
}
}
"STRENGTH" => {
if result.strength.is_none() {
result.strength = Some(());
} else {
arg_is_duplicate = true
}
}
"AGILITY" => {
if result.agility.is_none() {
result.agility = Some(());
} else {
arg_is_duplicate = true
}
}
"STEALTH_SLOWS" => {
if result.stealth_slows.is_none() {
let stealth_slows = i32::try_from_argument_group(
token,
source,
diagnostics,
add_diagnostics_on_err,
)?;
result.stealth_slows = Some(stealth_slows);
} else {
arg_is_duplicate = true
}
}
_ => {
Self::diagnostics_wrong_enum_type(
&arg0?,
vec!["LAYERS_SLOW", "STRENGTH", "AGILITY", "STEALTH_SLOWS"],
source,
diagnostics,
add_diagnostics_on_err,
);
return Err(());
}
};
if arg_is_duplicate {
diagnostics.add_message(
DMExtraInfo {
range: arg.node.get_range(),
message_template_data: hash_map! {
"token_name" => format!("`{}`", arg.value),
"parent_token" => format!("`{}`", token.get_argument(0).unwrap().value),
},
},
"duplicate_token_warn",
);
}
}
Ok(result)
}
}