199 lines
6.7 KiB
Rust
199 lines
6.7 KiB
Rust
use pumpkin_protocol::java::client::play::CClearTitle;
|
|
use pumpkin_util::text::TextComponent;
|
|
|
|
use crate::command::CommandResult;
|
|
use crate::command::args::time::TimeArgumentConsumer;
|
|
use crate::entity::EntityBase;
|
|
use crate::{
|
|
command::{
|
|
CommandError, CommandExecutor, CommandSender,
|
|
args::{
|
|
Arg, ConsumedArgs, FindArg, players::PlayersArgumentConsumer,
|
|
textcomponent::TextComponentArgConsumer,
|
|
},
|
|
tree::CommandTree,
|
|
tree::builder::{argument, literal},
|
|
},
|
|
entity::player::TitleMode,
|
|
};
|
|
|
|
const NAMES: [&str; 1] = ["title"];
|
|
|
|
const DESCRIPTION: &str = "Displays a title.";
|
|
|
|
const ARG_TARGETS: &str = "targets";
|
|
|
|
const ARG_TITLE: &str = "title";
|
|
const ARG_FADE_IN: &str = "fadeIn";
|
|
const ARG_STAY: &str = "stay";
|
|
const ARG_FADE_OUT: &str = "fadeOut";
|
|
/// bool: Whether to reset or not
|
|
struct ClearOrResetExecutor(bool);
|
|
|
|
impl CommandExecutor for ClearOrResetExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
_server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
|
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
|
};
|
|
let reset = self.0;
|
|
|
|
for target in targets {
|
|
target.client.enqueue_packet(&CClearTitle::new(reset)).await;
|
|
}
|
|
sender
|
|
.send_message(if targets.len() == 1 {
|
|
let text = if reset {
|
|
"commands.title.reset.single"
|
|
} else {
|
|
"commands.title.cleared.single"
|
|
};
|
|
TextComponent::translate_cross(
|
|
text,
|
|
text,
|
|
[targets[0].get_display_name().await],
|
|
)
|
|
} else {
|
|
let text = if reset {
|
|
"commands.title.reset.multiple"
|
|
} else {
|
|
"commands.title.cleared.multiple"
|
|
};
|
|
TextComponent::translate_cross(
|
|
text,
|
|
text,
|
|
[TextComponent::text(targets.len().to_string())],
|
|
)
|
|
})
|
|
.await;
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct TitleExecutor(TitleMode);
|
|
|
|
impl CommandExecutor for TitleExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
_server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
|
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
|
};
|
|
|
|
let text = TextComponentArgConsumer::find_arg(args, ARG_TITLE)?;
|
|
|
|
let mode = &self.0;
|
|
|
|
for target in targets {
|
|
target.show_title(&text, mode).await;
|
|
}
|
|
|
|
let mode_name = format!("{mode:?}").to_lowercase();
|
|
sender
|
|
.send_message(if targets.len() == 1 {
|
|
TextComponent::translate_cross(
|
|
format!("commands.title.show.{mode_name}.single").clone(),
|
|
format!("commands.title.show.{mode_name}.single"),
|
|
[targets[0].get_display_name().await],
|
|
)
|
|
} else {
|
|
TextComponent::translate_cross(
|
|
format!("commands.title.show.{mode_name}.multiple"),
|
|
format!("commands.title.show.{mode_name}.multiple"),
|
|
[TextComponent::text(targets.len().to_string())],
|
|
)
|
|
})
|
|
.await;
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
struct TimesTitleExecutor;
|
|
|
|
impl CommandExecutor for TimesTitleExecutor {
|
|
fn execute<'a>(
|
|
&'a self,
|
|
sender: &'a CommandSender,
|
|
_server: &'a crate::server::Server,
|
|
args: &'a ConsumedArgs<'a>,
|
|
) -> CommandResult<'a> {
|
|
Box::pin(async move {
|
|
let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else {
|
|
return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into())));
|
|
};
|
|
|
|
let fade_in = TimeArgumentConsumer::find_arg(args, ARG_FADE_IN)?;
|
|
let stay = TimeArgumentConsumer::find_arg(args, ARG_STAY)?;
|
|
let fade_out = TimeArgumentConsumer::find_arg(args, ARG_FADE_OUT)?;
|
|
|
|
for target in targets {
|
|
target.send_title_animation(fade_in, stay, fade_out).await;
|
|
}
|
|
|
|
sender
|
|
.send_message(if targets.len() == 1 {
|
|
TextComponent::translate_cross(
|
|
"commands.title.times.single",
|
|
"commands.title.times.single",
|
|
[targets[0].get_display_name().await],
|
|
)
|
|
} else {
|
|
TextComponent::translate_cross(
|
|
"commands.title.times.multiple",
|
|
"commands.title.times.multiple",
|
|
[TextComponent::text(targets.len().to_string())],
|
|
)
|
|
})
|
|
.await;
|
|
|
|
Ok(targets.len() as i32)
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn init_command_tree() -> CommandTree {
|
|
CommandTree::new(NAMES, DESCRIPTION).then(
|
|
argument(ARG_TARGETS, PlayersArgumentConsumer)
|
|
.then(literal("clear").execute(ClearOrResetExecutor(false)))
|
|
.then(literal("reset").execute(ClearOrResetExecutor(true)))
|
|
.then(
|
|
literal("title").then(
|
|
argument(ARG_TITLE, TextComponentArgConsumer)
|
|
.execute(TitleExecutor(TitleMode::Title)),
|
|
),
|
|
)
|
|
.then(
|
|
literal("subtitle").then(
|
|
argument(ARG_TITLE, TextComponentArgConsumer)
|
|
.execute(TitleExecutor(TitleMode::SubTitle)),
|
|
),
|
|
)
|
|
.then(
|
|
literal("actionbar").then(
|
|
argument(ARG_TITLE, TextComponentArgConsumer)
|
|
.execute(TitleExecutor(TitleMode::ActionBar)),
|
|
),
|
|
)
|
|
.then(literal("times").then(
|
|
argument(ARG_FADE_IN, TimeArgumentConsumer).then(
|
|
argument(ARG_STAY, TimeArgumentConsumer).then(
|
|
argument(ARG_FADE_OUT, TimeArgumentConsumer).execute(TimesTitleExecutor),
|
|
),
|
|
),
|
|
)),
|
|
)
|
|
}
|