Skip to main content

steel_core/command/builtins/
damage.rs

1//! Vanilla damage entity command.
2
3use super::super::{
4    brigadier::{ArgumentType, CommandNodeBuilder, CommandSyntaxError},
5    execution::{
6        CommandSource, SteelArgumentType, SteelCommandContext, SteelCommandRuntime, argument,
7        literal,
8    },
9    registration::CommandRegistration,
10};
11use crate::entity::damage::DamageSource;
12use steel_registry::vanilla_damage_types;
13use steel_utils::Identifier;
14use steel_utils::translations::{COMMANDS_DAMAGE_INVULNERABLE, COMMANDS_DAMAGE_SUCCESS};
15use text_components::TextComponent;
16
17pub(super) fn registration() -> CommandRegistration<CommandSource> {
18    CommandRegistration::new(Identifier::vanilla_static("damage"), |_| command())
19}
20
21fn command() -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
22    literal("damage").then(
23        argument("target", SteelArgumentType::entity()).then(
24            argument("amount", ArgumentType::float(0.0, f32::MAX))
25                .executes(damage)
26                .then(
27                    argument("damageType", SteelArgumentType::damage_type())
28                        .executes(damage)
29                        .then(literal("at").then(
30                            argument("location", SteelArgumentType::vec3(true)).executes(damage),
31                        ))
32                        .then(
33                            literal("by").then(
34                                argument("entity", SteelArgumentType::entity())
35                                    .executes(damage)
36                                    .then(
37                                        literal("from").then(
38                                            argument("cause", SteelArgumentType::entity())
39                                                .executes(damage),
40                                        ),
41                                    ),
42                            ),
43                        ),
44                ),
45        ),
46    )
47}
48
49fn damage(context: &SteelCommandContext<CommandSource>) -> Result<i32, CommandSyntaxError> {
50    let target = context.entity("target")?;
51    let Some(amount) = context.float("amount") else {
52        return Err(CommandSyntaxError::dynamic(
53            "Parsed value for amount is missing from the command context",
54        ));
55    };
56
57    // The base damage type for this command is generic
58    let damage_type = context
59        .damage_type("damageType")
60        .unwrap_or(&vanilla_damage_types::GENERIC);
61
62    // Create the DamageSource to apply modifiers after
63    let mut damage_source = DamageSource::environment(damage_type);
64
65    // If we can get "location" from the context, it's from "at"
66    if let Some(coordinates) = context.coordinates("location") {
67        damage_source.source_position = Some(coordinates.position(context.source()));
68    }
69
70    // Else, it's from the "by", or maybe it's nothing
71    if let Ok(entity) = context.entity("entity") {
72        damage_source.direct_entity_id = Some(entity.id());
73
74        // Maybe even the causing entity is known
75        if let Ok(cause) = context.entity("cause") {
76            damage_source.causing_entity_id = Some(cause.id());
77        }
78    }
79
80    let Some(target_world) = target.level() else {
81        return Err(CommandSyntaxError::dynamic(
82            "The entity is not in a world or the world was dropped.",
83        ));
84    };
85
86    if target.hurt(&target_world, &damage_source, amount) {
87        context.source().send_success(
88            &COMMANDS_DAMAGE_SUCCESS
89                .message([
90                    TextComponent::plain(format!("{amount:?}")),
91                    target.display_name(),
92                ])
93                .component(),
94            true,
95        );
96        Ok(1)
97    } else {
98        context
99            .source()
100            .send_failure(COMMANDS_DAMAGE_INVULNERABLE.msg().component());
101        Ok(0)
102    }
103}