playdate_sprite/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3extern crate sys;
4extern crate alloc;
5
6use core::ffi::c_int;
7use core::ffi::c_float;
8
9use sys::traits::AsRaw;
10use sys::ffi::SpriteQueryInfo;
11use sys::ffi::LCDRect;
12use sys::ffi::LCDSprite;
13
14pub mod ext;
15mod sprite;
16pub mod api;
17
18pub mod callback {
19	pub mod draw;
20	pub mod update;
21	pub mod collision;
22}
23
24pub mod prelude {
25	pub use super::sprite::*;
26	pub use super::api::Api as _;
27	pub use super::api::Default as DefaultSpriteApi;
28	pub use super::callback::draw::SpriteDraw;
29	pub use super::callback::update::SpriteUpdate;
30	pub use super::callback::collision::SpriteCollisionResponse;
31
32	pub use super::{TypedSprite, SpriteApi};
33}
34
35pub use sprite::*;
36use crate::api::Api;
37
38
39/// If set to `true`, causes all sprites to draw each frame, whether or not they have been marked dirty.
40/// This may speed up the performance of your game if the system’s dirty rect tracking is taking up too much time -
41/// for example if there are many sprites moving around on screen at once.
42///
43/// Equivalent to [`sys::ffi::playdate_sprite::setAlwaysRedraw`]
44#[doc(alias = "sys::ffi::playdate_sprite::setAlwaysRedraw")]
45pub fn set_always_redraw(value: bool) {
46	let f = api::Api::set_always_redraw(&api::Default);
47	unsafe { f(value.into()) }
48}
49
50/// Marks the given dirty_rect (in screen coordinates) as needing a redraw.
51///
52/// Graphics drawing functions now call this automatically,
53/// adding their drawn areas to the sprite’s dirty list,
54/// so there’s usually no need to call this manually.
55///
56/// Equivalent to [`sys::ffi::playdate_sprite::addDirtyRect`]
57#[doc(alias = "sys::ffi::playdate_sprite::addDirtyRect")]
58pub fn add_dirty_rect(rect: LCDRect) {
59	let f = api::Api::add_dirty_rect(&api::Default);
60	unsafe { f(rect) }
61}
62
63/// Draws every sprite in the display list.
64///
65/// Equivalent to [`sys::ffi::playdate_sprite::drawSprites`]
66#[doc(alias = "sys::ffi::playdate_sprite::drawSprites")]
67pub fn draw_sprites() {
68	let f = api::Api::draw_sprites(&api::Default);
69	unsafe { f() }
70}
71
72/// Updates and draws every sprite in the display list.
73///
74/// Equivalent to [`sys::ffi::playdate_sprite::updateAndDrawSprites`]
75#[doc(alias = "sys::ffi::playdate_sprite::updateAndDrawSprites")]
76pub fn update_and_draw_sprites() {
77	let f = api::Api::update_and_draw_sprites(&api::Default);
78	unsafe { f() }
79}
80
81
82/// Adds the given sprite to the display list,
83/// so that it is drawn in the current scene.
84///
85/// See also [`Sprite::add`]
86///
87/// Equivalent to [`sys::ffi::playdate_sprite::addSprite`]
88#[doc(alias = "sys::ffi::playdate_sprite::addSprite")]
89pub fn add_sprite(sprite: &impl AnySprite) {
90	let f = sprite.api_ref().add_sprite();
91	unsafe { f(sprite.as_raw()) }
92}
93
94/// Removes the given sprite from the display list.
95///
96/// See also [`Sprite::remove`]
97///
98/// Equivalent to [`sys::ffi::playdate_sprite::removeSprite`]
99#[doc(alias = "sys::ffi::playdate_sprite::removeSprite")]
100pub fn remove_sprite(sprite: &impl AnySprite) {
101	let f = sprite.api_ref().remove_sprite();
102	unsafe { f(sprite.as_raw()) }
103}
104
105/// Removes all of the given sprites from the display list.
106///
107/// Equivalent to [`sys::ffi::playdate_sprite::removeSprites`]
108#[doc(alias = "sys::ffi::playdate_sprite::removeSprites")]
109pub fn remove_sprites(sprites: &[impl AnySprite]) {
110	let mut ptrs = alloc::vec::Vec::with_capacity(sprites.len());
111	ptrs.extend(sprites.into_iter().map(|sp| unsafe { sp.as_raw() }));
112	let f = sprites.first()
113	               .map(|sp| sp.api_ref().remove_sprites())
114	               .unwrap_or(api::Default.remove_sprites());
115	unsafe { f(ptrs.as_mut_ptr(), sprites.len() as _) }
116	drop(ptrs);
117}
118
119
120/// Removes all sprites from the display list.
121///
122/// Equivalent to [`sys::ffi::playdate_sprite::removeAllSprites`]
123#[doc(alias = "sys::ffi::playdate_sprite::removeAllSprites")]
124pub fn remove_all_sprites() {
125	let f = api::Api::remove_all_sprites(&api::Default);
126	unsafe { f() }
127}
128
129
130/// Returns the total number of sprites in the display list.
131///
132/// Equivalent to [`sys::ffi::playdate_sprite::getSpriteCount`]
133#[doc(alias = "sys::ffi::playdate_sprite::getSpriteCount")]
134pub fn sprite_count() -> c_int {
135	let f = api::Api::get_sprite_count(&api::Default);
136	unsafe { f() }
137}
138
139
140/// Sets the clipping rectangle for all sprites with a Z index within `start_z` and `end_z` __inclusive__.
141///
142/// Equivalent to [`sys::ffi::playdate_sprite::setClipRectsInRange`]
143#[doc(alias = "sys::ffi::playdate_sprite::setClipRectsInRange")]
144pub fn set_clip_rects_in_range(clip: LCDRect, start_z: c_int, end_z: c_int) {
145	let f = api::Api::set_clip_rects_in_range(&api::Default);
146	unsafe { f(clip, start_z, end_z) }
147}
148
149/// Clears the clipping rectangle for all sprites with a Z index within `start_z` and `end_z` __inclusive__.
150///
151/// Equivalent to [`sys::ffi::playdate_sprite::clearClipRectsInRange`]
152#[doc(alias = "sys::ffi::playdate_sprite::clearClipRectsInRange")]
153pub fn clear_clip_rects_in_range(start_z: c_int, end_z: c_int) {
154	let f = api::Api::clear_clip_rects_in_range(&api::Default);
155	unsafe { f(start_z, end_z) }
156}
157
158
159/// Frees and reallocates internal collision data, resetting everything to its default state.
160///
161/// Equivalent to [`sys::ffi::playdate_sprite::resetCollisionWorld`]
162#[doc(alias = "sys::ffi::playdate_sprite::resetCollisionWorld")]
163pub fn reset_collision_world() {
164	let f = api::Api::reset_collision_world(&api::Default);
165	unsafe { f() }
166}
167
168
169/// Returns an slice of all sprites with collision rects containing the point at `x`, `y`.
170///
171/// Equivalent to [`sys::ffi::playdate_sprite::querySpritesAtPoint`]
172#[doc(alias = "sys::ffi::playdate_sprite::querySpritesAtPoint")]
173pub fn query_sprites_at_point(x: c_float, y: c_float) -> &'static [SpriteRef] {
174	let mut len: c_int = 0;
175	let api = api::Default;
176	let f = api.query_sprites_at_point();
177	let ptr = unsafe { f(x, y, &mut len) };
178	let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
179	unsafe { core::mem::transmute(slice) }
180}
181
182/// Returns an slice of all sprites with collision rects
183/// that intersect the `width` by `height` rect at `x`, `y`.
184///
185/// Equivalent to [`sys::ffi::playdate_sprite::querySpritesInRect`]
186#[doc(alias = "sys::ffi::playdate_sprite::querySpritesInRect")]
187pub fn query_sprites_in_rect(x: c_float, y: c_float, width: c_float, height: c_float) -> &'static [SpriteRef] {
188	let mut len: c_int = 0;
189	let f = api::Api::query_sprites_in_rect(&api::Default);
190	let ptr = unsafe { f(x, y, width, height, &mut len) };
191	let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
192	unsafe { core::mem::transmute(slice) }
193}
194
195/// Returns an slice of all sprites with collision rects
196/// that intersect the line connecting `x1`, `y1` and `x2`, `y2`.
197///
198/// Equivalent to [`sys::ffi::playdate_sprite::querySpritesAlongLine`]
199#[doc(alias = "sys::ffi::playdate_sprite::querySpritesAlongLine")]
200pub fn query_sprites_along_line(x1: c_float, y1: c_float, x2: c_float, y2: c_float) -> &'static [SpriteRef] {
201	let mut len: c_int = 0;
202	let f = api::Api::query_sprites_along_line(&api::Default);
203	let ptr = unsafe { f(x1, y1, x2, y2, &mut len) };
204	let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
205	unsafe { core::mem::transmute(slice) }
206}
207
208/// Returns an slice of [`SpriteQueryInfo`]s for all sprites with collision rects
209/// that intersect the line connecting `x1`, `y1` and `x2`, `y2`.
210///
211/// If you don’t need this information, use [`query_sprites_along_line`] as it will be faster.
212///
213/// Equivalent to [`sys::ffi::playdate_sprite::querySpriteInfoAlongLine`]
214#[doc(alias = "sys::ffi::playdate_sprite::querySpriteInfoAlongLine")]
215pub fn query_sprite_info_along_line(x1: c_float,
216                                    y1: c_float,
217                                    x2: c_float,
218                                    y2: c_float)
219                                    -> &'static [SpriteQueryInfo] {
220	let mut len: c_int = 0;
221	let f = api::Api::query_sprite_info_along_line(&api::Default);
222	let ptr = unsafe { f(x1, y1, x2, y2, &mut len) };
223	unsafe { core::slice::from_raw_parts(ptr, len as _) }
224}
225
226
227/// Returns an slice of all sprites that have collide rects that are currently overlapping.
228///
229/// Each consecutive pair of sprites is overlapping (eg. 0 & 1 overlap, 2 & 3 overlap, etc).
230///
231/// Equivalent to [`sys::ffi::playdate_sprite::allOverlappingSprites`]
232#[doc(alias = "sys::ffi::playdate_sprite::allOverlappingSprites")]
233pub fn all_overlapping_sprites() -> &'static [SpriteRef] {
234	let f = api::Api::all_overlapping_sprites(&api::Default);
235	let mut len: c_int = 0;
236	let ptr = unsafe { f(&mut len) };
237	let slice = unsafe { core::slice::from_raw_parts(ptr, len as _) };
238	unsafe { core::mem::transmute(slice) }
239}
240
241
242pub trait AnySprite: AsRaw<Type = LCDSprite> + SpriteApi {}
243impl<T: AnySprite> AnySprite for &'_ T {}
244
245
246pub trait SpriteApi {
247	/// Type of inner API access-point.
248	type Api: api::Api;
249
250	/// Get a copy of inner api access point.
251	fn api(&self) -> Self::Api
252		where Self::Api: Copy;
253
254	/// Get a ref to inner api access point.
255	fn api_ref(&self) -> &Self::Api;
256}
257
258impl<T: SpriteApi> SpriteApi for &'_ T {
259	type Api = T::Api;
260
261	fn api(&self) -> Self::Api
262		where Self::Api: Copy {
263		(*self).api()
264	}
265
266	fn api_ref(&self) -> &Self::Api { (*self).api_ref() }
267}
268
269
270/// Represents strictly typed sprite, includes associated user-data and free-on-drop flag.
271pub trait TypedSprite: AsRaw<Type = LCDSprite> + SpriteApi {
272	/// Associated user-data with sprite.
273	type Userdata;
274	/// Should be freed when sprite is dropped.
275	const FREE_ON_DROP: bool = true;
276}
277
278
279/// Type of sprite, includes associated user-data and free-on-drop flag.
280pub trait SpriteType {
281	/// Type of API access-point.
282	type Api: api::Api;
283
284	/// Associated user-data with sprite.
285	type Userdata;
286
287	/// Should be freed when sprite is dropped.
288	const FREE_ON_DROP: bool = false;
289}
290
291impl<T: TypedSprite> SpriteType for T {
292	type Api = <T as SpriteApi>::Api;
293	type Userdata = <T as TypedSprite>::Userdata;
294	const FREE_ON_DROP: bool = <T as TypedSprite>::FREE_ON_DROP;
295}
296
297
298pub mod utils {
299	use core::ops::Deref;
300
301	/// C array syzed at runtime.
302	#[must_use]
303	#[repr(transparent)]
304	pub struct Arr<'t, T>(pub(super) &'t [T]);
305
306	impl<T> Drop for Arr<'_, T> {
307		fn drop(&mut self) {
308			let p = self.0.as_ptr() as _;
309
310			#[inline]
311			const fn inner<T>(len: usize) -> core::alloc::Layout {
312				if let Ok(l) = core::alloc::Layout::array::<T>(len) {
313					l
314				} else {
315					use core::mem::{size_of, align_of};
316					let (size, align) = (size_of::<T>(), align_of::<T>());
317					unsafe { core::alloc::Layout::from_size_align_unchecked(size.unchecked_mul(len), align) }
318				}
319			}
320
321			let l = inner::<T>(self.0.len());
322			unsafe {
323				// We could simply `sys::allocator::dealloc(p)`, but we have to use SYSTEM GLOBAL allocator,
324				// which can be a user's custom allocator, not that one in `playdate-sys`.
325				alloc::alloc::dealloc(p, l);
326			};
327		}
328	}
329
330	impl<T> Deref for Arr<'_, T> {
331		type Target = [T];
332		fn deref(&self) -> &Self::Target { self.0 }
333	}
334	impl<T> AsRef<[T]> for Arr<'_, T> {
335		fn as_ref(&self) -> &[T] { self.0 }
336	}
337}