use bevy::{input_focus::{AutoFocus, InputFocus}, ui::RelativeCursorPosition};
use crate::{attachment::PartInShip, client::colors, ecs::{CanCraft, CraftingUi, Drill, MainCamera, Me, ToggleDrillEvent}, prelude::*};
pub fn crafting_ui_plugin(app: &mut App) {
app.add_systems(Update, (close_button, drill_button, drill_state_change));
}
#[derive(Component)]
struct CloseButton(Entity); // stores corresponding menu entity
#[derive(Component)]
struct PreviousInteraction(Interaction);
#[derive(Component)]
struct DrillButton(Entity); // stores corresponding part
pub fn open_crafting_ui(
ev: On<Pointer<Press>>,
crafting_parts: Query<(Entity, &Transform), (With<PartInShip>, With<CanCraft>)>,
hearty: Query<(Entity, &Transform), (With<Me>, With<CanCraft>)>,
camera: Single<(Entity, &Camera, &GlobalTransform), (With<MainCamera>, Without<PartInShip>)>,
commands: Commands,
drills: Query<&Drill>,
) {
if matches!(ev.button, PointerButton::Secondary) {
let (entity, transform) = if let Ok(part) = crafting_parts.get(ev.entity) {
part
} else if let Ok(part) = hearty.get(ev.entity) {
part
} else {
return
};
// we have our crafting entity!
// now make the ui
setup_ui(entity, transform, commands, camera, drills);
}
}
fn setup_ui(
parent_part: Entity,
parent_transform: &Transform,
mut commands: Commands,
camera: Single<(Entity, &Camera, &GlobalTransform), (With<MainCamera>, Without<PartInShip>)>,
drills: Query<&Drill>,
) {
let parent_pos = camera.1.world_to_viewport(camera.2, parent_transform.translation).unwrap();
let entity = commands.spawn((
UiTargetCamera(camera.0),
Node {
position_type: PositionType::Absolute,
left: Val::Px(parent_pos.x),
top: Val::Px(parent_pos.y),
width: Val::Px(100.0),
height: Val::Px(100.0),
display: Display::Flex,
flex_direction: FlexDirection::Column,
..default()
},
AutoFocus,
CraftingUi,
BackgroundColor(colors::MANTLE),
RelativeCursorPosition::default(),
))
.with_children(|parent| {
parent.spawn((
Node {
width: Val::Px(25.0),
height: Val::Px(25.0),
justify_content: JustifyContent::Center,
align_content: AlignContent::Center,
..Default::default()
},
Button,
BackgroundColor(colors::RED),
CloseButton(parent.target_entity()),
PreviousInteraction(Interaction::None),
))
.with_children(|parent| {
parent.spawn((
Node {
..Default::default()
},
Text::new("x"),
));
});
// only add the drill button if the part is a drill
if let Ok(drill) = drills.get(parent_part) {
parent.spawn((
Node {
width: Val::Px(100.0),
height: Val::Px(30.0),
..Default::default()
},
Button,
DrillButton(parent_part),
BackgroundColor(colors::CRUST),
PreviousInteraction(Interaction::None),
))
.with_children(|parent| {
parent.spawn((
Node {
..Default::default()
},
TextLayout::new(Justify::Center, LineBreak::WordBoundary),
TextFont {
font_size: 10.0,
..Default::default()
},
Text::new(get_drill_text(drill)),
));
});
}
});
}
fn drill_button(
mut interaction_query: Query<
(
&Interaction,
&mut PreviousInteraction,
&mut BackgroundColor,
&DrillButton,
&mut Button,
&Children,
),
Changed<Interaction>,
>,
mut toggle_drill_writer: MessageWriter<ToggleDrillEvent>,
mut text_query: Query<&mut Text>,
drills: Query<&Drill>,
) {
for (interaction, mut previous_interaction, mut color, drill_button, mut button, children) in &mut interaction_query {
match *interaction {
Interaction::Pressed => {
*color = colors::SURFACE_1.into();
}
Interaction::Hovered => {
*color = colors::SURFACE_0.into();
if previous_interaction.0 == Interaction::Pressed {
// released
let mut text = text_query.get_mut(children[0]).unwrap();
let Ok(drill) = drills.get(drill_button.0) else {
error!("A former drill is now not a drill, causing a problem in the drill button");
previous_interaction.0 = *interaction;
return
};
// don't allow drill toggling while not on a planet
if drill.on_planet.is_none() { return }
// the text is flipped because drill.drilling is an old value,
// which was now toggled
if drill.drilling {
**text = "Start Drill".to_string();
} else {
**text = "Stop Drill".to_string();
}
toggle_drill_writer.write(ToggleDrillEvent { drill_entity: drill_button.0 });
}
}
Interaction::None => {
*color = colors::CRUST.into();
}
}
previous_interaction.0 = *interaction;
}
}
fn drill_state_change(
drills: Query<&Drill, Changed<Drill>>,
drill_buttons: Query<(&DrillButton, &Children)>,
mut text_query: Query<&mut Text>,
) {
for (drill_button, children) in &drill_buttons {
let Ok(drill) = drills.get(drill_button.0) else {
continue
};
let mut text = text_query.get_mut(children[0]).unwrap();
**text = get_drill_text(drill);
}
}
fn get_drill_text(drill: &Drill) -> String {
if drill.on_planet.is_some() {
if drill.drilling {
"Stop Drill".to_string()
} else {
"Start Drill".to_string()
}
} else {
"Drill not on planet".to_string()
}
}
fn close_button(
mut commands: Commands,
mut interaction_query: Query<
(&Interaction, &mut PreviousInteraction, &mut BackgroundColor, &CloseButton, &mut Button),
Changed<Interaction>,
>,
mouse: Res<ButtonInput<MouseButton>>,
) {
for (interaction, mut previous_interaction, mut color,
close_button, mut button) in &mut interaction_query
{
match *interaction {
Interaction::Pressed => {
*color = colors::MAROON.into();
}
Interaction::Hovered => {
*color = colors::PINK.into();
if previous_interaction.0 == Interaction::Pressed {
commands.entity(close_button.0).despawn();
}
}
Interaction::None => {
*color = colors::RED.into();
}
}
previous_interaction.0 = *interaction;
}
}