Files
among-me/src/interaction/ui.rs
2025-04-06 21:01:38 +02:00

93 lines
2.9 KiB
Rust

use crate::GameState;
use crate::player::Player;
use crate::util::single;
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_egui::{EguiContexts, EguiPlugin, egui};
use bevy_rapier3d::prelude::*;
use bevy_rapier3d::rapier::prelude::CollisionEventFlags;
use std::iter;
use super::{Interact, objects};
pub(super) fn plugin(app: &mut App) {
app.add_plugins(EguiPlugin)
.init_resource::<InteractionOpportunity>()
.add_systems(
Update,
(
update_interaction_opportunities,
display_interaction_prompt,
(objects::handle_pick_up, objects::handle_drop, objects::handle_door_interaction),
)
.chain()
.run_if(in_state(GameState::Playing)),
);
}
#[derive(Debug, Clone, Eq, PartialEq, Resource, Default)]
pub struct InteractionOpportunity(pub Option<Entity>);
fn update_interaction_opportunities(
mut collision_events: EventReader<CollisionEvent>,
player_query: Query<Entity, With<Player>>,
parents: Query<&Parent>,
target_query: Query<Entity, (Without<Player>, With<Interact>)>,
mut interaction_opportunity: ResMut<InteractionOpportunity>,
) {
let player = single!(player_query);
for event in collision_events.read() {
let (e1, e2, started) = match event {
CollisionEvent::Started(e1, e2, CollisionEventFlags::SENSOR) => (*e1, *e2, true),
CollisionEvent::Stopped(e1, e2, CollisionEventFlags::SENSOR) => (*e1, *e2, false),
_ => {
continue;
}
};
let sensor = match player {
p if p == e1 => e2,
p if p == e2 => e1,
_ => continue,
};
let mut ancestors = iter::once(sensor).chain(parents.iter_ancestors(sensor));
let Some(interactable) = ancestors.find_map(|entity| target_query.get(entity).ok()) else {
continue;
};
if started {
interaction_opportunity.0.replace(interactable);
} else {
interaction_opportunity.0.take_if(|t| *t == interactable);
}
}
}
fn display_interaction_prompt(
interaction_opportunity: Res<InteractionOpportunity>,
mut egui_contexts: EguiContexts,
primary_windows: Query<&Window, With<PrimaryWindow>>,
names: Query<&Name, With<Interact>>, // only the interactables ofcourse
) {
let Some(opportunity) = interaction_opportunity.0 else {
return;
};
let window = single!(primary_windows);
let entity_name = names
.get(opportunity)
.map(|name| name.as_str())
.expect("A named Interactable object");
// objective or item
egui::Window::new("Interaction")
.collapsible(false)
.title_bar(false)
.auto_sized()
.fixed_pos(egui::Pos2::new(window.width() / 2., window.height() / 2.))
.show(egui_contexts.ctx_mut(), |ui| {
ui.label(format!("E: Pick Up {entity_name}"));
});
}