use bevy::prelude::Reflect;
use crate::bindings::{BinaryInput, Chord};
use crate::processed::processor::Helper;
use crate::processed::stateful::input_analog::StatefulAnalogInput;
use crate::processed::updating::InputSources;
use crate::reporting::{ActionLocation, InputConfigProblem, InputConfigReport};
#[derive(Debug, Reflect, Clone)]
pub(crate) struct StatefulBinaryInput {
binary_input: ProcessedChord,
active: bool,
active_previous_tick: bool,
blocked: bool,
blockers: Vec<Chord>,
}
#[derive(Debug, Reflect, Clone)]
pub(crate) enum ProcessedChord {
Dummy,
Single(BinaryInput),
Chord(Vec<BinaryInput>),
}
impl ProcessedChord {
fn new(chord: Chord) -> Self {
if chord.len() > 1 {
ProcessedChord::Chord(chord)
} else if let Some(input) = chord.first() {
ProcessedChord::Single(input.clone())
} else {
ProcessedChord::Dummy
}
}
}
pub(crate) fn check_for_problems(
input: &Chord,
report: &mut InputConfigReport,
loc: &ActionLocation,
) {
for (index, first) in input.iter().enumerate() {
for second in input.iter().skip(index + 1) {
if first == second {
report.warning(InputConfigProblem::ChordContainsDuplicates { loc: loc.clone() });
}
}
}
}
impl StatefulBinaryInput {
pub(crate) fn new(value: &Chord, helper: &Helper<'_>) -> StatefulBinaryInput {
let blockers = helper
.inputs
.iter()
.filter_map(|(_, other)| {
if is_blocked_by(value, other) {
Some(other.clone())
} else {
None
}
})
.collect();
StatefulBinaryInput {
binary_input: ProcessedChord::new(value.clone()),
active: false,
active_previous_tick: false,
blocked: false,
blockers,
}
}
pub(crate) fn is_active(&self) -> bool {
!self.blocked && self.active
}
pub(crate) fn just_pressed(&self) -> bool {
!self.blocked && self.active && !self.active_previous_tick
}
pub(crate) fn just_released(&self) -> bool {
!self.blocked && !self.active && self.active_previous_tick
}
pub(crate) fn update(&mut self, sources: &mut InputSources<'_, '_>) {
self.active_previous_tick = self.active;
if sources.settings.input_blocked_by_pad() {
return;
}
self.active = Self::is_chord_pressed(&self.binary_input, sources);
self.blocked = self
.blockers
.iter()
.any(|blocker| blocker.iter().all(|child| Self::is_pressed(child, sources)));
if self.just_pressed() {
if let Some(pad) = &mut sources.settings.post_acceptance_delay {
pad.input_detected();
}
}
}
fn is_chord_pressed(chord: &ProcessedChord, sources: &InputSources<'_, '_>) -> bool {
match chord {
ProcessedChord::Dummy => false,
ProcessedChord::Single(input) => Self::is_pressed(input, sources),
ProcessedChord::Chord(inputs) => {
inputs.iter().all(|child| Self::is_pressed(child, sources))
}
}
}
fn is_pressed(input: &BinaryInput, sources: &InputSources<'_, '_>) -> bool {
match input {
BinaryInput::Key(key_code) => sources.input_keycodes.pressed(*key_code),
BinaryInput::KeyGroup(group) => group
.iter()
.any(|key_code| sources.input_keycodes.pressed(*key_code)),
BinaryInput::MouseButton(mouse_btn) => sources.input_mouse_btn.pressed(*mouse_btn),
BinaryInput::Gamepad(btn) => {
sources
.gamepads
.iter()
.any(|(_, gamepad)| gamepad.pressed(*btn))
}
BinaryInput::Axis(input, threshold) => {
let value = StatefulAnalogInput::calc_value(input, sources);
threshold.is_reached(value)
}
}
}
}
fn is_blocked_by(this: &Chord, other: &Chord) -> bool {
if this.is_empty() || this.len() >= other.len() {
return false;
}
this.iter().all(|this_input| other.contains(this_input))
}