use core::fmt;
use super::{
wire,
world::{corner, mirrored, span_byte},
Bounds, Contacts, Force, Velocity, World,
};
use crate::{motion::floor_i16, BitFlags, SpriteFlag, SpriteId};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[must_use = "a seat with no id kept to it can never be retired"]
pub struct MemberId {
pub(super) slot: u8,
pub(super) generation: u8,
}
impl MemberId {
pub const NOBODY: Self = Self {
slot: u8::MAX,
generation: 0,
};
pub const fn seat(self) -> usize {
self.slot as usize
}
}
#[derive(Clone, Copy)]
pub struct Member<'a> {
pub(super) id: MemberId,
pub(super) record: &'a wire::Record,
pub(super) mass: f32,
pub(super) own_solid: bool,
}
impl Member<'_> {
pub fn builder(x: f32, y: f32, width: u16, height: u16) -> MemberBuilder {
let (rx, ry) = (floor_i16(x), floor_i16(y));
MemberBuilder {
record: wire::Record {
x,
y,
rx,
ry,
bx: rx,
by: ry,
bw: width,
bh: height,
heeds: BitFlags::<SpriteFlag>::all().bits(),
..wire::EMPTY
},
solid: None,
mass: 1.0,
offset: (0, 0),
}
}
#[inline]
pub fn id(&self) -> MemberId {
self.id
}
#[inline]
pub fn pos(&self) -> (f32, f32) {
(self.record.x, self.record.y)
}
#[inline]
pub fn draw_pos(&self) -> (i16, i16) {
(self.record.rx, self.record.ry)
}
#[inline]
pub fn velocity(&self) -> Velocity {
Velocity::new(self.record.dx, self.record.dy)
}
#[inline]
pub fn contacts(&self) -> Contacts {
Contacts::from_wire(self.record.sides, self.record.touched)
}
#[inline]
pub fn bounds(&self) -> Bounds {
Bounds::new(
self.record.bx,
self.record.by,
self.record.bw,
self.record.bh,
)
}
#[inline]
pub fn confines(&self) -> Option<Bounds> {
(self.record.meta & wire::CONFINED != 0).then(|| {
Bounds::new(
self.record.cx,
self.record.cy,
self.record.cw,
self.record.ch,
)
})
}
#[inline]
pub fn sprite(&self) -> Option<SpriteId> {
match self.record.sprite {
wire::UNWORN => None,
id => Some(SpriteId(id as u8)),
}
}
#[inline]
pub fn solid(&self) -> Option<BitFlags<SpriteFlag>> {
self.own_solid.then(|| {
BitFlags::from_bits(self.record.solid)
.expect("a seat's solid was written from real flags")
})
}
#[inline]
pub fn heeds(&self) -> BitFlags<SpriteFlag> {
BitFlags::from_bits(self.record.heeds).expect("a seat's heeds was written from real flags")
}
#[inline]
pub fn mass(&self) -> f32 {
self.mass
}
#[inline]
pub fn flip(&self) -> (bool, bool) {
(
self.record.meta & wire::FLIP_X != 0,
self.record.meta & wire::FLIP_Y != 0,
)
}
#[inline]
pub fn span(&self) -> (u8, u8) {
((self.record.span & 0x0f) + 1, (self.record.span >> 4) + 1)
}
#[inline]
pub fn hidden(&self) -> bool {
self.record.meta & wire::HIDDEN != 0
}
}
impl fmt::Debug for Member<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Member")
.field("id", &self.id)
.field("pos", &self.pos())
.field("draw_pos", &self.draw_pos())
.field("velocity", &self.velocity())
.field("contacts", &self.contacts())
.field("bounds", &self.bounds())
.field("confines", &self.confines())
.field("sprite", &self.sprite())
.field("solid", &self.solid())
.field("heeds", &self.heeds())
.field("mass", &self.mass())
.field("flip", &self.flip())
.field("span", &self.span())
.field("hidden", &self.hidden())
.field("prop", &(self.record.meta & wire::PROP != 0))
.finish()
}
}
pub struct MemberMut<'a> {
pub(super) id: MemberId,
pub(super) record: &'a mut wire::Record,
pub(super) mass: &'a mut f32,
pub(super) offset: &'a mut (i16, i16),
pub(super) seated: &'a mut u64,
pub(super) own_solid: &'a mut u64,
pub(super) generation: &'a mut u8,
pub(super) scene_solid: BitFlags<SpriteFlag>,
}
impl MemberMut<'_> {
#[inline]
pub fn id(&self) -> MemberId {
self.read().id()
}
#[inline]
pub fn pos(&self) -> (f32, f32) {
self.read().pos()
}
#[inline]
pub fn draw_pos(&self) -> (i16, i16) {
self.read().draw_pos()
}
#[inline]
pub fn velocity(&self) -> Velocity {
self.read().velocity()
}
#[inline]
pub fn contacts(&self) -> Contacts {
self.read().contacts()
}
#[inline]
pub fn bounds(&self) -> Bounds {
self.read().bounds()
}
#[inline]
pub fn confines(&self) -> Option<Bounds> {
self.read().confines()
}
#[inline]
pub fn sprite(&self) -> Option<SpriteId> {
self.read().sprite()
}
#[inline]
pub fn solid(&self) -> Option<BitFlags<SpriteFlag>> {
self.read().solid()
}
#[inline]
pub fn heeds(&self) -> BitFlags<SpriteFlag> {
self.read().heeds()
}
#[inline]
pub fn mass(&self) -> f32 {
self.read().mass()
}
#[inline]
pub fn flip(&self) -> (bool, bool) {
self.read().flip()
}
#[inline]
pub fn span(&self) -> (u8, u8) {
self.read().span()
}
#[inline]
pub fn hidden(&self) -> bool {
self.read().hidden()
}
#[inline]
pub fn set_pos(&mut self, x: f32, y: f32) {
(self.record.x, self.record.y) = (x, y);
(self.record.rx, self.record.ry) = (floor_i16(x), floor_i16(y));
(self.record.bx, self.record.by) = corner((self.record.rx, self.record.ry), *self.offset);
}
#[inline]
pub fn set_velocity(&mut self, velocity: Velocity) {
(self.record.dx, self.record.dy) = (velocity.dx, velocity.dy);
}
#[inline]
pub fn resize(&mut self, width: u16, height: u16) {
(self.record.bw, self.record.bh) = (width, height);
}
#[inline]
pub fn set_offset(&mut self, dx: i16, dy: i16) {
*self.offset = (dx, dy);
(self.record.bx, self.record.by) = corner((self.record.rx, self.record.ry), (dx, dy));
}
#[inline]
pub fn set_confines(&mut self, confines: Option<Bounds>) {
match confines {
Some(limits) => {
self.record.meta |= wire::CONFINED;
(self.record.cx, self.record.cy) = (limits.x(), limits.y());
(self.record.cw, self.record.ch) = (limits.width(), limits.height());
}
None => self.record.meta &= !wire::CONFINED,
}
}
#[inline]
pub fn set_sprite(&mut self, sprite: Option<SpriteId>) {
self.record.sprite = match sprite {
Some(sprite) => sprite.0 as u16,
None => wire::UNWORN,
};
}
#[inline]
pub fn set_solid(&mut self, solid: Option<BitFlags<SpriteFlag>>) {
let slot = self.id.seat();
let word = match solid {
Some(solid) => {
*self.own_solid |= 1 << slot;
solid
}
None => {
*self.own_solid &= !(1 << slot);
self.scene_solid
}
};
self.record.solid = word.bits();
}
#[inline]
pub fn set_heeds(&mut self, heeds: impl Into<BitFlags<SpriteFlag>>) {
self.record.heeds = heeds.into().bits();
}
#[inline]
pub fn set_mass(&mut self, mass: f32) {
*self.mass = mass;
}
#[inline]
pub fn set_flip(&mut self, flip_x: bool, flip_y: bool) {
self.record.meta = mirrored(self.record.meta, flip_x, flip_y);
}
#[inline]
pub fn set_span(&mut self, width: u8, height: u8) {
self.record.span = span_byte(width, height);
}
#[inline]
pub fn set_hidden(&mut self, hidden: bool) {
if hidden {
self.record.meta |= wire::HIDDEN;
} else {
self.record.meta &= !wire::HIDDEN;
}
}
#[inline]
pub fn retire(self) {
let slot = self.id.seat();
*self.seated &= !(1 << slot);
*self.own_solid &= !(1 << slot);
*self.record = wire::VACANT;
*self.mass = 1.0;
*self.offset = (0, 0);
*self.generation = self.generation.wrapping_add(1);
}
#[inline]
fn read(&self) -> Member<'_> {
Member {
id: self.id,
record: self.record,
mass: *self.mass,
own_solid: *self.own_solid & (1 << self.id.seat()) != 0,
}
}
}
impl fmt::Debug for MemberMut<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("MemberMut").field(&self.read()).finish()
}
}
#[derive(Clone, Copy)]
#[must_use = "a member described and never enlisted is nobody: `enlist` seats it"]
pub struct MemberBuilder {
record: wire::Record,
solid: Option<BitFlags<SpriteFlag>>,
mass: f32,
offset: (i16, i16),
}
impl MemberBuilder {
pub fn moving(mut self, dx: f32, dy: f32) -> Self {
(self.record.dx, self.record.dy) = (dx, dy);
self
}
pub fn wearing(mut self, sprite: SpriteId) -> Self {
self.record.sprite = sprite.0 as u16;
self
}
pub fn flipped(mut self, flip_x: bool, flip_y: bool) -> Self {
self.record.meta = mirrored(self.record.meta, flip_x, flip_y);
self
}
pub fn spanning(mut self, width: u8, height: u8) -> Self {
self.record.span = span_byte(width, height);
self
}
pub fn stopped_by(mut self, solid: impl Into<BitFlags<SpriteFlag>>) -> Self {
self.solid = Some(solid.into());
self
}
pub fn heeding(mut self, heeds: impl Into<BitFlags<SpriteFlag>>) -> Self {
self.record.heeds = heeds.into().bits();
self
}
pub fn confined_to(mut self, confines: Bounds) -> Self {
self.record.meta |= wire::CONFINED;
(self.record.cx, self.record.cy) = (confines.x(), confines.y());
(self.record.cw, self.record.ch) = (confines.width(), confines.height());
self
}
pub fn offset(mut self, dx: i16, dy: i16) -> Self {
self.offset = (dx, dy);
(self.record.bx, self.record.by) = corner((self.record.rx, self.record.ry), (dx, dy));
self
}
pub fn prop(mut self) -> Self {
self.record.meta |= wire::PROP;
self
}
pub fn hidden(mut self) -> Self {
self.record.meta |= wire::HIDDEN;
self
}
pub fn weighing(mut self, mass: f32) -> Self {
self.mass = mass;
self
}
#[must_use = "a seat with no id kept to it can never be retired"]
pub fn enlist<const N: usize, F>(self, world: &mut World<N, F>) -> Option<MemberId>
where
F: Force,
{
world.claim(self.record, self.solid, self.mass, self.offset)
}
}