1use core::cell::RefCell;
2
3use alloc::boxed::Box;
4use alloc::vec::Vec;
5use playdate_rs_sys::LCDPattern;
6
7use crate::{
8 graphics::Bitmap,
9 math::{Rect, SideOffsets, Vec2},
10 util::Ref,
11 PLAYDATE,
12};
13
14pub use sys::SpriteCollisionResponseType;
15
16pub struct PlaydateSprite {
17 handle: *const sys::playdate_sprite,
18}
19
20impl PlaydateSprite {
21 pub(crate) fn new(handle: *const sys::playdate_sprite) -> Self {
22 Self { handle }
23 }
24
25 pub fn set_always_redraw(&self, flag: bool) {
27 unsafe {
28 (*self.handle).setAlwaysRedraw.unwrap()(flag as i32);
29 }
30 }
31
32 pub fn add_dirty_rect(&self, dirty_rect: SideOffsets<i32>) {
34 unsafe {
35 (*self.handle).addDirtyRect.unwrap()(dirty_rect.into());
36 }
37 }
38
39 pub fn draw_sprites(&self) {
41 unsafe {
42 (*self.handle).drawSprites.unwrap()();
43 }
44 }
45
46 pub fn update_and_draw_sprites(&self) {
48 unsafe {
49 (*self.handle).updateAndDrawSprites.unwrap()();
50 }
51 }
52
53 pub fn add_sprite(&self, sprite: impl AsRef<Sprite>) {
55 unsafe {
56 (*self.handle).addSprite.unwrap()(sprite.as_ref().handle);
57 }
58 }
59
60 pub fn remove_sprite(&self, sprite: impl AsRef<Sprite>) {
62 unsafe {
63 (*self.handle).removeSprite.unwrap()(sprite.as_ref().handle);
64 }
65 }
66
67 pub fn remove_sprites(&self, sprites: &[&Sprite]) {
69 let mut sprites: Vec<_> = sprites.iter().map(|s| s.handle).collect();
70 unsafe {
71 (*self.handle).removeSprites.unwrap()(sprites.as_mut_ptr(), sprites.len() as i32);
72 }
73 }
74
75 pub fn remove_all_sprites(&self) {
77 unsafe {
78 (*self.handle).removeAllSprites.unwrap()();
79 }
80 }
81
82 pub fn get_sprite_count(&self) -> usize {
84 unsafe { (*self.handle).getSpriteCount.unwrap()() as _ }
85 }
86
87 pub fn set_clip_rects_in_range(&self, clip_rect: SideOffsets<i32>, start_z: i32, end_z: i32) {
89 unsafe {
90 (*self.handle).setClipRectsInRange.unwrap()(clip_rect.into(), start_z, end_z);
91 }
92 }
93
94 pub fn clear_clip_rects_in_range(&self, start_z: i32, end_z: i32) {
96 unsafe {
97 (*self.handle).clearClipRectsInRange.unwrap()(start_z, end_z);
98 }
99 }
100
101 pub fn reset_collision_world(&self) {
103 unsafe {
104 (*self.handle).resetCollisionWorld.unwrap()();
105 }
106 }
107
108 pub fn query_sprites_at_point(&self, pos: Vec2<f32>) -> Vec<Ref<Sprite>> {
109 let mut len = 0;
110 let sprites =
111 unsafe { (*self.handle).querySpritesAtPoint.unwrap()(pos.x, pos.y, &mut len) };
112 let mut result = Vec::new();
113 for i in 0..len {
114 let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
115 result.push(Sprite::from_ref(*sprite));
116 }
117 PLAYDATE.system.realloc(sprites as _, 0);
118 result
119 }
120
121 pub fn query_sprites_in_rect(&self, rect: Rect<f32>) -> Vec<Ref<Sprite>> {
122 let mut len = 0;
123 let sprites = unsafe {
124 (*self.handle).querySpritesInRect.unwrap()(
125 rect.x,
126 rect.y,
127 rect.width,
128 rect.height,
129 &mut len,
130 )
131 };
132 let mut result = Vec::new();
133 for i in 0..len {
134 let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
135 result.push(Sprite::from_ref(*sprite));
136 }
137 PLAYDATE.system.realloc(sprites as _, 0);
138 result
139 }
140
141 pub fn query_sprites_along_line(&self, x1: f32, y1: f32, x2: f32, y2: f32) -> Vec<Ref<Sprite>> {
142 let mut len = 0;
143 let sprites =
144 unsafe { (*self.handle).querySpritesAlongLine.unwrap()(x1, y1, x2, y2, &mut len) };
145 let mut result = Vec::new();
146 for i in 0..len {
147 let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
148 result.push(Sprite::from_ref(*sprite));
149 }
150 PLAYDATE.system.realloc(sprites as _, 0);
151 result
152 }
153
154 pub fn query_sprite_info_along_line(
155 &self,
156 x1: f32,
157 y1: f32,
158 x2: f32,
159 y2: f32,
160 ) -> Vec<SpriteQueryInfo> {
161 let mut len = 0;
162 let info =
163 unsafe { (*self.handle).querySpriteInfoAlongLine.unwrap()(x1, y1, x2, y2, &mut len) };
164 let mut result = Vec::new();
165 for i in 0..len {
166 let info = unsafe { info.offset(i as isize).as_ref().unwrap() };
167 result.push(SpriteQueryInfo::new(info));
168 }
169 PLAYDATE.system.realloc(info as _, 0);
170 result
171 }
172
173 pub fn all_overlapping_sprites(&self) -> Vec<Ref<Sprite>> {
175 let mut len = 0;
176 let sprites = unsafe { (*self.handle).allOverlappingSprites.unwrap()(&mut len) };
177 let mut result = Vec::new();
178 for i in 0..len {
179 let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
180 result.push(Sprite::from_ref(*sprite));
181 }
182 PLAYDATE.system.realloc(sprites as _, 0);
183 result
184 }
185}
186
187#[derive(Debug)]
188pub struct Sprite {
189 handle: *mut sys::LCDSprite,
190}
191
192impl PartialEq for Sprite {
193 fn eq(&self, other: &Self) -> bool {
194 self.handle == other.handle
195 }
196}
197
198impl Eq for Sprite {}
199
200unsafe impl Sync for Sprite {}
201unsafe impl Send for Sprite {}
202
203impl Default for Sprite {
204 fn default() -> Self {
205 Self::new()
206 }
207}
208
209type DataCell<F> = RefCell<Option<F>>;
210type UpdateFn = Box<dyn Fn(&Sprite)>;
211type DrawFn = Box<dyn Fn(&Sprite, Rect<f32>, Rect<f32>)>;
212type CollisionResponseFn = Box<dyn Fn(&Sprite, &Sprite) -> SpriteCollisionResponseType>;
213
214#[derive(Default)]
215struct SpriteData {
216 update_fn: DataCell<UpdateFn>,
217 draw_fn: DataCell<DrawFn>,
218 collision_response_fn: DataCell<CollisionResponseFn>,
219}
220
221impl Sprite {
222 pub(crate) fn from(handle: *mut sys::LCDSprite) -> Self {
223 Self { handle }
224 }
225
226 pub(crate) fn from_ref<'a>(handle: *mut sys::LCDSprite) -> Ref<'a, Self> {
227 Ref::new(Self { handle })
228 }
229
230 pub fn new() -> Self {
232 Self::from(unsafe { (*PLAYDATE.sprite.handle).newSprite.unwrap()() })
233 }
234
235 pub fn get_position(&self) -> Vec2<f32> {
237 let mut x = 0.0;
238 let mut y = 0.0;
239 unsafe {
240 (*PLAYDATE.sprite.handle).getPosition.unwrap()(self.handle, &mut x, &mut y);
241 }
242 Vec2::new(x, y)
243 }
244
245 pub fn set_bounds(&self, bounds: Rect<f32>) {
247 unsafe { (*PLAYDATE.sprite.handle).setBounds.unwrap()(self.handle, bounds.into()) }
248 }
249
250 pub fn get_bounds(&self) -> Rect<f32> {
253 unsafe { (*PLAYDATE.sprite.handle).getBounds.unwrap()(self.handle).into() }
254 }
255
256 pub fn move_to(&self, pos: Vec2<f32>) {
258 unsafe { (*PLAYDATE.sprite.handle).moveTo.unwrap()(self.handle, pos.x, pos.y) }
259 }
260
261 pub fn move_by(&self, delta: Vec2<f32>) {
263 unsafe { (*PLAYDATE.sprite.handle).moveBy.unwrap()(self.handle, delta.x, delta.y) }
264 }
265
266 pub fn set_image(&self, image: Bitmap, flip: sys::LCDBitmapFlip) {
268 if let Some(old_image) = self.get_image() {
270 let _boxed = Bitmap::from(old_image.handle);
271 }
272 unsafe {
274 (*PLAYDATE.sprite.handle).setImage.unwrap()(self.handle, image.as_ref().handle, flip)
275 };
276 core::mem::forget(image);
278 }
279
280 pub fn get_image(&self) -> Option<Ref<Bitmap>> {
282 let ptr = unsafe { (*PLAYDATE.sprite.handle).getImage.unwrap()(self.handle) };
283 if ptr.is_null() {
284 None
285 } else {
286 Some(Bitmap::from_ref(ptr))
287 }
288 }
289
290 pub fn set_size(&self, width: f32, height: f32) {
292 unsafe { (*PLAYDATE.sprite.handle).setSize.unwrap()(self.handle, width, height) }
293 }
294
295 pub fn set_z_index(&self, z_index: i16) {
297 unsafe { (*PLAYDATE.sprite.handle).setZIndex.unwrap()(self.handle, z_index) }
298 }
299
300 pub fn get_z_index(&self) -> i16 {
302 unsafe { (*PLAYDATE.sprite.handle).getZIndex.unwrap()(self.handle) }
303 }
304
305 pub fn set_draw_mode(&self, mode: sys::LCDBitmapDrawMode) {
307 unsafe { (*PLAYDATE.sprite.handle).setDrawMode.unwrap()(self.handle, mode) }
308 }
309
310 pub fn set_image_flip(&self, flip: sys::LCDBitmapFlip) {
312 unsafe { (*PLAYDATE.sprite.handle).setImageFlip.unwrap()(self.handle, flip) }
313 }
314
315 pub fn get_image_flip(&self) -> sys::LCDBitmapFlip {
317 unsafe { (*PLAYDATE.sprite.handle).getImageFlip.unwrap()(self.handle) }
318 }
319
320 pub fn set_stencil(&self, stencil: impl AsRef<Bitmap>) {
322 unsafe {
323 (*PLAYDATE.sprite.handle).setStencil.unwrap()(self.handle, stencil.as_ref().handle)
324 };
325 }
326
327 pub fn set_clip_rect(&self, clip_rect: SideOffsets<i32>) {
329 unsafe { (*PLAYDATE.sprite.handle).setClipRect.unwrap()(self.handle, clip_rect.into()) };
330 }
331
332 pub fn clear_clip_rect(&self) {
334 unsafe { (*PLAYDATE.sprite.handle).clearClipRect.unwrap()(self.handle) };
335 }
336
337 pub fn set_updates_enabled(&self, flag: bool) {
339 unsafe { (*PLAYDATE.sprite.handle).setUpdatesEnabled.unwrap()(self.handle, flag as _) };
340 }
341
342 pub fn updates_enabled(&self) -> bool {
344 unsafe { (*PLAYDATE.sprite.handle).updatesEnabled.unwrap()(self.handle) == 1 }
345 }
346
347 pub fn set_collisions_enabled(&self, flag: bool) {
349 unsafe { (*PLAYDATE.sprite.handle).setCollisionsEnabled.unwrap()(self.handle, flag as _) };
350 }
351
352 pub fn collisions_enabled(&self) -> bool {
354 unsafe { (*PLAYDATE.sprite.handle).collisionsEnabled.unwrap()(self.handle) == 1 }
355 }
356
357 pub fn set_visible(&self, flag: bool) {
359 unsafe { (*PLAYDATE.sprite.handle).setVisible.unwrap()(self.handle, flag as _) };
360 }
361
362 pub fn is_visible(&self) -> bool {
364 unsafe { (*PLAYDATE.sprite.handle).isVisible.unwrap()(self.handle) == 1 }
365 }
366
367 pub fn set_opaque(&self, flag: bool) {
369 unsafe { (*PLAYDATE.sprite.handle).setOpaque.unwrap()(self.handle, flag as _) };
370 }
371
372 pub fn mark_dirty(&self) {
374 unsafe { (*PLAYDATE.sprite.handle).markDirty.unwrap()(self.handle) };
375 }
376
377 pub fn set_tag(&self, tag: u8) {
379 unsafe { (*PLAYDATE.sprite.handle).setTag.unwrap()(self.handle, tag) }
380 }
381
382 pub fn get_tag(&self) -> u8 {
384 unsafe { (*PLAYDATE.sprite.handle).getTag.unwrap()(self.handle) }
385 }
386
387 pub fn set_ignores_draw_offset(&self, flag: i32) {
391 unsafe { (*PLAYDATE.sprite.handle).setIgnoresDrawOffset.unwrap()(self.handle, flag) };
392 }
393
394 pub fn set_update_function(&self, func: impl Fn(&Sprite) + 'static) {
396 *self.get_userdata().update_fn.borrow_mut() = Some(Box::new(func));
397 extern "C" fn callback(sprite: *mut sys::LCDSprite) {
398 let sprite = Sprite::from_ref(sprite);
399 let func = sprite.get_userdata().update_fn.borrow();
400 let func = &func.as_ref().unwrap();
401 func(&sprite)
402 }
403 unsafe {
404 (*PLAYDATE.sprite.handle).setUpdateFunction.unwrap()(self.handle, Some(callback))
405 };
406 }
407
408 pub fn set_draw_function(&self, func: impl Fn(&Sprite, Rect<f32>, Rect<f32>) + 'static) {
410 *self.get_userdata().draw_fn.borrow_mut() = Some(Box::new(func));
411 extern "C" fn callback(
412 sprite: *mut sys::LCDSprite,
413 bounds: sys::PDRect,
414 drawrect: sys::PDRect,
415 ) {
416 let sprite = Sprite::from_ref(sprite);
417 let func = sprite.get_userdata().draw_fn.borrow();
418 let func = &func.as_ref().unwrap();
419 func(&sprite, bounds.into(), drawrect.into())
420 }
421 unsafe { (*PLAYDATE.sprite.handle).setDrawFunction.unwrap()(self.handle, Some(callback)) };
422 }
423
424 pub fn set_collide_rect(&self, collide_rect: Rect<f32>) {
426 unsafe {
427 (*PLAYDATE.sprite.handle).setCollideRect.unwrap()(self.handle, collide_rect.into());
428 }
429 }
430
431 pub fn get_collide_rect(&self) -> Rect<f32> {
433 unsafe { (*PLAYDATE.sprite.handle).getCollideRect.unwrap()(self.handle).into() }
434 }
435
436 pub fn clear_collide_rect(&self) {
438 unsafe { (*PLAYDATE.sprite.handle).clearCollideRect.unwrap()(self.handle) };
439 }
440
441 pub fn set_collision_response_function(
443 &self,
444 func: impl Fn(&Sprite, &Sprite) -> SpriteCollisionResponseType + 'static,
445 ) {
446 *self.get_userdata().collision_response_fn.borrow_mut() = Some(Box::new(func));
447 extern "C" fn callback(
448 sprite: *mut sys::LCDSprite,
449 other: *mut sys::LCDSprite,
450 ) -> SpriteCollisionResponseType {
451 let sprite = Sprite::from_ref(sprite);
452 let other = Sprite::from_ref(other);
453 let func = sprite.get_userdata().collision_response_fn.borrow();
454 let func = &func.as_ref().unwrap();
455 func(&sprite, &other)
456 }
457 unsafe {
458 (*PLAYDATE.sprite.handle)
459 .setCollisionResponseFunction
460 .unwrap()(self.handle, Some(callback));
461 }
462 }
463
464 pub fn set_stencil_pattern(&self, mut pattern: LCDPattern) {
466 unsafe {
467 (*PLAYDATE.sprite.handle).setStencilPattern.unwrap()(self.handle, pattern.as_mut_ptr())
468 };
469 }
470
471 pub fn clear_stencil(&self) {
473 unsafe { (*PLAYDATE.sprite.handle).clearStencil.unwrap()(self.handle) };
474 }
475
476 fn get_userdata(&self) -> &SpriteData {
478 let ptr = unsafe { (*PLAYDATE.sprite.handle).getUserdata.unwrap()(self.handle) };
479 if ptr.is_null() {
480 let ptr = Box::into_raw(Box::<SpriteData>::default());
481 unsafe { (*PLAYDATE.sprite.handle).setUserdata.unwrap()(self.handle, ptr as _) };
482 }
483 let ptr = unsafe { (*PLAYDATE.sprite.handle).getUserdata.unwrap()(self.handle) };
484 unsafe { &*(ptr as *mut SpriteData) }
485 }
486
487 fn drop_userdata(&self) {
488 let ptr = unsafe { (*PLAYDATE.sprite.handle).getUserdata.unwrap()(self.handle) };
489 if !ptr.is_null() {
490 let _boxed = unsafe { Box::from_raw(ptr as *mut SpriteData) };
491 }
492 }
493
494 pub fn set_stencil_image(&self, stencil: impl AsRef<Bitmap>, tile: i32) {
496 unsafe {
497 (*PLAYDATE.sprite.handle).setStencilImage.unwrap()(
498 self.handle,
499 stencil.as_ref().handle,
500 tile,
501 )
502 };
503 }
504
505 pub fn check_collisions(&self, move_goal: Vec2<f32>) -> Vec<SpriteCollisionInfo> {
507 let mut actual_x = 0.0;
508 let mut actual_y = 0.0;
509 let mut len = 0;
510 let info = unsafe {
511 (*PLAYDATE.sprite.handle).checkCollisions.unwrap()(
512 self.handle,
513 move_goal.x,
514 move_goal.y,
515 &mut actual_x,
516 &mut actual_y,
517 &mut len,
518 )
519 };
520 let mut result = Vec::new();
521 for i in 0..len {
522 let info = unsafe { info.offset(i as isize).as_ref().unwrap() };
523 result.push(SpriteCollisionInfo::new(info));
524 }
525 PLAYDATE.system.realloc(info as _, 0);
527 result
528 }
529
530 pub fn move_with_collisions(&self, goal: Vec2<f32>) -> (Vec2<f32>, Vec<SpriteCollisionInfo>) {
532 let mut actual_x = 0.0;
533 let mut actual_y = 0.0;
534 let mut len = 0;
535 let info = unsafe {
536 (*PLAYDATE.sprite.handle).moveWithCollisions.unwrap()(
537 self.handle,
538 goal.x,
539 goal.y,
540 &mut actual_x,
541 &mut actual_y,
542 &mut len,
543 )
544 };
545 let mut result = Vec::new();
546 for i in 0..len {
547 let info = unsafe { info.offset(i as isize).as_ref().unwrap() };
548 result.push(SpriteCollisionInfo::new(info));
549 }
550 PLAYDATE.system.realloc(info as _, 0);
552 (vec2!(actual_x, actual_y), result)
553 }
554
555 pub fn overlapping_sprites(&self) -> Vec<Ref<Sprite>> {
557 let mut len = 0;
558 let sprites =
559 unsafe { (*PLAYDATE.sprite.handle).overlappingSprites.unwrap()(self.handle, &mut len) };
560 let mut result = Vec::new();
561 for i in 0..len {
562 let sprite = unsafe { sprites.offset(i as isize).as_ref().unwrap() };
563 result.push(Sprite::from_ref(*sprite));
564 }
565 PLAYDATE.system.realloc(sprites as _, 0);
566 result
567 }
568}
569
570impl AsRef<Self> for Sprite {
571 fn as_ref(&self) -> &Self {
572 self
573 }
574}
575
576impl Drop for Sprite {
577 fn drop(&mut self) {
578 self.drop_userdata();
580 unsafe { (*PLAYDATE.sprite.handle).freeSprite.unwrap()(self.handle) };
581 }
582}
583
584impl Clone for Sprite {
585 fn clone(&self) -> Self {
586 let handle = unsafe { (*PLAYDATE.sprite.handle).copy.unwrap()(self.handle) };
587 Self { handle }
588 }
589}
590
591#[derive(Debug, Clone, PartialEq)]
592pub struct SpriteCollisionInfo<'a> {
593 pub sprite: Ref<'a, Sprite>,
594 pub other: Ref<'a, Sprite>,
595 pub response_type: SpriteCollisionResponseType,
596 pub overlaps: u8,
597 pub ti: f32,
598 pub move_: Vec2<f32>,
599 pub normal: Vec2<i32>,
600 pub touch: Vec2<f32>,
601 pub sprite_rect: Rect<f32>,
602 pub other_rect: Rect<f32>,
603}
604
605impl<'a> SpriteCollisionInfo<'a> {
606 fn new(info: &sys::SpriteCollisionInfo) -> Self {
607 Self {
608 sprite: Sprite::from_ref(info.sprite),
609 other: Sprite::from_ref(info.other),
610 response_type: info.responseType,
611 overlaps: info.overlaps,
612 ti: info.ti,
613 move_: info.move_.into(),
614 normal: info.normal.into(),
615 touch: info.touch.into(),
616 sprite_rect: info.spriteRect.into(),
617 other_rect: info.otherRect.into(),
618 }
619 }
620}
621
622#[derive(Debug, Clone, PartialEq)]
623pub struct SpriteQueryInfo<'a> {
624 pub sprite: Ref<'a, Sprite>,
625 pub ti1: f32,
626 pub ti2: f32,
627 pub entry_point: Vec2<f32>,
628 pub exit_point: Vec2<f32>,
629}
630
631impl<'a> SpriteQueryInfo<'a> {
632 fn new(info: &sys::SpriteQueryInfo) -> Self {
633 Self {
634 sprite: Sprite::from_ref(info.sprite),
635 ti1: info.ti1,
636 ti2: info.ti2,
637 entry_point: info.entryPoint.into(),
638 exit_point: info.exitPoint.into(),
639 }
640 }
641}