Skip to main content

steel_core/command/builtins/
kill.rs

1//! Entity killing command.
2
3use std::slice;
4
5use steel_utils::{Identifier, translations};
6use text_components::TextComponent;
7
8use super::super::{
9    brigadier::{CommandNodeBuilder, CommandSyntaxError},
10    execution::{
11        CommandSource, SteelArgumentType, SteelCommandContext, SteelCommandRuntime, argument,
12        literal,
13    },
14    registration::CommandRegistration,
15};
16use crate::entity::SharedEntity;
17
18pub(super) fn registration() -> CommandRegistration<CommandSource> {
19    CommandRegistration::new(Identifier::vanilla_static("kill"), |_| command())
20}
21
22fn command() -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
23    literal("kill")
24        .executes(kill_self)
25        .then(argument("targets", SteelArgumentType::entities()).executes(kill_targets))
26}
27
28fn kill_self(context: &SteelCommandContext<CommandSource>) -> Result<i32, CommandSyntaxError> {
29    let Some(entity) = context.source().entity() else {
30        return Err(CommandSyntaxError::dynamic(TextComponent::from(
31            &translations::PERMISSIONS_REQUIRES_ENTITY,
32        )));
33    };
34    kill_entities(context, slice::from_ref(entity))
35}
36
37fn kill_targets(context: &SteelCommandContext<CommandSource>) -> Result<i32, CommandSyntaxError> {
38    let targets = context.entities("targets")?;
39    kill_entities(context, &targets)
40}
41
42fn kill_entities(
43    context: &SteelCommandContext<CommandSource>,
44    targets: &[SharedEntity],
45) -> Result<i32, CommandSyntaxError> {
46    let Ok(result) = i32::try_from(targets.len()) else {
47        return Err(CommandSyntaxError::dynamic(
48            "Target count exceeds the command result range",
49        ));
50    };
51    for target in targets {
52        target.kill(context.source().world());
53    }
54
55    let message = if let [target] = targets {
56        translations::COMMANDS_KILL_SUCCESS_SINGLE
57            .message([TextComponent::plain(target.plain_text_name())])
58            .component()
59    } else {
60        translations::COMMANDS_KILL_SUCCESS_MULTIPLE
61            .message([TextComponent::plain(targets.len().to_string())])
62            .component()
63    };
64    context.source().send_success(&message, true);
65    Ok(result)
66}
67
68#[cfg(test)]
69mod tests {
70    use steel_registry::init_vanilla_registry;
71
72    use super::super::create_dispatcher;
73    use crate::command::execution::SteelArgumentType;
74
75    #[test]
76    fn kill_graph_supports_self_and_multiple_entity_targets() {
77        init_vanilla_registry();
78        let Ok(dispatcher) = create_dispatcher() else {
79            panic!("built-in commands should register");
80        };
81        let Some(kill) = dispatcher.children(dispatcher.root()).and_then(|children| {
82            children.iter().copied().find(|child| {
83                dispatcher
84                    .node(*child)
85                    .is_some_and(|node| node.name() == "kill")
86            })
87        }) else {
88            panic!("kill root should exist");
89        };
90        let Some(kill_node) = dispatcher.node(kill) else {
91            panic!("kill root node should exist");
92        };
93        assert!(kill_node.is_executable());
94
95        let Some(targets) = dispatcher
96            .children(kill)
97            .and_then(|children| children.first())
98        else {
99            panic!("kill targets should exist");
100        };
101        assert!(matches!(
102            dispatcher.node(*targets),
103            Some(node)
104                if node.is_executable()
105                    && node.argument_type() == Some(&SteelArgumentType::entities())
106        ));
107    }
108}