yareio_sys/
lib.rs

1#![doc = include_str!("../README.md")]
2
3#[cfg(all(
4    any(
5        all(feature = "circles", any(feature = "squares", feature = "triangles")),
6        all(feature = "squares", feature = "triangles")
7    ),
8    not(doc)
9))]
10compile_error!(
11    r#"Only one of the features "circles", "squares", and "triangles" can be enabled at the same time"#
12);
13
14#[macro_use]
15mod macros {
16    macro_rules! try_can_from {
17        (impl TryFrom<$from:ident>, Error = $error:ident for $t:ty) => {
18            impl TryFrom<$from> for $t {
19                type Error = $error;
20
21                #[inline]
22                fn try_from(value: $from) -> Result<Self, Self::Error> {
23                    if <$t as CanFrom<$from>>::can_from(&value) {
24                        Ok(wasm_bindgen::JsCast::unchecked_into(value))
25                    } else {
26                        Err(wasm_bindgen::JsCast::unchecked_into(value))
27                    }
28                }
29            }
30
31            impl<'a> TryFrom<&'a $from> for &'a $t {
32                type Error = &'a $error;
33
34                #[inline]
35                fn try_from(value: &'a $from) -> Result<Self, Self::Error> {
36                    if <$t as CanFrom<$from>>::can_from(value) {
37                        Ok(wasm_bindgen::JsCast::unchecked_ref(value))
38                    } else {
39                        Err(wasm_bindgen::JsCast::unchecked_ref(value))
40                    }
41                }
42            }
43        };
44    }
45}
46
47pub mod console;
48pub mod graphics;
49pub mod memory;
50pub mod players;
51pub mod spirit;
52pub mod structure;
53
54#[cfg(feature = "RenderService")]
55pub mod render_service;
56
57use js_sys::{Array, JsString, Object, Reflect};
58use players::PlayerID;
59use spirit::{DeadSpirit, LivingEnemySpiritID, OperableSpiritID};
60use std::{convert::TryFrom, fmt::Debug, marker::PhantomData, ops::Deref};
61use structure::StructureID;
62use wasm_bindgen::{prelude::*, JsCast};
63/// The most useful items to import.
64pub mod prelude {
65    pub use crate::players::this_player_id;
66    pub use crate::spirit::{
67        my_spirits, spirits, DeadFriendlySpirit, DeadFriendlySpiritID, LivingEnemySpirit,
68        LivingEnemySpiritID, LivingFriendlySpirit, LivingFriendlySpiritID, OperableSpirit,
69        OperableSpiritID, Spirit, SpiritID,
70    };
71    pub use crate::structure::base::{base, bases, enemy_base, Base};
72    pub use crate::structure::outpost::{outpost_mdo, outposts, Outpost};
73    pub use crate::structure::star::{star_a1c, star_p89, star_zxq, stars, Star};
74    pub use crate::{
75        console, graphics, log, tick, Destructible, Entity, EntityID, EnumerateByID, GetByID,
76        OutpostSight, Position, Shape, Sight, TryGetByID,
77    };
78
79    #[cfg(feature = "RenderService")]
80    pub use crate::render_service;
81}
82
83pub(crate) trait CanFrom<S: JsCast>: JsCast {
84    fn can_from(value: &S) -> bool;
85}
86
87/// A position on the game board. Ordered pair of [`f64`].
88pub type Position = [f64];
89
90/// The possible values of a spirit or base's [`shape`](Destructible::shape) property.
91#[wasm_bindgen(typescript_type = "Shape")]
92pub enum Shape {
93    Circles = "circles",
94    Squares = "squares",
95    Triangles = "triangles",
96}
97
98// OutpostSight
99#[wasm_bindgen]
100extern "C" {
101    /// The [`sight`](Outpost::sight) of an outpost.
102    ///
103    /// [Yare.io Documentation](https://yare.io/documentation#doc_outpost)
104    #[wasm_bindgen(extends = Object, typescript_type = "OutpostSight")]
105    #[derive(Clone, Debug, PartialEq, Eq)]
106    pub type OutpostSight;
107
108    #[wasm_bindgen(method, getter)]
109    pub fn enemies(this: &OutpostSight) -> Vec<LivingEnemySpiritID>;
110}
111
112// Sight
113#[wasm_bindgen]
114extern "C" {
115    /// The [`sight`](Destructible::sight) of a spirit or base.
116    ///
117    /// [Yare.io Documentation](https://yare.io/documentation)
118    #[wasm_bindgen(extends = OutpostSight, typescript_type = "Sight")]
119    #[derive(Clone, Debug, PartialEq, Eq)]
120    pub type Sight;
121
122    #[wasm_bindgen(method, getter)]
123    pub fn friends(this: &Sight) -> Vec<OperableSpiritID>;
124
125    #[wasm_bindgen(method, getter)]
126    pub fn friends_beamable(this: &Sight) -> Vec<OperableSpiritID>;
127
128    #[wasm_bindgen(method, getter)]
129    pub fn enemies_beamable(this: &Sight) -> Vec<LivingEnemySpiritID>;
130
131    #[wasm_bindgen(method, getter)]
132    pub fn structures(this: &Sight) -> Vec<StructureID>;
133}
134
135// Entity
136#[wasm_bindgen]
137extern "C" {
138    /// The ID of an [`Entity`](crate::Entity).
139    #[wasm_bindgen(extends = JsString, typescript_type = "EntityID")]
140    #[derive(Clone, Debug, PartialEq, Eq)]
141    pub type EntityID;
142
143    /// Any object potentially on the game board: can be a [`Spirit`](crate::spirit::Spirit), [`Base`](crate::structure::base::Base), [`Outpost`](crate::structure::outpost::Outpost), or [`Star`](crate::structure::star::Star).
144    ///
145    /// [Yare.io Documentation](https://yare.io/documentation)
146    #[wasm_bindgen(extends = Object, typescript_type = "Entity")]
147    #[derive(Clone, Debug, PartialEq, Eq)]
148    pub type Entity;
149
150    #[wasm_bindgen(method, getter)]
151    pub fn id(this: &Entity) -> EntityID;
152
153    #[wasm_bindgen(method, getter)]
154    pub fn position(this: &Entity) -> Box<Position>;
155
156    #[wasm_bindgen(method, getter)]
157    pub fn size(this: &Entity) -> u32;
158
159    #[wasm_bindgen(method, getter)]
160    pub fn energy(this: &Entity) -> i32;
161
162    #[wasm_bindgen(method, getter)]
163    fn _last_energized(this: &Entity) -> JsString;
164
165    #[wasm_bindgen(method, getter)]
166    pub fn energy_capacity(this: &Entity) -> i32;
167}
168
169impl Entity {
170    #[inline]
171    pub fn last_energized(&self) -> Option<EntityID> {
172        let jsval = self._last_energized();
173        if jsval.is_falsy() {
174            None
175        } else {
176            Some(jsval.unchecked_into())
177        }
178    }
179}
180
181// LivingEntity
182#[wasm_bindgen]
183extern "C" {
184    /// The ID of a [`LivingEntity`].
185    #[wasm_bindgen(extends = EntityID, typescript_type = "EntityID")]
186    #[derive(Clone, Debug, PartialEq, Eq)]
187    pub type LivingEntityID;
188
189    /// Any object on the game board (not destroyed): can be a [`Spirit`](crate::spirit::Spirit), [`Base`](crate::structure::base::Base), [`Outpost`](crate::structure::outpost::Outpost), or [`Star`](crate::structure::star::Star).
190    ///
191    /// [Yare.io Documentation](https://yare.io/documentation)
192    #[wasm_bindgen(extends = Entity, typescript_type = "Entity")]
193    #[derive(Clone, Debug, PartialEq, Eq)]
194    pub type LivingEntity;
195
196    #[wasm_bindgen(method, getter)]
197    pub fn id(this: &LivingEntity) -> LivingEntityID;
198
199}
200
201impl CanFrom<Entity> for LivingEntity {
202    #[inline]
203    fn can_from(value: &Entity) -> bool {
204        !Reflect::has(value, &"hp".into()).unwrap()
205            || value.unchecked_ref::<Destructible>().hp() > 0
206    }
207}
208
209try_can_from!(impl TryFrom<Entity>, Error = DeadSpirit for LivingEntity);
210
211// Destructible
212#[wasm_bindgen]
213extern "C" {
214    /// The ID of a [`Destructible`].
215    #[wasm_bindgen(extends = EntityID, typescript_type = "EntityID")]
216    #[derive(Clone, Debug, PartialEq, Eq)]
217    pub type DestructibleID;
218
219    /// Any [`Entity`](crate::Entity) that can potentially be destroyed: can be a [`Spirit`](crate::spirit::Spirit) or [`Base`](crate::structure::base::Base).
220    ///
221    /// [Yare.io Documentation](https://yare.io/documentation)
222    #[wasm_bindgen(extends = Entity, typescript_type = "Destructible")]
223    #[derive(Clone, Debug, PartialEq, Eq)]
224    pub type Destructible;
225
226    #[wasm_bindgen(method, getter)]
227    pub fn id(this: &Destructible) -> DestructibleID;
228
229    #[wasm_bindgen(method, getter)]
230    pub fn hp(this: &Destructible) -> i32;
231
232    #[wasm_bindgen(method, getter)]
233    pub fn sight(this: &Destructible) -> Sight;
234
235    #[wasm_bindgen(method, getter)]
236    pub fn player_id(this: &Destructible) -> PlayerID;
237
238    #[wasm_bindgen(method, getter)]
239    pub fn shape(this: &Destructible) -> Shape;
240
241    #[wasm_bindgen(method, getter)]
242    pub fn color(this: &Destructible) -> String;
243}
244
245impl CanFrom<Entity> for Destructible {
246    #[inline]
247    fn can_from(value: &Entity) -> bool {
248        Reflect::has(value, &"hp".into()).unwrap()
249    }
250}
251
252try_can_from!(impl TryFrom<Entity>, Error = Entity for Destructible);
253
254// LivingDesctructible
255#[wasm_bindgen]
256extern "C" {
257    /// The ID of a [`LivingDestructible`].
258    #[wasm_bindgen(extends = DestructibleID, extends = LivingEntityID, typescript_type = "EntityID")]
259    #[derive(Clone, Debug, PartialEq, Eq)]
260    pub type LivingDestructibleID;
261
262    /// Any [`Destructible`] that has not yet been destroyed.
263    ///
264    /// [Yare.io Documentation](https://yare.io/documentation)
265    #[wasm_bindgen(extends = Destructible, extends = LivingEntity, typescript_type = "Destructible")]
266    #[derive(Clone, Debug, PartialEq, Eq)]
267    pub type LivingDestructible;
268
269    #[wasm_bindgen(method, getter)]
270    pub fn id(this: &LivingDestructible) -> LivingDestructibleID;
271}
272
273impl CanFrom<Destructible> for LivingDestructible {
274    #[inline]
275    fn can_from(value: &Destructible) -> bool {
276        value.hp() > 0
277    }
278}
279
280try_can_from!(impl TryFrom<Destructible>, Error = Destructible for LivingDestructible);
281
282impl CanFrom<LivingEntity> for LivingDestructible {
283    #[inline]
284    fn can_from(value: &LivingEntity) -> bool {
285        Reflect::has(value, &"hp".into()).unwrap()
286    }
287}
288
289try_can_from!(impl TryFrom<LivingEntity>, Error = LivingEntity for LivingDestructible);
290
291// GetById
292
293/// This trait is implemented for the global objects that give mappings of [ID](EntityID)s to entities:
294/// [`spirits`](spirit::spirits), [`bases`](structure::base::bases),
295/// [`outposts`](structure::outpost::outposts), and [`stars`](structure::star::stars).
296/// It allows fetching entities by their ID.
297pub trait GetByID<ID: JsCast, V: JsCast>
298where
299    Self: AsRef<JsValue> + Deref<Target = Object>,
300{
301    /// Returns the value for this key.
302    fn get(&self, id: &ID) -> V {
303        Reflect::get(self.as_ref(), id.as_ref())
304            .ok()
305            .map(JsCast::unchecked_into)
306            .unwrap()
307    }
308}
309
310/// This trait is implemented for the global objects that give mappings of [ID](EntityID)s to entities:
311/// [`spirits`](spirit::spirits), [`bases`](structure::base::bases),
312/// [`outposts`](structure::outpost::outposts), and [`stars`](structure::star::stars).
313/// It allows faillibly fetching entities by their ID.
314pub trait TryGetByID<ID: JsCast, V: JsCast>
315where
316    Self: AsRef<JsValue> + Deref<Target = Object>,
317{
318    /// Returns the value for this key.
319    fn get(&self, id: &ID) -> Option<V> {
320        Reflect::get(self.as_ref(), id.as_ref())
321            .ok()
322            .map(JsCast::unchecked_into)
323    }
324}
325
326impl<ID: JsCast, V: JsCast, T: GetByID<ID, V>> TryGetByID<ID, V> for T {}
327/// This trait is implemented for the global objects that give mappings of [ID](EntityID)s to entities:
328/// [`spirits`](spirit::spirits), [`bases`](structure::base::bases),
329/// [`outposts`](structure::outpost::outposts), and [`stars`](structure::star::stars).
330/// It allows iterating over entities by their ID.
331pub trait EnumerateByID<ID: JsCast, V: JsCast>: GetByID<ID, V> {
332    // An iterator visiting all IDs.
333    fn ids(&self) -> ArrayTypedIter<ID> {
334        let array = Object::keys(self);
335        ArrayTypedIter::<ID> {
336            range: 0..array.length(),
337            array,
338            phantom: PhantomData,
339        }
340    }
341
342    // An iterator visiting all values.
343    fn values(&self) -> ArrayTypedIter<V> {
344        let array = Object::values(self);
345        ArrayTypedIter::<V> {
346            range: 0..array.length(),
347            array,
348            phantom: PhantomData,
349        }
350    }
351}
352
353/// Iterator returned by [`EnumerateByID`]'s methods.
354pub struct ArrayTypedIter<T: JsCast> {
355    range: std::ops::Range<u32>,
356    array: Array,
357    phantom: PhantomData<T>,
358}
359
360impl<T: JsCast> std::iter::Iterator for ArrayTypedIter<T> {
361    type Item = T;
362
363    fn next(&mut self) -> Option<Self::Item> {
364        let index = self.range.next()?;
365        Some(self.array.get(index).unchecked_into())
366    }
367
368    #[inline]
369    fn size_hint(&self) -> (usize, Option<usize>) {
370        self.range.size_hint()
371    }
372}
373
374impl<T: JsCast> std::iter::DoubleEndedIterator for ArrayTypedIter<T> {
375    fn next_back(&mut self) -> Option<Self::Item> {
376        let index = self.range.next_back()?;
377        Some(self.array.get(index).unchecked_into())
378    }
379}
380impl<T: JsCast> std::iter::FusedIterator for ArrayTypedIter<T> {}
381
382impl<T: JsCast> std::iter::ExactSizeIterator for ArrayTypedIter<T> {}
383
384// `tick`
385#[wasm_bindgen]
386extern "C" {
387    #[wasm_bindgen(js_name = "tick")]
388    static _tick: u32;
389}
390
391/// `tick` (the number of ticks since the start of the game).
392#[inline(always)]
393pub fn tick() -> &'static u32 {
394    &_tick
395}