1#![doc = include_str!("../README.md")]
2#![warn(missing_docs)]
3
4use std::f64::consts::TAU;
5
6#[doc(hidden)]
7pub mod panic;
8mod vec;
9
10#[allow(missing_docs)]
11#[derive(Copy, Clone)]
12pub enum SystemState {
13 Class,
14 Seed,
15 PositionX,
16 PositionY,
17 VelocityX,
18 VelocityY,
19 Heading,
20 AngularVelocity,
21
22 AccelerateX,
23 AccelerateY,
24 Torque,
25
26 Aim0,
27 Aim1,
28 Aim2,
29 Aim3,
30
31 Fire0,
32 Fire1,
33 Fire2,
34 Fire3,
35
36 Explode,
37
38 Radar0Heading,
39 Radar0Width,
40 Radar0MinDistance,
41 Radar0MaxDistance,
42 Radar0EcmMode,
43
44 Radar0ContactFound,
45 Radar0ContactClass,
46 Radar0ContactPositionX,
47 Radar0ContactPositionY,
48 Radar0ContactVelocityX,
49 Radar0ContactVelocityY,
50 Radar0ContactRssi,
51 Radar0ContactSnr,
52
53 Radar1Heading,
54 Radar1Width,
55 Radar1MinDistance,
56 Radar1MaxDistance,
57 Radar1EcmMode,
58
59 Radar1ContactFound,
60 Radar1ContactClass,
61 Radar1ContactPositionX,
62 Radar1ContactPositionY,
63 Radar1ContactVelocityX,
64 Radar1ContactVelocityY,
65 Radar1ContactRssi,
66 Radar1ContactSnr,
67
68 DebugTextPointer,
69 DebugTextLength,
70
71 MaxForwardAcceleration,
72 MaxLateralAcceleration,
73 MaxAngularAcceleration,
74
75 DebugLinesPointer,
76 DebugLinesLength,
77
78 CurrentTick,
79 MaxBackwardAcceleration,
80
81 ActivateAbility,
82
83 Radio0Channel, Radio0Send,
85 Radio0Receive,
86 Radio0Data0,
87 Radio0Data1,
88 Radio0Data2,
89 Radio0Data3,
90
91 Radio1Channel,
92 Radio1Send,
93 Radio1Receive,
94 Radio1Data0,
95 Radio1Data1,
96 Radio1Data2,
97 Radio1Data3,
98
99 Radio2Channel,
100 Radio2Send,
101 Radio2Receive,
102 Radio2Data0,
103 Radio2Data1,
104 Radio2Data2,
105 Radio2Data3,
106
107 Radio3Channel,
108 Radio3Send,
109 Radio3Receive,
110 Radio3Data0,
111 Radio3Data1,
112 Radio3Data2,
113 Radio3Data3,
114
115 Radio4Channel,
116 Radio4Send,
117 Radio4Receive,
118 Radio4Data0,
119 Radio4Data1,
120 Radio4Data2,
121 Radio4Data3,
122
123 Radio5Channel,
124 Radio5Send,
125 Radio5Receive,
126 Radio5Data0,
127 Radio5Data1,
128 Radio5Data2,
129 Radio5Data3,
130
131 Radio6Channel,
132 Radio6Send,
133 Radio6Receive,
134 Radio6Data0,
135 Radio6Data1,
136 Radio6Data2,
137 Radio6Data3,
138
139 Radio7Channel,
140 Radio7Send,
141 Radio7Receive,
142 Radio7Data0,
143 Radio7Data1,
144 Radio7Data2,
145 Radio7Data3,
146
147 SelectedRadio,
149 SelectedRadar,
150
151 DrawnTextPointer,
152 DrawnTextLength,
153
154 Health,
155 Fuel,
156
157 ReloadTicks0,
158 ReloadTicks1,
159 ReloadTicks2,
160 ReloadTicks3,
161
162 Id,
163
164 Size,
165 MaxSize = 128,
166}
167
168#[allow(missing_docs)]
169pub const MAX_ENVIRONMENT_SIZE: usize = 1024;
170
171#[allow(missing_docs)]
173#[derive(Copy, Clone, PartialEq, Eq, Debug)]
174pub enum Class {
175 Fighter,
176 Frigate,
177 Cruiser,
178 Asteroid,
179 Target,
180 Missile,
181 Torpedo,
182 Unknown,
183}
184
185impl Class {
186 #[allow(missing_docs)]
187 pub fn from_f64(v: f64) -> Class {
188 match v as u32 {
189 0 => Class::Fighter,
190 1 => Class::Frigate,
191 2 => Class::Cruiser,
192 3 => Class::Asteroid,
193 4 => Class::Target,
194 5 => Class::Missile,
195 6 => Class::Torpedo,
196 _ => Class::Unknown,
197 }
198 }
199
200 pub fn default_stats(&self) -> ClassStats {
202 match self {
203 Class::Fighter => ClassStats {
204 max_health: 100.0,
205 mass: 15000.0,
206 max_forward_acceleration: 60.0,
207 max_backward_acceleration: 30.0,
208 max_lateral_acceleration: 30.0,
209 max_angular_acceleration: TAU,
210 },
211 Class::Frigate => ClassStats {
212 max_health: 10000.0,
213 mass: 4e6,
214 max_forward_acceleration: 10.0,
215 max_backward_acceleration: 5.0,
216 max_lateral_acceleration: 5.0,
217 max_angular_acceleration: TAU / 8.0,
218 },
219 Class::Cruiser => ClassStats {
220 max_health: 20000.0,
221 mass: 9e6,
222 max_forward_acceleration: 5.0,
223 max_backward_acceleration: 2.5,
224 max_lateral_acceleration: 2.5,
225 max_angular_acceleration: TAU / 16.0,
226 },
227 Class::Asteroid => ClassStats {
228 max_health: 200.0,
229 mass: 20e6,
230 max_forward_acceleration: 0.0,
231 max_backward_acceleration: 0.0,
232 max_lateral_acceleration: 0.0,
233 max_angular_acceleration: 0.0,
234 },
235 Class::Target => ClassStats {
236 max_health: 1.0,
237 mass: 10.0,
238 max_forward_acceleration: 0.0,
239 max_backward_acceleration: 0.0,
240 max_lateral_acceleration: 0.0,
241 max_angular_acceleration: 0.0,
242 },
243 Class::Missile => ClassStats {
244 max_health: 20.0,
245 mass: 150.0,
246 max_forward_acceleration: 300.0,
247 max_backward_acceleration: 0.0,
248 max_lateral_acceleration: 100.0,
249 max_angular_acceleration: 4.0 * TAU,
250 },
251 Class::Torpedo => ClassStats {
252 max_health: 100.0,
253 mass: 500.0,
254 max_forward_acceleration: 70.0,
255 max_backward_acceleration: 0.0,
256 max_lateral_acceleration: 20.0,
257 max_angular_acceleration: 2.0 * TAU,
258 },
259 Class::Unknown => ClassStats {
260 max_health: 100.0,
261 mass: 1000.0,
262 max_forward_acceleration: 0.0,
263 max_backward_acceleration: 0.0,
264 max_lateral_acceleration: 0.0,
265 max_angular_acceleration: 0.0,
266 },
267 }
268 }
269}
270
271#[derive(Debug, Clone)]
273#[allow(missing_docs)]
274pub struct ClassStats {
275 pub max_health: f64,
276 pub mass: f64,
277 pub max_forward_acceleration: f64,
278 pub max_backward_acceleration: f64,
279 pub max_lateral_acceleration: f64,
280 pub max_angular_acceleration: f64,
281}
282
283#[repr(transparent)]
285pub struct ActiveAbilities(pub u64);
286
287impl ActiveAbilities {
288 pub fn set_ability(&mut self, ability: Ability) {
290 let mut mask = 0u64;
291 mask ^= 1u64 << (ability as u64);
292 self.0 |= mask;
293 }
294 pub fn unset_ability(&mut self, ability: Ability) {
296 let mut mask = 0u64;
297 mask ^= 1u64 << (ability as u64);
298 self.0 &= !mask;
299 }
300 pub fn get_ability(&self, ability: Ability) -> bool {
302 (self.0 & 1 << (ability as u64)) >> (ability as u64) != 0
303 }
304 pub fn clear(&mut self) {
306 self.0 = 0
307 }
308 pub fn invert(&mut self) {
310 self.0 = !self.0
311 }
312 pub fn active_iter(&self) -> impl Iterator<Item = Ability> + core::fmt::Debug + Clone + '_ {
314 ABILITIES
315 .iter()
316 .flat_map(|&ability| self.get_ability(ability).then_some(ability))
317 }
318}
319
320#[derive(Copy, Clone, PartialEq, Eq, Debug)]
322#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
323pub enum Ability {
324 #[doc(hidden)]
326 None,
327 Boost,
329 #[doc(hidden)]
331 ShapedCharge,
332 Decoy,
334 Shield,
336}
337
338pub const ABILITIES: &[Ability] = &[Ability::Boost, Ability::Decoy, Ability::Shield];
340
341#[derive(Copy, Clone, PartialEq, Eq, Debug)]
343#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
344pub enum EcmMode {
345 None,
347 Noise,
350}
351
352impl From<f64> for EcmMode {
353 fn from(x: f64) -> Self {
354 match x as u32 {
355 0 => EcmMode::None,
356 1 => EcmMode::Noise,
357 _ => EcmMode::None,
358 }
359 }
360}
361
362#[doc(hidden)]
363#[derive(Default, Clone)]
364pub struct Line {
365 pub x0: f64,
366 pub y0: f64,
367 pub x1: f64,
368 pub y1: f64,
369 pub color: u32,
370}
371
372#[doc(hidden)]
373#[derive(Default, Clone, Debug)]
374#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
375pub struct Text {
376 pub x: f64,
377 pub y: f64,
378 pub color: u32,
379 pub length: u8,
380 pub text: [u8; 11],
381}
382
383pub type Message = [f64; 4];
385
386#[doc(hidden)]
388pub mod sys {
389 use super::SystemState;
390 use crate::MAX_ENVIRONMENT_SIZE;
391 use std::ptr;
392
393 #[no_mangle]
395 pub static mut SYSTEM_STATE: [u64; SystemState::MaxSize as usize] =
396 [0; SystemState::MaxSize as usize];
397
398 pub fn read_system_state_u64(index: SystemState) -> u64 {
399 let system_state = unsafe { ptr::addr_of!(SYSTEM_STATE) };
400 unsafe { (*system_state)[index as usize] }
401 }
402
403 pub fn write_system_state_u64(index: SystemState, value: u64) {
404 let system_state = unsafe { ptr::addr_of_mut!(SYSTEM_STATE) };
405 unsafe { (*system_state)[index as usize] = value };
406 }
407
408 pub fn read_system_state(index: SystemState) -> f64 {
409 f64::from_bits(read_system_state_u64(index))
410 }
411
412 pub fn write_system_state(index: SystemState, value: f64) {
413 write_system_state_u64(index, value.to_bits())
414 }
415
416 #[no_mangle]
417 pub static mut ENVIRONMENT: [u8; MAX_ENVIRONMENT_SIZE] = [0; MAX_ENVIRONMENT_SIZE];
418
419 pub fn read_environment() -> &'static str {
420 unsafe {
422 let environment = ptr::addr_of!(ENVIRONMENT);
423 let n = (*environment)
424 .iter()
425 .position(|&c| c == 0)
426 .unwrap_or((*environment).len());
427 std::str::from_utf8(&(*environment)[..n])
428 .expect("Failed to convert environment to string")
429 }
430 }
431
432 pub fn getenv(key: &str) -> Option<&'static str> {
433 let environment = read_environment();
434 for line in environment.lines() {
435 let mut parts = line.splitn(2, '=');
436 if let Some(k) = parts.next() {
437 if k == key {
438 return parts.next();
439 }
440 }
441 }
442 None
443 }
444}
445
446mod math {
447 pub use std::f64::consts::{PI, TAU};
448
449 pub fn angle_diff(a: f64, b: f64) -> f64 {
453 let c = (b - a).rem_euclid(TAU);
454 if c > PI {
455 c - TAU
456 } else {
457 c
458 }
459 }
460}
461
462mod rng {
463 fn rng() -> &'static mut oorandom::Rand64 {
464 let rng_state = unsafe { super::rng_state::get() };
465 &mut rng_state.rng
466 }
467
468 pub fn rand(low: f64, high: f64) -> f64 {
470 rng().rand_float() * (high - low) + low
471 }
472}
473
474#[doc(hidden)]
475pub mod rng_state {
476 #[derive(Clone)]
477 pub struct RngState {
478 pub rng: oorandom::Rand64,
479 }
480
481 static mut RNG_STATE: Option<RngState> = None;
482
483 impl RngState {
484 #[allow(clippy::new_without_default)]
485 pub fn new() -> Self {
486 Self {
487 rng: oorandom::Rand64::new(super::api::seed()),
488 }
489 }
490 }
491
492 pub unsafe fn get() -> &'static mut RngState {
493 RNG_STATE.as_mut().unwrap()
494 }
495
496 pub unsafe fn set(s: RngState) {
497 RNG_STATE = Some(s)
498 }
499}
500
501mod api {
502 use super::sys::{read_system_state, write_system_state};
503 use super::{Ability, Class, EcmMode, SystemState};
504 use crate::sys::{read_system_state_u64, write_system_state_u64};
505 use crate::{vec::*, ActiveAbilities, Message};
506
507 pub const TICK_LENGTH: f64 = 1.0 / 60.0;
509
510 pub fn id() -> u32 {
512 read_system_state(SystemState::Id) as u32
513 }
514
515 pub fn class() -> Class {
517 Class::from_f64(read_system_state(SystemState::Class))
518 }
519
520 pub fn seed() -> u128 {
522 read_system_state(super::SystemState::Seed) as u128
523 }
524
525 pub fn scenario_name() -> &'static str {
527 super::sys::getenv("SCENARIO_NAME").unwrap_or("unknown")
528 }
529
530 pub fn world_size() -> f64 {
532 super::sys::getenv("WORLD_SIZE")
533 .unwrap_or("0.0")
534 .parse()
535 .unwrap_or(0.0)
536 }
537
538 pub fn position() -> Vec2 {
540 vec2(
541 read_system_state(SystemState::PositionX),
542 read_system_state(SystemState::PositionY),
543 )
544 }
545
546 pub fn velocity() -> Vec2 {
548 vec2(
549 read_system_state(SystemState::VelocityX),
550 read_system_state(SystemState::VelocityY),
551 )
552 }
553
554 pub fn heading() -> f64 {
556 read_system_state(SystemState::Heading)
557 }
558
559 pub fn angular_velocity() -> f64 {
561 read_system_state(SystemState::AngularVelocity)
562 }
563
564 pub fn accelerate(mut acceleration: Vec2) {
566 acceleration = acceleration.rotate(-heading());
567 if acceleration.x > max_forward_acceleration() {
568 acceleration *= max_forward_acceleration() / acceleration.x;
569 }
570 if acceleration.x < -max_backward_acceleration() {
571 acceleration *= max_backward_acceleration() / -acceleration.x;
572 }
573 if acceleration.y.abs() > max_lateral_acceleration() {
574 acceleration *= max_lateral_acceleration() / acceleration.y.abs();
575 }
576 write_system_state(SystemState::AccelerateX, acceleration.x);
577 write_system_state(SystemState::AccelerateY, acceleration.y);
578 }
579
580 pub fn turn(speed: f64) {
584 let max = max_angular_acceleration() * 0.2;
585 let dv = speed.clamp(-max, max) - angular_velocity();
586 if dv.abs() < max_angular_acceleration() * TICK_LENGTH {
587 torque(dv / TICK_LENGTH);
588 } else {
589 torque(dv.signum() * max_angular_acceleration());
590 }
591 }
592
593 pub fn torque(angular_acceleration: f64) {
597 write_system_state(SystemState::Torque, angular_acceleration);
598 }
599
600 pub fn aim(index: usize, heading: f64) {
605 let state_index = match index {
606 0 => SystemState::Aim0,
607 1 => SystemState::Aim1,
608 2 => SystemState::Aim2,
609 3 => SystemState::Aim3,
610 _ => return,
611 };
612 write_system_state(state_index, heading);
613 }
614
615 pub fn fire(index: usize) {
619 let state_index = match index {
620 0 => SystemState::Fire0,
621 1 => SystemState::Fire1,
622 2 => SystemState::Fire2,
623 3 => SystemState::Fire3,
624 _ => return,
625 };
626 write_system_state(state_index, 1.0);
627 }
628
629 pub fn reload_ticks(index: usize) -> u32 {
633 let state_index = match index {
634 0 => SystemState::ReloadTicks0,
635 1 => SystemState::ReloadTicks1,
636 2 => SystemState::ReloadTicks2,
637 3 => SystemState::ReloadTicks3,
638 _ => return 0,
639 };
640 read_system_state(state_index) as u32
641 }
642
643 pub fn explode() {
647 write_system_state(SystemState::Explode, 1.0);
648 }
649
650 pub fn health() -> f64 {
652 read_system_state(SystemState::Health)
653 }
654
655 pub fn fuel() -> f64 {
657 read_system_state(SystemState::Fuel)
658 }
659
660 #[doc(hidden)]
661 pub mod radar_internal {
662 use super::SystemState;
663
664 pub const MAX_RADARS: usize = 2;
665
666 pub struct RadarControlIndices {
667 pub heading: SystemState,
668 pub width: SystemState,
669 pub min_distance: SystemState,
670 pub max_distance: SystemState,
671 pub ecm_mode: SystemState,
672 }
673
674 pub fn radar_control_indices(sel: usize) -> RadarControlIndices {
675 assert!(sel < MAX_RADARS);
676 let stride = 13;
677 let offset = stride * sel;
678 let add_offset =
679 |x| unsafe { ::std::mem::transmute::<u8, SystemState>((x as u8) + offset as u8) };
680 RadarControlIndices {
681 heading: add_offset(SystemState::Radar0Heading),
682 width: add_offset(SystemState::Radar0Width),
683 min_distance: add_offset(SystemState::Radar0MinDistance),
684 max_distance: add_offset(SystemState::Radar0MaxDistance),
685 ecm_mode: add_offset(SystemState::Radar0EcmMode),
686 }
687 }
688
689 pub fn current_radar_control_indices() -> RadarControlIndices {
690 let sel = super::read_system_state(SystemState::SelectedRadar) as usize;
691 radar_control_indices(sel)
692 }
693
694 pub struct RadarContactIndices {
695 pub found: SystemState,
696 pub class: SystemState,
697 pub position: [SystemState; 2],
698 pub velocity: [SystemState; 2],
699 pub rssi: SystemState,
700 pub snr: SystemState,
701 }
702
703 pub fn radar_contact_indices(sel: usize) -> RadarContactIndices {
704 assert!(sel < MAX_RADARS);
705 let stride = 13;
706 let offset = stride * sel;
707 let add_offset =
708 |x| unsafe { ::std::mem::transmute::<u8, SystemState>((x as u8) + offset as u8) };
709 RadarContactIndices {
710 found: add_offset(SystemState::Radar0ContactFound),
711 class: add_offset(SystemState::Radar0ContactClass),
712 position: [
713 add_offset(SystemState::Radar0ContactPositionX),
714 add_offset(SystemState::Radar0ContactPositionY),
715 ],
716 velocity: [
717 add_offset(SystemState::Radar0ContactVelocityX),
718 add_offset(SystemState::Radar0ContactVelocityY),
719 ],
720 rssi: add_offset(SystemState::Radar0ContactRssi),
721 snr: add_offset(SystemState::Radar0ContactSnr),
722 }
723 }
724
725 pub fn current_radar_contact_indices() -> RadarContactIndices {
726 let sel = super::read_system_state(SystemState::SelectedRadar) as usize;
727 radar_contact_indices(sel)
728 }
729 }
730
731 pub fn select_radar(index: usize) {
733 let index = index.clamp(0, radar_internal::MAX_RADARS - 1);
734 write_system_state(SystemState::SelectedRadar, index as f64);
735 }
736
737 pub fn radar_heading() -> f64 {
739 read_system_state(radar_internal::current_radar_control_indices().heading)
740 }
741
742 pub fn set_radar_heading(heading: f64) {
746 write_system_state(
747 radar_internal::current_radar_control_indices().heading,
748 heading,
749 );
750 }
751
752 pub fn radar_width() -> f64 {
756 read_system_state(radar_internal::current_radar_control_indices().width)
757 }
758
759 pub fn set_radar_width(width: f64) {
764 write_system_state(radar_internal::current_radar_control_indices().width, width);
765 }
766
767 pub fn radar_min_distance() -> f64 {
769 read_system_state(radar_internal::current_radar_control_indices().min_distance)
770 }
771
772 pub fn set_radar_min_distance(dist: f64) {
776 write_system_state(
777 radar_internal::current_radar_control_indices().min_distance,
778 dist,
779 );
780 }
781
782 pub fn radar_max_distance() -> f64 {
784 read_system_state(radar_internal::current_radar_control_indices().max_distance)
785 }
786
787 pub fn set_radar_max_distance(dist: f64) {
791 write_system_state(
792 radar_internal::current_radar_control_indices().max_distance,
793 dist,
794 );
795 }
796
797 pub fn radar_ecm_mode() -> EcmMode {
799 read_system_state(radar_internal::current_radar_control_indices().ecm_mode).into()
800 }
801
802 pub fn set_radar_ecm_mode(mode: EcmMode) {
804 write_system_state(
805 radar_internal::current_radar_control_indices().ecm_mode,
806 mode as u32 as f64,
807 );
808 }
809
810 #[derive(Clone, Debug)]
812 pub struct ScanResult {
813 pub class: Class,
815 pub position: Vec2,
817 pub velocity: Vec2,
819 pub rssi: f64,
821 pub snr: f64,
823 }
824
825 pub fn scan() -> Option<ScanResult> {
827 let indices = radar_internal::current_radar_contact_indices();
828 if read_system_state(indices.found) == 0.0 {
829 return None;
830 }
831 Some(ScanResult {
832 class: Class::from_f64(read_system_state(indices.class)),
833 position: vec2(
834 read_system_state(indices.position[0]),
835 read_system_state(indices.position[1]),
836 ),
837 velocity: vec2(
838 read_system_state(indices.velocity[0]),
839 read_system_state(indices.velocity[1]),
840 ),
841 rssi: read_system_state(indices.rssi),
842 snr: read_system_state(indices.snr),
843 })
844 }
845
846 #[doc(hidden)]
847 pub mod radio_internal {
848 use super::SystemState;
849
850 pub const MAX_RADIOS: usize = 8;
851
852 pub struct RadioIndices {
853 pub channel: SystemState,
854 pub send: SystemState,
855 pub receive: SystemState,
856 pub data: [SystemState; 4],
857 }
858
859 pub fn radio_indices(sel: usize) -> RadioIndices {
860 assert!(sel < MAX_RADIOS);
861 let stride = 7;
862 let offset = stride * sel;
863 let add_offset =
864 |x| unsafe { ::std::mem::transmute::<u8, SystemState>((x as u8) + offset as u8) };
865 RadioIndices {
866 channel: add_offset(SystemState::Radio0Channel),
867 send: add_offset(SystemState::Radio0Send),
868 receive: add_offset(SystemState::Radio0Receive),
869 data: [
870 add_offset(SystemState::Radio0Data0),
871 add_offset(SystemState::Radio0Data1),
872 add_offset(SystemState::Radio0Data2),
873 add_offset(SystemState::Radio0Data3),
874 ],
875 }
876 }
877 }
878
879 pub fn select_radio(index: usize) {
881 let index = index.clamp(0, radio_internal::MAX_RADIOS - 1);
882 write_system_state(SystemState::SelectedRadio, index as f64);
883 }
884
885 pub fn set_radio_channel(channel: usize) {
889 write_system_state(
890 radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize)
891 .channel,
892 channel as f64,
893 );
894 }
895
896 pub fn get_radio_channel() -> usize {
898 read_system_state(
899 radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize)
900 .channel,
901 ) as usize
902 }
903
904 pub fn send(msg: Message) {
910 let idxs =
911 radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
912 write_system_state(idxs.send, 1.0);
913 write_system_state(idxs.data[0], msg[0]);
914 write_system_state(idxs.data[1], msg[1]);
915 write_system_state(idxs.data[2], msg[2]);
916 write_system_state(idxs.data[3], msg[3]);
917 }
918
919 pub fn receive() -> Option<Message> {
921 let idxs =
922 radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
923 if read_system_state(idxs.receive) != 0.0 {
924 Some([
925 read_system_state(idxs.data[0]),
926 read_system_state(idxs.data[1]),
927 read_system_state(idxs.data[2]),
928 read_system_state(idxs.data[3]),
929 ])
930 } else {
931 None
932 }
933 }
934
935 pub fn send_bytes(msg: &[u8]) {
942 let mut bytes = [[0; 8]; 4];
943 bytes
944 .iter_mut()
945 .flatten()
946 .zip(msg)
947 .for_each(|(b, m)| *b = *m);
948
949 let idxs =
950 radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
951 write_system_state(idxs.send, 1.0);
952 write_system_state_u64(idxs.data[0], u64::from_ne_bytes(bytes[0]));
953 write_system_state_u64(idxs.data[1], u64::from_ne_bytes(bytes[1]));
954 write_system_state_u64(idxs.data[2], u64::from_ne_bytes(bytes[2]));
955 write_system_state_u64(idxs.data[3], u64::from_ne_bytes(bytes[3]));
956 }
957
958 pub fn receive_bytes() -> Option<[u8; 32]> {
960 let idxs =
961 radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
962 if read_system_state(idxs.receive) != 0.0 {
963 let mut bytes = [0; 32];
964 bytes[0..8].copy_from_slice(&read_system_state_u64(idxs.data[0]).to_ne_bytes());
965 bytes[8..16].copy_from_slice(&read_system_state_u64(idxs.data[1]).to_ne_bytes());
966 bytes[16..24].copy_from_slice(&read_system_state_u64(idxs.data[2]).to_ne_bytes());
967 bytes[24..32].copy_from_slice(&read_system_state_u64(idxs.data[3]).to_ne_bytes());
968 Some(bytes)
969 } else {
970 None
971 }
972 }
973
974 #[deprecated]
976 pub fn max_acceleration() -> Vec2 {
977 vec2(
978 read_system_state(SystemState::MaxForwardAcceleration),
979 read_system_state(SystemState::MaxBackwardAcceleration),
980 )
981 }
982
983 pub fn max_forward_acceleration() -> f64 {
985 read_system_state(SystemState::MaxForwardAcceleration)
986 }
987
988 pub fn max_backward_acceleration() -> f64 {
990 read_system_state(SystemState::MaxBackwardAcceleration)
991 }
992
993 pub fn max_lateral_acceleration() -> f64 {
995 read_system_state(SystemState::MaxLateralAcceleration)
996 }
997
998 pub fn max_angular_acceleration() -> f64 {
1000 read_system_state(SystemState::MaxAngularAcceleration)
1001 }
1002
1003 pub fn current_tick() -> u32 {
1005 read_system_state(SystemState::CurrentTick) as u32
1006 }
1007
1008 pub fn current_time() -> f64 {
1010 read_system_state(SystemState::CurrentTick) * TICK_LENGTH
1011 }
1012
1013 pub fn activate_ability(ability: Ability) {
1015 let mut active_abilities =
1016 ActiveAbilities(read_system_state_u64(SystemState::ActivateAbility));
1017 active_abilities.set_ability(ability);
1018 write_system_state_u64(SystemState::ActivateAbility, active_abilities.0);
1019 }
1020
1021 pub fn deactivate_ability(ability: Ability) {
1023 let mut active_abilities =
1024 ActiveAbilities(read_system_state_u64(SystemState::ActivateAbility));
1025 active_abilities.unset_ability(ability);
1026 write_system_state_u64(SystemState::ActivateAbility, active_abilities.0);
1027 }
1028
1029 pub fn active_abilities() -> ActiveAbilities {
1031 ActiveAbilities(read_system_state_u64(SystemState::ActivateAbility))
1032 }
1033
1034 pub fn target() -> Vec2 {
1037 vec2(
1038 read_system_state(radar_internal::current_radar_contact_indices().position[0]),
1039 read_system_state(radar_internal::current_radar_contact_indices().position[1]),
1040 )
1041 }
1042
1043 pub fn target_velocity() -> Vec2 {
1046 vec2(
1047 read_system_state(radar_internal::current_radar_contact_indices().velocity[0]),
1048 read_system_state(radar_internal::current_radar_contact_indices().velocity[1]),
1049 )
1050 }
1051}
1052
1053#[doc(hidden)]
1054#[macro_use]
1055pub mod dbg {
1056 use super::{Line, Text};
1057 use crate::sys::write_system_state;
1058 use crate::vec::*;
1059 use std::f64::consts::TAU;
1060 use std::ptr;
1061
1062 static mut TEXT_BUFFER: String = String::new();
1063 static mut LINE_BUFFER: Vec<Line> = Vec::new();
1064 static mut DRAWN_TEXT_BUFFER: Vec<Text> = Vec::new();
1065
1066 #[macro_export]
1070 macro_rules! debug {
1071 ($($arg:tt)*) => {
1072 $crate::dbg::write(std::format_args!($($arg)*))
1073 };
1074 }
1075
1076 #[allow(unused)]
1077 #[doc(hidden)]
1078 pub fn write(args: std::fmt::Arguments) {
1079 use std::fmt::Write;
1080 unsafe {
1081 let buf = ptr::addr_of_mut!(TEXT_BUFFER);
1082 let _ = std::fmt::write(&mut *buf, args);
1083 (*buf).push('\n');
1084 }
1085 }
1086
1087 pub fn rgb(r: u8, g: u8, b: u8) -> u32 {
1089 let r = r as u32;
1090 let g = g as u32;
1091 let b = b as u32;
1092 r << 16 | g << 8 | b
1093 }
1094
1095 pub fn draw_line(a: Vec2, b: Vec2, color: u32) {
1103 unsafe {
1104 let buf = ptr::addr_of_mut!(LINE_BUFFER);
1105 (*buf).push(Line {
1106 x0: a.x,
1107 y0: a.y,
1108 x1: b.x,
1109 y1: b.y,
1110 color,
1111 });
1112 }
1113 }
1114
1115 #[deprecated]
1116 #[doc(hidden)]
1117 pub fn debug_line(a: Vec2, b: Vec2, color: u32) {
1118 draw_line(a, b, color)
1119 }
1120
1121 pub fn draw_polygon(center: Vec2, radius: f64, sides: i32, angle: f64, color: u32) {
1126 let delta_angle = TAU / sides as f64;
1127 let sin = delta_angle.sin();
1128 let cos = delta_angle.cos();
1129 let rotation: maths_rs::Mat2d = maths_rs::prelude::MatNew2::new(cos, -sin, sin, cos);
1130 let mut p = vec2(radius, 0.0).rotate(angle);
1131 for _ in 0..sides {
1132 let p2 = rotation * p;
1133 draw_line(center + p, center + p2, color);
1134 p = p2;
1135 }
1136 }
1137
1138 #[deprecated]
1139 #[doc(hidden)]
1140 pub fn debug_polygon(center: Vec2, radius: f64, sides: i32, angle: f64, color: u32) {
1141 draw_polygon(center, radius, sides, angle, color)
1142 }
1143
1144 pub fn draw_triangle(center: Vec2, radius: f64, color: u32) {
1149 let x = f64::sqrt(3.0) * radius / 2.0;
1150 let y = radius / 2.0;
1151 let points = [
1152 center + vec2(0.0, radius),
1153 center + vec2(-x, -y),
1154 center + vec2(x, -y),
1155 ];
1156 for i in 0..3 {
1157 draw_line(points[i], points[(i + 1) % 3], color);
1158 }
1159 }
1160
1161 #[deprecated]
1162 #[doc(hidden)]
1163 pub fn debug_triangle(center: Vec2, radius: f64, color: u32) {
1164 draw_triangle(center, radius, color)
1165 }
1166
1167 pub fn draw_square(center: Vec2, radius: f64, color: u32) {
1172 let offset = radius / f64::sqrt(2.0);
1173 let mut p = vec2(offset, offset);
1174 for _ in 0..4 {
1175 let p2 = vec2(-p.y, p.x);
1176 draw_line(center + p, center + p2, color);
1177 p = p2;
1178 }
1179 }
1180
1181 #[deprecated]
1182 #[doc(hidden)]
1183 pub fn debug_square(center: Vec2, radius: f64, color: u32) {
1184 draw_square(center, radius, color)
1185 }
1186
1187 pub fn draw_diamond(center: Vec2, radius: f64, color: u32) {
1192 let mut p = vec2(radius, 0.0);
1193 for _ in 0..4 {
1194 let p2 = vec2(-p.y, p.x);
1195 draw_line(center + p, center + p2, color);
1196 p = p2;
1197 }
1198 }
1199
1200 #[deprecated]
1201 #[doc(hidden)]
1202 pub fn debug_diamond(center: Vec2, radius: f64, color: u32) {
1203 draw_diamond(center, radius, color)
1204 }
1205
1206 #[macro_export]
1210 macro_rules! draw_text {
1211 ($topleft:expr, $color:expr, $($arg:tt)*) => {
1212 $crate::dbg::draw_text_internal($topleft, $color, std::format_args!($($arg)*))
1213 };
1214 }
1215
1216 #[allow(unused)]
1217 #[doc(hidden)]
1218 pub fn draw_text_internal(topleft: Vec2, color: u32, args: std::fmt::Arguments) {
1219 use std::fmt::Write;
1220 let mut text = String::new();
1221 let _ = std::fmt::write(&mut text, args);
1222 let buf = unsafe { &mut *ptr::addr_of_mut!(DRAWN_TEXT_BUFFER) };
1223 let mut text_buf = [0u8; 11];
1225 text_buf
1226 .iter_mut()
1227 .zip(text.bytes())
1228 .for_each(|(d, s)| *d = s);
1229 buf.push(Text {
1230 x: topleft.x,
1231 y: topleft.y,
1232 color,
1233 length: text.len().min(text_buf.len()) as u8,
1234 text: text_buf,
1235 });
1236 }
1237
1238 #[doc(hidden)]
1239 pub fn update() {
1240 {
1241 let slice = unsafe { &mut *ptr::addr_of_mut!(TEXT_BUFFER) }.as_bytes();
1242 write_system_state(
1243 super::SystemState::DebugTextPointer,
1244 slice.as_ptr() as u32 as f64,
1245 );
1246 write_system_state(
1247 super::SystemState::DebugTextLength,
1248 slice.len() as u32 as f64,
1249 );
1250 }
1251 {
1252 let slice = unsafe { &mut *ptr::addr_of_mut!(LINE_BUFFER) }.as_slice();
1253 write_system_state(
1254 super::SystemState::DebugLinesPointer,
1255 slice.as_ptr() as u32 as f64,
1256 );
1257 write_system_state(
1258 super::SystemState::DebugLinesLength,
1259 slice.len() as u32 as f64,
1260 );
1261 }
1262 {
1263 let slice = unsafe { &mut *ptr::addr_of_mut!(DRAWN_TEXT_BUFFER) }.as_slice();
1264 write_system_state(
1265 super::SystemState::DrawnTextPointer,
1266 slice.as_ptr() as u32 as f64,
1267 );
1268 write_system_state(
1269 super::SystemState::DrawnTextLength,
1270 slice.len() as u32 as f64,
1271 );
1272 }
1273 }
1274
1275 #[doc(hidden)]
1276 pub fn reset() {
1277 unsafe {
1278 TEXT_BUFFER.clear();
1279 LINE_BUFFER.clear();
1280 DRAWN_TEXT_BUFFER.clear();
1281 }
1282 }
1283}
1284
1285mod deprecated {
1286 use super::api::*;
1287 use super::sys::write_system_state;
1288 use super::SystemState;
1289
1290 #[deprecated]
1292 pub fn aim_gun(index: usize, heading: f64) {
1293 aim(index, heading);
1294 }
1295
1296 #[deprecated]
1298 pub fn fire_gun(index: usize) {
1299 fire(index);
1300 }
1301
1302 #[deprecated]
1304 pub fn launch_missile(index: usize, _unused: f64) {
1305 use super::Class::*;
1306 let state_index = match (class(), index) {
1307 (Fighter, 0) => SystemState::Fire1,
1308
1309 (Frigate, 0) => SystemState::Fire3,
1310
1311 (Cruiser, 0) => SystemState::Fire1,
1312 (Cruiser, 1) => SystemState::Fire2,
1313 (Cruiser, 2) => SystemState::Fire3,
1314
1315 _ => return,
1316 };
1317 write_system_state(state_index, 1.0);
1318 }
1319
1320 #[deprecated]
1322 pub fn orders() -> f64 {
1323 0.0
1324 }
1325}
1326
1327pub mod prelude {
1329 #[doc(inline)]
1330 pub use super::api::*;
1331 #[doc(inline)]
1332 pub use super::dbg::*;
1333 #[doc(hidden)]
1334 pub use super::deprecated::*;
1335 #[doc(inline)]
1336 pub use super::math::*;
1337 #[doc(inline)]
1338 pub use super::rng::*;
1339 #[doc(inline)]
1340 pub use super::vec::*;
1341 #[doc(inline)]
1342 pub use super::{Ability, Class, EcmMode, Message};
1343 #[doc(inline)]
1344 pub use crate::{debug, draw_text};
1345
1346 pub use byteorder;
1347 pub use maths_rs;
1348 pub use oorandom;
1349}