steel_core/command/builtins/execute/
source.rs1use steel_utils::translations;
4use text_components::TextComponent;
5
6use super::super::super::{
7 brigadier::{CommandNodeBuilder, CommandRedirectTarget, CommandSyntaxError},
8 execution::{
9 CommandSource, SteelArgumentType, SteelCommandContext, SteelCommandRuntime, argument,
10 literal,
11 },
12};
13use super::super::summon;
14use crate::entity::{EntityAnchor, SharedEntity};
15
16type Builder = CommandNodeBuilder<CommandSource, SteelCommandRuntime>;
17
18const EXECUTE_ROOT: CommandRedirectTarget = CommandRedirectTarget::CommandRoot;
19
20pub(super) fn as_operation() -> Builder {
21 literal("as").then(argument("targets", SteelArgumentType::entities()).forks(
22 EXECUTE_ROOT,
23 |context| {
24 Ok(context
25 .optional_entities("targets")?
26 .into_iter()
27 .map(|entity| context.source().with_entity(entity))
28 .collect())
29 },
30 ))
31}
32
33pub(super) fn at_operation() -> Builder {
34 literal("at").then(argument("targets", SteelArgumentType::entities()).forks(
35 EXECUTE_ROOT,
36 |context| {
37 context
38 .optional_entities("targets")?
39 .into_iter()
40 .map(|entity| {
41 let world = entity.level().ok_or_else(|| {
42 CommandSyntaxError::dynamic("Selected entity is not in a loaded world")
43 })?;
44 Ok(context
45 .source()
46 .with_world(world)?
47 .with_position(entity.position())
48 .with_rotation(entity.rotation()))
49 })
50 .collect::<Result<Vec<_>, CommandSyntaxError>>()
51 },
52 ))
53}
54
55pub(super) fn positioned_operation() -> Builder {
56 literal("positioned")
57 .then(
58 argument("pos", SteelArgumentType::vec3(true)).redirects_with(
59 EXECUTE_ROOT,
60 |context: &SteelCommandContext<CommandSource>| {
61 let position = required_coordinates(context, "pos")?.position(context.source());
62 Ok(context
63 .source()
64 .with_position(position)
65 .with_anchor(EntityAnchor::Feet))
66 },
67 ),
68 )
69 .then(
70 literal("as").then(argument("targets", SteelArgumentType::entities()).forks(
71 EXECUTE_ROOT,
72 |context| {
73 Ok(context
74 .optional_entities("targets")?
75 .into_iter()
76 .map(|entity| context.source().with_position(entity.position()))
77 .collect())
78 },
79 )),
80 )
81 .then(literal("over").then(
82 argument("heightmap", SteelArgumentType::heightmap()).redirects_with(
83 EXECUTE_ROOT,
84 |context: &SteelCommandContext<CommandSource>| {
85 let source = context.source();
86 let position = source.position();
87 let heightmap = context.heightmap("heightmap")?;
88 let Some(height) = source.world().height_at(
89 heightmap,
90 position.x.floor() as i32,
91 position.z.floor() as i32,
92 ) else {
93 return Err(CommandSyntaxError::dynamic(TextComponent::from(
94 &translations::ARGUMENT_POS_UNLOADED,
95 )));
96 };
97 Ok(source.with_position(position.with_y(f64::from(height))))
98 },
99 ),
100 ))
101}
102
103pub(super) fn rotated_operation() -> Builder {
104 literal("rotated")
105 .then(
106 argument("rot", SteelArgumentType::rotation()).redirects_with(
107 EXECUTE_ROOT,
108 |context| {
109 let rotation = required_coordinates(context, "rot")?.rotation(context.source());
110 Ok(context.source().with_rotation(rotation))
111 },
112 ),
113 )
114 .then(
115 literal("as").then(argument("targets", SteelArgumentType::entities()).forks(
116 EXECUTE_ROOT,
117 |context| {
118 Ok(context
119 .optional_entities("targets")?
120 .into_iter()
121 .map(|entity| context.source().with_rotation(entity.rotation()))
122 .collect())
123 },
124 )),
125 )
126}
127
128pub(super) fn facing_operation() -> Builder {
129 literal("facing")
130 .then(
131 literal("entity").then(argument("targets", SteelArgumentType::entities()).then(
132 argument("anchor", SteelArgumentType::entity_anchor()).forks(
133 EXECUTE_ROOT,
134 |context| {
135 let anchor = context.entity_anchor("anchor")?;
136 Ok(context
137 .optional_entities("targets")?
138 .into_iter()
139 .map(|entity| {
140 context
141 .source()
142 .facing_position(anchor.position(entity.as_ref()))
143 })
144 .collect())
145 },
146 ),
147 )),
148 )
149 .then(
150 argument("pos", SteelArgumentType::vec3(true)).redirects_with(
151 EXECUTE_ROOT,
152 |context| {
153 let position = required_coordinates(context, "pos")?.position(context.source());
154 Ok(context.source().facing_position(position))
155 },
156 ),
157 )
158}
159
160pub(super) fn align_operation() -> Builder {
161 literal("align").then(
162 argument("axes", SteelArgumentType::swizzle()).redirects_with(
163 EXECUTE_ROOT,
164 |context: &SteelCommandContext<CommandSource>| {
165 let axes = context.swizzle("axes")?;
166 Ok(context
167 .source()
168 .with_position(axes.align(context.source().position())))
169 },
170 ),
171 )
172}
173
174pub(super) fn anchored_operation() -> Builder {
175 literal("anchored").then(
176 argument("anchor", SteelArgumentType::entity_anchor()).redirects_with(
177 EXECUTE_ROOT,
178 |context: &SteelCommandContext<CommandSource>| {
179 let anchor = context.entity_anchor("anchor")?;
180 Ok(context.source().with_anchor(anchor))
181 },
182 ),
183 )
184}
185
186pub(super) fn in_operation() -> Builder {
187 literal("in").then(
188 argument("dimension", SteelArgumentType::world()).redirects_with(
189 EXECUTE_ROOT,
190 |context: &SteelCommandContext<CommandSource>| {
191 let world = context
192 .world_argument("dimension")?
193 .resolve(context.source())?;
194 context.source().with_world(world)
195 },
196 ),
197 )
198}
199
200pub(super) fn summon_operation() -> Builder {
201 literal("summon").then(
202 argument("entity", SteelArgumentType::summonable_entity()).redirects_with(
203 EXECUTE_ROOT,
204 |context| {
205 let entity_type = context.entity_type("entity")?;
206 let entity =
207 summon::create_entity(context, entity_type, context.source().position())?;
208 Ok(context.source().with_entity(entity))
209 },
210 ),
211 )
212}
213
214pub(super) fn on_relations() -> Builder {
215 literal("on")
216 .then(literal("vehicle").forks(EXECUTE_ROOT, |context| {
217 Ok(one_relation_sources(context.source(), |entity| {
218 entity.vehicle()
219 }))
220 }))
221 .then(literal("controller").forks(EXECUTE_ROOT, |context| {
222 Ok(one_relation_sources(context.source(), |entity| {
223 entity.controlling_passenger()
224 }))
225 }))
226 .then(literal("passengers").forks(EXECUTE_ROOT, |context| {
227 Ok(passenger_sources(context.source()))
228 }))
229}
230
231fn one_relation_sources(
232 source: &CommandSource,
233 relation: impl FnOnce(&SharedEntity) -> Option<SharedEntity>,
234) -> Vec<CommandSource> {
235 let entity = source.entity().and_then(relation);
236 entity
237 .filter(|entity| !entity.is_removed())
238 .map_or_else(Vec::new, |entity| vec![source.with_entity(entity)])
239}
240
241fn passenger_sources(source: &CommandSource) -> Vec<CommandSource> {
242 source.entity().map_or_else(Vec::new, |entity| {
243 entity
244 .passengers()
245 .into_iter()
246 .filter(|passenger| !passenger.is_removed())
247 .map(|passenger| source.with_entity(passenger))
248 .collect()
249 })
250}
251
252fn required_coordinates(
253 context: &SteelCommandContext<CommandSource>,
254 name: &str,
255) -> Result<super::super::super::execution::Coordinates, CommandSyntaxError> {
256 context.coordinates(name)
257}