use core::ops::{BitOr, BitOrAssign};
use crate::{flags::bitflag_enum, BitFlags, SpriteFlag};
bitflag_enum! {
pub enum Contact {
Below = 1 << 0,
Above = 1 << 1,
Left = 1 << 2,
Right = 1 << 3,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Contacts {
pub(super) sides: BitFlags<Contact>,
pub(super) touched: BitFlags<SpriteFlag>,
}
impl Contacts {
#[doc(hidden)]
pub fn wire(self) -> (u8, u8) {
(self.sides.bits(), self.touched.bits())
}
#[doc(hidden)]
pub fn from_wire(sides: u8, touched: u8) -> Self {
let mismatch = "step_cast answered an unknown flag bit (pixel8 host/SDK mismatch)";
Self {
sides: BitFlags::from_bits(sides).expect(mismatch),
touched: BitFlags::from_bits(touched).expect(mismatch),
}
}
pub const fn empty() -> Self {
Self {
sides: BitFlags::empty(),
touched: BitFlags::empty(),
}
}
pub fn below(self) -> bool {
self.sides().contains(Contact::Below)
}
pub fn above(self) -> bool {
self.sides().contains(Contact::Above)
}
pub fn left(self) -> bool {
self.sides().contains(Contact::Left)
}
pub fn right(self) -> bool {
self.sides().contains(Contact::Right)
}
pub fn sides(self) -> BitFlags<Contact> {
self.sides
}
pub fn touched(self) -> BitFlags<SpriteFlag> {
self.touched
}
pub fn touches(self, flags: impl Into<BitFlags<SpriteFlag>>) -> bool {
self.touched.intersects(flags)
}
}
impl BitOr for Contacts {
type Output = Self;
fn bitor(self, other: Self) -> Self {
Self {
sides: self.sides | other.sides,
touched: self.touched | other.touched,
}
}
}
impl BitOrAssign for Contacts {
fn bitor_assign(&mut self, other: Self) {
*self = *self | other;
}
}
impl Default for Contacts {
fn default() -> Self {
Self::empty()
}
}
impl From<Contact> for Contacts {
fn from(side: Contact) -> Self {
Self {
sides: side.into(),
touched: BitFlags::empty(),
}
}
}
impl From<BitFlags<Contact>> for Contacts {
fn from(sides: BitFlags<Contact>) -> Self {
Self {
sides,
touched: BitFlags::empty(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const WALL: SpriteFlag = SpriteFlag::Flag0;
const WATER: SpriteFlag = SpriteFlag::Flag1;
const SPIKES: SpriteFlag = SpriteFlag::Flag2;
fn met(sides: BitFlags<Contact>, touched: BitFlags<SpriteFlag>) -> Contacts {
Contacts { sides, touched }
}
#[test]
fn touches_answers_for_any_flag_in_common() {
let swum = met(BitFlags::empty(), WATER.into());
assert!(swum.touches(WATER));
assert!(!swum.touches(WALL));
assert!(swum.touches(WATER | SPIKES));
assert!(!swum.touches(WALL | SPIKES));
let everything = met(BitFlags::empty(), WALL | WATER | SPIKES);
assert!(everything.touches(WALL) && everything.touches(SPIKES));
assert!(!everything.touches(BitFlags::empty()));
assert!(!Contacts::empty().touches(WALL));
}
#[test]
fn the_stopped_sides_come_back_as_the_set_they_are() {
let cornered = met(Contact::Below | Contact::Left, WALL.into());
assert_eq!(cornered.sides(), Contact::Below | Contact::Left);
let walking_left = true;
let ahead = if walking_left {
Contact::Left
} else {
Contact::Right
};
assert!(cornered.sides().contains(ahead));
assert!(!cornered.sides().contains(Contact::Above));
assert!(cornered.below() && cornered.left());
assert!(!cornered.above() && !cornered.right());
assert_eq!(Contacts::empty().sides(), BitFlags::empty());
}
#[test]
fn two_answers_about_one_update_fold_into_one() {
let stepped = met(Contact::Left.into(), WATER.into());
let held = met(Contact::Below.into(), WALL.into());
let both = stepped | held;
assert!(both.left() && both.below());
assert_eq!(both.touched(), WALL | WATER);
let mut gathered = Contacts::empty();
gathered |= stepped;
gathered |= held;
assert_eq!(gathered, both);
assert_eq!(both | Contacts::empty(), both);
}
}