use alloc::vec::Vec;
use crate::{
graphics::{so2d_to_lcdrect, Bitmap},
math::{Point2D, Rect, SideOffsets2D, Vec2D},
PLAYDATE,
};
pub use sys::SpriteCollisionResponseType;
fn rect_to_pdrect(rect: Rect<f32>) -> sys::PDRect {
sys::PDRect {
x: rect.origin.x,
y: rect.origin.y,
width: rect.size.width,
height: rect.size.height,
}
}
fn pdrect_to_rect(rect: sys::PDRect) -> Rect<f32> {
Rect {
origin: (rect.x, rect.y).into(),
size: (rect.width, rect.height).into(),
}
}
fn collision_point_to_point(point: sys::CollisionPoint) -> Point2D<f32> {
Point2D::new(point.x, point.y)
}
fn collision_vec_to_vec(vec: sys::CollisionVector) -> Vec2D<i32> {
Vec2D::new(vec.x, vec.y)
}
pub struct _Sprite {
handle: *const sys::playdate_sprite,
}
impl _Sprite {
pub(crate) fn new(handle: *const sys::playdate_sprite) -> Self {
Self { handle }
}
pub fn set_always_redraw(&self, flag: bool) {
unsafe {
(*self.handle).setAlwaysRedraw.unwrap()(flag as i32);
}
}
pub fn add_dirty_rect(&self, dirty_rect: SideOffsets2D<i32>) {
unsafe {
(*self.handle).addDirtyRect.unwrap()(so2d_to_lcdrect(dirty_rect));
}
}
pub fn draw_sprites(&self) {
unsafe {
(*self.handle).drawSprites.unwrap()();
}
}
pub fn update_and_draw_sprites(&self) {
unsafe {
(*self.handle).updateAndDrawSprites.unwrap()();
}
}
pub fn new_sprite(&self) -> Sprite {
Sprite::from(unsafe { (*self.handle).newSprite.unwrap()() })
}
pub(crate) fn free_sprite(&self, sprite: *mut sys::LCDSprite) {
unsafe {
(*self.handle).freeSprite.unwrap()(sprite);
}
}
pub(crate) fn copy(&self, sprite: *mut sys::LCDSprite) -> *mut sys::LCDSprite {
unsafe { (*self.handle).copy.unwrap()(sprite) }
}
pub fn add_sprite(&self, sprite: &Sprite) {
unsafe {
(*self.handle).addSprite.unwrap()(sprite.handle);
}
}
pub fn remove_sprite(&self, sprite: &Sprite) {
unsafe {
(*self.handle).removeSprite.unwrap()(sprite.handle);
}
}
pub fn remove_sprites(&self, sprites: &[&Sprite]) {
let mut sprites: Vec<_> = sprites.iter().map(|s| s.handle).collect();
unsafe {
(*self.handle).removeSprites.unwrap()(sprites.as_mut_ptr(), sprites.len() as i32);
}
}
pub fn remove_all_sprites(&self) {
unsafe {
(*self.handle).removeAllSprites.unwrap()();
}
}
pub fn get_sprite_count(&self) -> i32 {
unsafe { (*self.handle).getSpriteCount.unwrap()() }
}
pub(crate) fn set_bounds(&self, sprite: *mut sys::LCDSprite, bounds: Rect<f32>) {
unsafe {
(*self.handle).setBounds.unwrap()(sprite, rect_to_pdrect(bounds));
}
}
pub(crate) fn get_bounds(&self, sprite: *mut sys::LCDSprite) -> Rect<f32> {
unsafe { pdrect_to_rect((*self.handle).getBounds.unwrap()(sprite)) }
}
pub(crate) fn move_to(&self, sprite: *mut sys::LCDSprite, x: f32, y: f32) {
unsafe {
(*self.handle).moveTo.unwrap()(sprite, x, y);
}
}
pub(crate) fn move_by(&self, sprite: *mut sys::LCDSprite, dx: f32, dy: f32) {
unsafe {
(*self.handle).moveBy.unwrap()(sprite, dx, dy);
}
}
pub(crate) fn set_image(
&self,
sprite: *mut sys::LCDSprite,
image: *mut sys::LCDBitmap,
flip: sys::LCDBitmapFlip,
) {
unsafe {
(*self.handle).setImage.unwrap()(sprite, image, flip);
}
}
pub(crate) fn get_image(&self, sprite: *mut sys::LCDSprite) -> *mut sys::LCDBitmap {
unsafe { (*self.handle).getImage.unwrap()(sprite) }
}
pub(crate) fn set_size(&self, sprite: *mut sys::LCDSprite, width: f32, height: f32) {
unsafe {
(*self.handle).setSize.unwrap()(sprite, width, height);
}
}
pub(crate) fn set_z_index(&self, sprite: *mut sys::LCDSprite, z_index: i16) {
unsafe {
(*self.handle).setZIndex.unwrap()(sprite, z_index);
}
}
pub(crate) fn get_z_index(&self, sprite: *mut sys::LCDSprite) -> i16 {
unsafe { (*self.handle).getZIndex.unwrap()(sprite) }
}
pub(crate) fn set_draw_mode(&self, sprite: *mut sys::LCDSprite, mode: sys::LCDBitmapDrawMode) {
unsafe {
(*self.handle).setDrawMode.unwrap()(sprite, mode);
}
}
pub(crate) fn set_image_flip(&self, sprite: *mut sys::LCDSprite, flip: sys::LCDBitmapFlip) {
unsafe {
(*self.handle).setImageFlip.unwrap()(sprite, flip);
}
}
pub(crate) fn get_image_flip(&self, sprite: *mut sys::LCDSprite) -> sys::LCDBitmapFlip {
unsafe { (*self.handle).getImageFlip.unwrap()(sprite) }
}
pub(crate) fn set_stencil(&self, sprite: *mut sys::LCDSprite, stencil: *mut sys::LCDBitmap) {
unsafe {
(*self.handle).setStencil.unwrap()(sprite, stencil);
}
}
pub(crate) fn set_clip_rect(&self, sprite: *mut sys::LCDSprite, clip_rect: SideOffsets2D<i32>) {
unsafe {
(*self.handle).setClipRect.unwrap()(sprite, so2d_to_lcdrect(clip_rect));
}
}
pub(crate) fn clear_clip_rect(&self, sprite: *mut sys::LCDSprite) {
unsafe {
(*self.handle).clearClipRect.unwrap()(sprite);
}
}
pub fn set_clip_rects_in_range(&self, clip_rect: SideOffsets2D<i32>, start_z: i32, end_z: i32) {
unsafe {
(*self.handle).setClipRectsInRange.unwrap()(so2d_to_lcdrect(clip_rect), start_z, end_z);
}
}
pub fn clear_clip_rects_in_range(&self, start_z: i32, end_z: i32) {
unsafe {
(*self.handle).clearClipRectsInRange.unwrap()(start_z, end_z);
}
}
pub(crate) fn set_updates_enabled(&self, sprite: *mut sys::LCDSprite, flag: i32) {
unsafe {
(*self.handle).setUpdatesEnabled.unwrap()(sprite, flag);
}
}
pub(crate) fn updates_enabled(&self, sprite: *mut sys::LCDSprite) -> i32 {
unsafe { (*self.handle).updatesEnabled.unwrap()(sprite) }
}
pub(crate) fn set_collisions_enabled(&self, sprite: *mut sys::LCDSprite, flag: i32) {
unsafe {
(*self.handle).setCollisionsEnabled.unwrap()(sprite, flag);
}
}
pub(crate) fn collisions_enabled(&self, sprite: *mut sys::LCDSprite) -> i32 {
unsafe { (*self.handle).collisionsEnabled.unwrap()(sprite) }
}
pub(crate) fn set_visible(&self, sprite: *mut sys::LCDSprite, flag: i32) {
unsafe {
(*self.handle).setVisible.unwrap()(sprite, flag);
}
}
pub(crate) fn is_visible(&self, sprite: *mut sys::LCDSprite) -> i32 {
unsafe { (*self.handle).isVisible.unwrap()(sprite) }
}
pub(crate) fn set_opaque(&self, sprite: *mut sys::LCDSprite, flag: i32) {
unsafe {
(*self.handle).setOpaque.unwrap()(sprite, flag);
}
}
pub(crate) fn mark_dirty(&self, sprite: *mut sys::LCDSprite) {
unsafe {
(*self.handle).markDirty.unwrap()(sprite);
}
}
pub(crate) fn set_tag(&self, sprite: *mut sys::LCDSprite, tag: u8) {
unsafe {
(*self.handle).setTag.unwrap()(sprite, tag);
}
}
pub(crate) fn get_tag(&self, sprite: *mut sys::LCDSprite) -> u8 {
unsafe { (*self.handle).getTag.unwrap()(sprite) }
}
pub(crate) fn set_ignores_draw_offset(&self, sprite: *mut sys::LCDSprite, flag: i32) {
unsafe {
(*self.handle).setIgnoresDrawOffset.unwrap()(sprite, flag);
}
}
pub(crate) fn set_update_function(
&self,
sprite: *mut sys::LCDSprite,
func: sys::LCDSpriteUpdateFunction,
) {
unsafe {
(*self.handle).setUpdateFunction.unwrap()(sprite, func);
}
}
pub(crate) fn set_draw_function(
&self,
sprite: *mut sys::LCDSprite,
func: sys::LCDSpriteDrawFunction,
) {
unsafe {
(*self.handle).setDrawFunction.unwrap()(sprite, func);
}
}
pub(crate) fn get_position(&self, sprite: *mut sys::LCDSprite) -> (f32, f32) {
let mut x = 0.0;
let mut y = 0.0;
unsafe {
(*self.handle).getPosition.unwrap()(sprite, &mut x, &mut y);
}
(x, y)
}
pub fn reset_collision_world(&self) {
unsafe {
(*self.handle).resetCollisionWorld.unwrap()();
}
}
pub(crate) fn set_collide_rect(&self, sprite: *mut sys::LCDSprite, collide_rect: Rect<f32>) {
unsafe {
(*self.handle).setCollideRect.unwrap()(sprite, rect_to_pdrect(collide_rect));
}
}
pub(crate) fn get_collide_rect(&self, sprite: *mut sys::LCDSprite) -> Rect<f32> {
unsafe { pdrect_to_rect((*self.handle).getCollideRect.unwrap()(sprite)) }
}
pub(crate) fn clear_collide_rect(&self, sprite: *mut sys::LCDSprite) {
unsafe {
(*self.handle).clearCollideRect.unwrap()(sprite);
}
}
pub(crate) fn set_collision_response_function(
&self,
sprite: *mut sys::LCDSprite,
func: sys::LCDSpriteCollisionFilterProc,
) {
unsafe {
(*self.handle).setCollisionResponseFunction.unwrap()(sprite, func);
}
}
pub fn check_collisions(
&self,
sprite: &Sprite,
goal_x: f32,
goal_y: f32,
) -> Vec<SpriteCollisionInfo> {
let mut actual_x = 0.0;
let mut actual_y = 0.0;
let mut len = 0;
let info = unsafe {
(*self.handle).checkCollisions.unwrap()(
sprite.handle,
goal_x,
goal_y,
&mut actual_x,
&mut actual_y,
&mut len,
)
};
let mut result = Vec::new();
for i in 0..len {
let info = unsafe { info.offset(i as isize).as_ref().unwrap() };
result.push(SpriteCollisionInfo::new(info));
}
result
}
pub fn move_with_collisions(
&self,
sprite: &Sprite,
goal_x: f32,
goal_y: f32,
) -> Vec<SpriteCollisionInfo> {
let mut actual_x = 0.0;
let mut actual_y = 0.0;
let mut len = 0;
let info = unsafe {
(*self.handle).moveWithCollisions.unwrap()(
sprite.handle,
goal_x,
goal_y,
&mut actual_x,
&mut actual_y,
&mut len,
)
};
let mut result = Vec::new();
for i in 0..len {
let info = unsafe { info.offset(i as isize).as_ref().unwrap() };
result.push(SpriteCollisionInfo::new(info));
}
result
}
pub fn query_sprites_at_point(&self, x: f32, y: f32) -> Vec<Sprite> {
let mut len = 0;
let sprites = unsafe { (*self.handle).querySpritesAtPoint.unwrap()(x, y, &mut len) };
let mut result = Vec::new();
for i in 0..len {
let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
result.push(Sprite::from_ref(*sprite));
}
result
}
pub fn query_sprites_in_rect(&self, x: f32, y: f32, width: f32, height: f32) -> Vec<Sprite> {
let mut len = 0;
let sprites =
unsafe { (*self.handle).querySpritesInRect.unwrap()(x, y, width, height, &mut len) };
let mut result = Vec::new();
for i in 0..len {
let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
result.push(Sprite::from_ref(*sprite));
}
result
}
pub fn query_sprites_along_line(&self, x1: f32, y1: f32, x2: f32, y2: f32) -> Vec<Sprite> {
let mut len = 0;
let sprites =
unsafe { (*self.handle).querySpritesAlongLine.unwrap()(x1, y1, x2, y2, &mut len) };
let mut result = Vec::new();
for i in 0..len {
let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
result.push(Sprite::from_ref(*sprite));
}
result
}
pub fn query_sprite_info_along_line(
&self,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
) -> Vec<SpriteQueryInfo> {
let mut len = 0;
let info =
unsafe { (*self.handle).querySpriteInfoAlongLine.unwrap()(x1, y1, x2, y2, &mut len) };
let mut result = Vec::new();
for i in 0..len {
let info = unsafe { info.offset(i as isize).as_ref().unwrap() };
result.push(SpriteQueryInfo::new(info));
}
result
}
pub fn overlapping_sprites(&self, sprite: &Sprite) -> Vec<Sprite> {
let mut len = 0;
let sprites =
unsafe { (*self.handle).overlappingSprites.unwrap()(sprite.handle, &mut len) };
let mut result = Vec::new();
for i in 0..len {
let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
result.push(Sprite::from_ref(*sprite));
}
result
}
pub fn all_overlapping_sprites(&self) -> Vec<Sprite> {
let mut len = 0;
let sprites = unsafe { (*self.handle).allOverlappingSprites.unwrap()(&mut len) };
let mut result = Vec::new();
for i in 0..len {
let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
result.push(Sprite::from_ref(*sprite));
}
result
}
pub(crate) fn set_stencil_pattern(&self, sprite: *mut sys::LCDSprite, pattern: *mut u8) {
unsafe {
(*self.handle).setStencilPattern.unwrap()(sprite, pattern);
}
}
pub(crate) fn clear_stencil(&self, sprite: *mut sys::LCDSprite) {
unsafe {
(*self.handle).clearStencil.unwrap()(sprite);
}
}
pub(crate) fn set_userdata(
&self,
sprite: *mut sys::LCDSprite,
userdata: *mut core::ffi::c_void,
) {
unsafe {
(*self.handle).setUserdata.unwrap()(sprite, userdata);
}
}
pub(crate) fn get_userdata(&self, sprite: *mut sys::LCDSprite) -> *mut core::ffi::c_void {
unsafe { (*self.handle).getUserdata.unwrap()(sprite) }
}
pub(crate) fn set_stencil_image(
&self,
sprite: *mut sys::LCDSprite,
stencil: *mut sys::LCDBitmap,
tile: i32,
) {
unsafe {
(*self.handle).setStencilImage.unwrap()(sprite, stencil, tile);
}
}
}
#[derive(Debug)]
pub struct Sprite {
handle: *mut sys::LCDSprite,
forget: bool,
}
impl PartialEq for Sprite {
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle
}
}
impl Eq for Sprite {}
unsafe impl Sync for Sprite {}
unsafe impl Send for Sprite {}
impl Default for Sprite {
fn default() -> Self {
Self::new()
}
}
impl Sprite {
pub(crate) fn from(handle: *mut sys::LCDSprite) -> Self {
Self {
handle,
forget: false,
}
}
pub(crate) fn from_ref(handle: *mut sys::LCDSprite) -> Self {
Self {
handle,
forget: true,
}
}
pub fn new() -> Self {
PLAYDATE.sprite.new_sprite()
}
pub fn get_position(&self) -> (f32, f32) {
PLAYDATE.sprite.get_position(self.handle)
}
pub fn set_bounds(&self, bounds: Rect<f32>) {
PLAYDATE.sprite.set_bounds(self.handle, bounds);
}
pub fn get_bounds(&self) -> Rect<f32> {
PLAYDATE.sprite.get_bounds(self.handle)
}
pub fn move_to(&self, x: f32, y: f32) {
PLAYDATE.sprite.move_to(self.handle, x, y);
}
pub fn move_by(&self, dx: f32, dy: f32) {
PLAYDATE.sprite.move_by(self.handle, dx, dy);
}
pub fn set_image(&self, image: &Bitmap, flip: sys::LCDBitmapFlip) {
PLAYDATE.sprite.set_image(self.handle, image.handle, flip);
}
pub fn get_image(&self) -> Bitmap {
Bitmap::from_ref(PLAYDATE.sprite.get_image(self.handle))
}
pub fn set_size(&self, width: f32, height: f32) {
PLAYDATE.sprite.set_size(self.handle, width, height);
}
pub fn set_z_index(&self, z_index: i16) {
PLAYDATE.sprite.set_z_index(self.handle, z_index);
}
pub fn get_z_index(&self) -> i16 {
PLAYDATE.sprite.get_z_index(self.handle)
}
pub fn set_draw_mode(&self, mode: sys::LCDBitmapDrawMode) {
PLAYDATE.sprite.set_draw_mode(self.handle, mode);
}
pub fn set_image_flip(&self, flip: sys::LCDBitmapFlip) {
PLAYDATE.sprite.set_image_flip(self.handle, flip);
}
pub fn get_image_flip(&self) -> sys::LCDBitmapFlip {
PLAYDATE.sprite.get_image_flip(self.handle)
}
pub fn set_stencil(&self, stencil: &Bitmap) {
PLAYDATE.sprite.set_stencil(self.handle, stencil.handle);
}
pub fn set_clip_rect(&self, clip_rect: SideOffsets2D<i32>) {
PLAYDATE.sprite.set_clip_rect(self.handle, clip_rect);
}
pub fn clear_clip_rect(&self) {
PLAYDATE.sprite.clear_clip_rect(self.handle);
}
pub fn set_updates_enabled(&self, flag: bool) {
PLAYDATE.sprite.set_updates_enabled(self.handle, flag as _);
}
pub fn updates_enabled(&self) -> bool {
PLAYDATE.sprite.updates_enabled(self.handle) == 1
}
pub fn set_collisions_enabled(&self, flag: bool) {
PLAYDATE
.sprite
.set_collisions_enabled(self.handle, flag as _);
}
pub fn collisions_enabled(&self) -> bool {
PLAYDATE.sprite.collisions_enabled(self.handle) == 1
}
pub fn set_visible(&self, flag: bool) {
PLAYDATE.sprite.set_visible(self.handle, flag as _);
}
pub fn is_visible(&self) -> bool {
PLAYDATE.sprite.is_visible(self.handle) == 1
}
pub fn set_opaque(&self, flag: bool) {
PLAYDATE.sprite.set_opaque(self.handle, flag as _);
}
pub fn mark_dirty(&self) {
PLAYDATE.sprite.mark_dirty(self.handle);
}
pub fn set_tag(&self, tag: u8) {
PLAYDATE.sprite.set_tag(self.handle, tag);
}
pub fn get_tag(&self) -> u8 {
PLAYDATE.sprite.get_tag(self.handle)
}
pub fn set_ignores_draw_offset(&self, flag: i32) {
PLAYDATE.sprite.set_ignores_draw_offset(self.handle, flag);
}
pub fn set_update_function(&self, func: sys::LCDSpriteUpdateFunction) {
PLAYDATE.sprite.set_update_function(self.handle, func);
}
pub fn set_draw_function(&self, func: sys::LCDSpriteDrawFunction) {
PLAYDATE.sprite.set_draw_function(self.handle, func);
}
pub fn set_collide_rect(&self, collide_rect: Rect<f32>) {
PLAYDATE.sprite.set_collide_rect(self.handle, collide_rect);
}
pub fn get_collide_rect(&self) -> Rect<f32> {
PLAYDATE.sprite.get_collide_rect(self.handle)
}
pub fn clear_collide_rect(&self) {
PLAYDATE.sprite.clear_collide_rect(self.handle);
}
pub fn set_collision_response_function(&self, func: sys::LCDSpriteCollisionFilterProc) {
PLAYDATE
.sprite
.set_collision_response_function(self.handle, func);
}
pub fn set_stencil_pattern(&self, pattern: *mut u8) {
PLAYDATE.sprite.set_stencil_pattern(self.handle, pattern);
}
pub fn clear_stencil(&self) {
PLAYDATE.sprite.clear_stencil(self.handle);
}
pub fn set_userdata(&self, userdata: *mut core::ffi::c_void) {
PLAYDATE.sprite.set_userdata(self.handle, userdata);
}
pub fn get_userdata(&self) -> *mut core::ffi::c_void {
PLAYDATE.sprite.get_userdata(self.handle)
}
pub fn set_stencil_image(&self, stencil: &Bitmap, tile: i32) {
PLAYDATE
.sprite
.set_stencil_image(self.handle, stencil.handle, tile);
}
}
impl Drop for Sprite {
fn drop(&mut self) {
if self.forget {
return;
}
PLAYDATE.sprite.free_sprite(self.handle as *mut _);
}
}
impl Clone for Sprite {
fn clone(&self) -> Self {
let handle = PLAYDATE.sprite.copy(self.handle as *mut _);
Self {
handle,
forget: false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SpriteCollisionInfo {
pub sprite: Sprite,
pub other: Sprite,
pub response_type: SpriteCollisionResponseType,
pub overlaps: u8,
pub ti: f32,
pub move_: Point2D<f32>,
pub normal: Vec2D<i32>,
pub touch: Point2D<f32>,
pub sprite_rect: Rect<f32>,
pub other_rect: Rect<f32>,
}
impl SpriteCollisionInfo {
fn new(info: &sys::SpriteCollisionInfo) -> Self {
Self {
sprite: Sprite::from_ref(info.sprite),
other: Sprite::from_ref(info.other),
response_type: info.responseType,
overlaps: info.overlaps,
ti: info.ti,
move_: collision_point_to_point(info.move_),
normal: collision_vec_to_vec(info.normal),
touch: collision_point_to_point(info.touch),
sprite_rect: pdrect_to_rect(info.spriteRect),
other_rect: pdrect_to_rect(info.otherRect),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SpriteQueryInfo {
pub sprite: Sprite,
pub ti1: f32,
pub ti2: f32,
pub entry_point: Point2D<f32>,
pub exit_point: Point2D<f32>,
}
impl SpriteQueryInfo {
fn new(info: &sys::SpriteQueryInfo) -> Self {
Self {
sprite: Sprite::from_ref(info.sprite),
ti1: info.ti1,
ti2: info.ti2,
entry_point: collision_point_to_point(info.entryPoint),
exit_point: collision_point_to_point(info.exitPoint),
}
}
}