#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
use std::f64::consts::TAU;
#[doc(hidden)]
pub mod panic;
mod vec;
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum SystemState {
Class,
Seed,
PositionX,
PositionY,
VelocityX,
VelocityY,
Heading,
AngularVelocity,
AccelerateX,
AccelerateY,
Torque,
Aim0,
Aim1,
Aim2,
Aim3,
Fire0,
Fire1,
Fire2,
Fire3,
Explode,
Radar0Heading,
Radar0Width,
Radar0MinDistance,
Radar0MaxDistance,
Radar0EcmMode,
Radar0ContactFound,
Radar0ContactClass,
Radar0ContactPositionX,
Radar0ContactPositionY,
Radar0ContactVelocityX,
Radar0ContactVelocityY,
Radar0ContactRssi,
Radar0ContactSnr,
Radar1Heading,
Radar1Width,
Radar1MinDistance,
Radar1MaxDistance,
Radar1EcmMode,
Radar1ContactFound,
Radar1ContactClass,
Radar1ContactPositionX,
Radar1ContactPositionY,
Radar1ContactVelocityX,
Radar1ContactVelocityY,
Radar1ContactRssi,
Radar1ContactSnr,
DebugTextPointer,
DebugTextLength,
MaxForwardAcceleration,
MaxLateralAcceleration,
MaxAngularAcceleration,
DebugLinesPointer,
DebugLinesLength,
CurrentTick,
MaxBackwardAcceleration,
ActivateAbility,
Radio0Channel, Radio0Send,
Radio0Receive,
Radio0Data0,
Radio0Data1,
Radio0Data2,
Radio0Data3,
Radio1Channel,
Radio1Send,
Radio1Receive,
Radio1Data0,
Radio1Data1,
Radio1Data2,
Radio1Data3,
Radio2Channel,
Radio2Send,
Radio2Receive,
Radio2Data0,
Radio2Data1,
Radio2Data2,
Radio2Data3,
Radio3Channel,
Radio3Send,
Radio3Receive,
Radio3Data0,
Radio3Data1,
Radio3Data2,
Radio3Data3,
Radio4Channel,
Radio4Send,
Radio4Receive,
Radio4Data0,
Radio4Data1,
Radio4Data2,
Radio4Data3,
Radio5Channel,
Radio5Send,
Radio5Receive,
Radio5Data0,
Radio5Data1,
Radio5Data2,
Radio5Data3,
Radio6Channel,
Radio6Send,
Radio6Receive,
Radio6Data0,
Radio6Data1,
Radio6Data2,
Radio6Data3,
Radio7Channel,
Radio7Send,
Radio7Receive,
Radio7Data0,
Radio7Data1,
Radio7Data2,
Radio7Data3,
SelectedRadio,
SelectedRadar,
DrawnTextPointer,
DrawnTextLength,
Health,
Fuel,
ReloadTicks0,
ReloadTicks1,
ReloadTicks2,
ReloadTicks3,
Id,
Size,
MaxSize = 128,
}
#[allow(missing_docs)]
pub const MAX_ENVIRONMENT_SIZE: usize = 1024;
#[allow(missing_docs)]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Class {
Fighter,
Frigate,
Cruiser,
Asteroid,
Target,
Missile,
Torpedo,
Unknown,
}
impl Class {
#[allow(missing_docs)]
pub fn from_f64(v: f64) -> Class {
match v as u32 {
0 => Class::Fighter,
1 => Class::Frigate,
2 => Class::Cruiser,
3 => Class::Asteroid,
4 => Class::Target,
5 => Class::Missile,
6 => Class::Torpedo,
_ => Class::Unknown,
}
}
pub fn default_stats(&self) -> ClassStats {
match self {
Class::Fighter => ClassStats {
max_health: 100.0,
mass: 15000.0,
max_forward_acceleration: 60.0,
max_backward_acceleration: 30.0,
max_lateral_acceleration: 30.0,
max_angular_acceleration: TAU,
},
Class::Frigate => ClassStats {
max_health: 10000.0,
mass: 4e6,
max_forward_acceleration: 10.0,
max_backward_acceleration: 5.0,
max_lateral_acceleration: 5.0,
max_angular_acceleration: TAU / 8.0,
},
Class::Cruiser => ClassStats {
max_health: 20000.0,
mass: 9e6,
max_forward_acceleration: 5.0,
max_backward_acceleration: 2.5,
max_lateral_acceleration: 2.5,
max_angular_acceleration: TAU / 16.0,
},
Class::Asteroid => ClassStats {
max_health: 200.0,
mass: 20e6,
max_forward_acceleration: 0.0,
max_backward_acceleration: 0.0,
max_lateral_acceleration: 0.0,
max_angular_acceleration: 0.0,
},
Class::Target => ClassStats {
max_health: 1.0,
mass: 10.0,
max_forward_acceleration: 0.0,
max_backward_acceleration: 0.0,
max_lateral_acceleration: 0.0,
max_angular_acceleration: 0.0,
},
Class::Missile => ClassStats {
max_health: 20.0,
mass: 150.0,
max_forward_acceleration: 300.0,
max_backward_acceleration: 0.0,
max_lateral_acceleration: 100.0,
max_angular_acceleration: 4.0 * TAU,
},
Class::Torpedo => ClassStats {
max_health: 100.0,
mass: 500.0,
max_forward_acceleration: 70.0,
max_backward_acceleration: 0.0,
max_lateral_acceleration: 20.0,
max_angular_acceleration: 2.0 * TAU,
},
Class::Unknown => ClassStats {
max_health: 100.0,
mass: 1000.0,
max_forward_acceleration: 0.0,
max_backward_acceleration: 0.0,
max_lateral_acceleration: 0.0,
max_angular_acceleration: 0.0,
},
}
}
}
#[derive(Debug, Clone)]
#[allow(missing_docs)]
pub struct ClassStats {
pub max_health: f64,
pub mass: f64,
pub max_forward_acceleration: f64,
pub max_backward_acceleration: f64,
pub max_lateral_acceleration: f64,
pub max_angular_acceleration: f64,
}
#[repr(transparent)]
pub struct ActiveAbilities(pub u64);
impl ActiveAbilities {
pub fn set_ability(&mut self, ability: Ability) {
let mut mask = 0u64;
mask ^= 1u64 << (ability as u64);
self.0 |= mask;
}
pub fn unset_ability(&mut self, ability: Ability) {
let mut mask = 0u64;
mask ^= 1u64 << (ability as u64);
self.0 &= !mask;
}
pub fn get_ability(&self, ability: Ability) -> bool {
(self.0 & 1 << (ability as u64)) >> (ability as u64) != 0
}
pub fn clear(&mut self) {
self.0 = 0
}
pub fn invert(&mut self) {
self.0 = !self.0
}
pub fn active_iter(&self) -> impl Iterator<Item = Ability> + core::fmt::Debug + Clone + '_ {
ABILITIES
.iter()
.flat_map(|&ability| self.get_ability(ability).then_some(ability))
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Ability {
#[doc(hidden)]
None,
Boost,
#[doc(hidden)]
ShapedCharge,
Decoy,
Shield,
}
pub const ABILITIES: &[Ability] = &[Ability::Boost, Ability::Decoy, Ability::Shield];
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EcmMode {
None,
Noise,
}
impl From<f64> for EcmMode {
fn from(x: f64) -> Self {
match x as u32 {
0 => EcmMode::None,
1 => EcmMode::Noise,
_ => EcmMode::None,
}
}
}
#[doc(hidden)]
#[derive(Default, Clone)]
pub struct Line {
pub x0: f64,
pub y0: f64,
pub x1: f64,
pub y1: f64,
pub color: u32,
}
#[doc(hidden)]
#[derive(Default, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Text {
pub x: f64,
pub y: f64,
pub color: u32,
pub length: u8,
pub text: [u8; 11],
}
pub type Message = [f64; 4];
#[doc(hidden)]
pub mod sys {
use super::SystemState;
use crate::MAX_ENVIRONMENT_SIZE;
use std::ptr;
#[no_mangle]
pub static mut SYSTEM_STATE: [u64; SystemState::MaxSize as usize] =
[0; SystemState::MaxSize as usize];
pub fn read_system_state_u64(index: SystemState) -> u64 {
let system_state = unsafe { ptr::addr_of!(SYSTEM_STATE) };
unsafe { (*system_state)[index as usize] }
}
pub fn write_system_state_u64(index: SystemState, value: u64) {
let system_state = unsafe { ptr::addr_of_mut!(SYSTEM_STATE) };
unsafe { (*system_state)[index as usize] = value };
}
pub fn read_system_state(index: SystemState) -> f64 {
f64::from_bits(read_system_state_u64(index))
}
pub fn write_system_state(index: SystemState, value: f64) {
write_system_state_u64(index, value.to_bits())
}
#[no_mangle]
pub static mut ENVIRONMENT: [u8; MAX_ENVIRONMENT_SIZE] = [0; MAX_ENVIRONMENT_SIZE];
pub fn read_environment() -> &'static str {
unsafe {
let environment = ptr::addr_of!(ENVIRONMENT);
let n = (*environment)
.iter()
.position(|&c| c == 0)
.unwrap_or((*environment).len());
std::str::from_utf8(&(*environment)[..n])
.expect("Failed to convert environment to string")
}
}
pub fn getenv(key: &str) -> Option<&'static str> {
let environment = read_environment();
for line in environment.lines() {
let mut parts = line.splitn(2, '=');
if let Some(k) = parts.next() {
if k == key {
return parts.next();
}
}
}
None
}
}
mod math {
pub use std::f64::consts::{PI, TAU};
pub fn angle_diff(a: f64, b: f64) -> f64 {
let c = (b - a).rem_euclid(TAU);
if c > PI {
c - TAU
} else {
c
}
}
}
mod rng {
fn rng() -> &'static mut oorandom::Rand64 {
let rng_state = unsafe { super::rng_state::get() };
&mut rng_state.rng
}
pub fn rand(low: f64, high: f64) -> f64 {
rng().rand_float() * (high - low) + low
}
}
#[doc(hidden)]
pub mod rng_state {
#[derive(Clone)]
pub struct RngState {
pub rng: oorandom::Rand64,
}
static mut RNG_STATE: Option<RngState> = None;
impl RngState {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
rng: oorandom::Rand64::new(super::api::seed()),
}
}
}
pub unsafe fn get() -> &'static mut RngState {
RNG_STATE.as_mut().unwrap()
}
pub unsafe fn set(s: RngState) {
RNG_STATE = Some(s)
}
}
mod api {
use super::sys::{read_system_state, write_system_state};
use super::{Ability, Class, EcmMode, SystemState};
use crate::sys::{read_system_state_u64, write_system_state_u64};
use crate::{vec::*, ActiveAbilities, Message};
pub const TICK_LENGTH: f64 = 1.0 / 60.0;
pub fn id() -> u32 {
read_system_state(SystemState::Id) as u32
}
pub fn class() -> Class {
Class::from_f64(read_system_state(SystemState::Class))
}
pub fn seed() -> u128 {
read_system_state(super::SystemState::Seed) as u128
}
pub fn scenario_name() -> &'static str {
super::sys::getenv("SCENARIO_NAME").unwrap_or("unknown")
}
pub fn world_size() -> f64 {
super::sys::getenv("WORLD_SIZE")
.unwrap_or("0.0")
.parse()
.unwrap_or(0.0)
}
pub fn position() -> Vec2 {
vec2(
read_system_state(SystemState::PositionX),
read_system_state(SystemState::PositionY),
)
}
pub fn velocity() -> Vec2 {
vec2(
read_system_state(SystemState::VelocityX),
read_system_state(SystemState::VelocityY),
)
}
pub fn heading() -> f64 {
read_system_state(SystemState::Heading)
}
pub fn angular_velocity() -> f64 {
read_system_state(SystemState::AngularVelocity)
}
pub fn accelerate(mut acceleration: Vec2) {
acceleration = acceleration.rotate(-heading());
if acceleration.x > max_forward_acceleration() {
acceleration *= max_forward_acceleration() / acceleration.x;
}
if acceleration.x < -max_backward_acceleration() {
acceleration *= max_backward_acceleration() / -acceleration.x;
}
if acceleration.y.abs() > max_lateral_acceleration() {
acceleration *= max_lateral_acceleration() / acceleration.y.abs();
}
write_system_state(SystemState::AccelerateX, acceleration.x);
write_system_state(SystemState::AccelerateY, acceleration.y);
}
pub fn turn(speed: f64) {
let max = max_angular_acceleration() * 0.2;
let dv = speed.clamp(-max, max) - angular_velocity();
if dv.abs() < max_angular_acceleration() * TICK_LENGTH {
torque(dv / TICK_LENGTH);
} else {
torque(dv.signum() * max_angular_acceleration());
}
}
pub fn torque(angular_acceleration: f64) {
write_system_state(SystemState::Torque, angular_acceleration);
}
pub fn aim(index: usize, heading: f64) {
let state_index = match index {
0 => SystemState::Aim0,
1 => SystemState::Aim1,
2 => SystemState::Aim2,
3 => SystemState::Aim3,
_ => return,
};
write_system_state(state_index, heading);
}
pub fn fire(index: usize) {
let state_index = match index {
0 => SystemState::Fire0,
1 => SystemState::Fire1,
2 => SystemState::Fire2,
3 => SystemState::Fire3,
_ => return,
};
write_system_state(state_index, 1.0);
}
pub fn reload_ticks(index: usize) -> u32 {
let state_index = match index {
0 => SystemState::ReloadTicks0,
1 => SystemState::ReloadTicks1,
2 => SystemState::ReloadTicks2,
3 => SystemState::ReloadTicks3,
_ => return 0,
};
read_system_state(state_index) as u32
}
pub fn explode() {
write_system_state(SystemState::Explode, 1.0);
}
pub fn health() -> f64 {
read_system_state(SystemState::Health)
}
pub fn fuel() -> f64 {
read_system_state(SystemState::Fuel)
}
#[doc(hidden)]
pub mod radar_internal {
use super::SystemState;
pub const MAX_RADARS: usize = 2;
pub struct RadarControlIndices {
pub heading: SystemState,
pub width: SystemState,
pub min_distance: SystemState,
pub max_distance: SystemState,
pub ecm_mode: SystemState,
}
pub fn radar_control_indices(sel: usize) -> RadarControlIndices {
assert!(sel < MAX_RADARS);
let stride = 13;
let offset = stride * sel;
let add_offset =
|x| unsafe { ::std::mem::transmute::<u8, SystemState>((x as u8) + offset as u8) };
RadarControlIndices {
heading: add_offset(SystemState::Radar0Heading),
width: add_offset(SystemState::Radar0Width),
min_distance: add_offset(SystemState::Radar0MinDistance),
max_distance: add_offset(SystemState::Radar0MaxDistance),
ecm_mode: add_offset(SystemState::Radar0EcmMode),
}
}
pub fn current_radar_control_indices() -> RadarControlIndices {
let sel = super::read_system_state(SystemState::SelectedRadar) as usize;
radar_control_indices(sel)
}
pub struct RadarContactIndices {
pub found: SystemState,
pub class: SystemState,
pub position: [SystemState; 2],
pub velocity: [SystemState; 2],
pub rssi: SystemState,
pub snr: SystemState,
}
pub fn radar_contact_indices(sel: usize) -> RadarContactIndices {
assert!(sel < MAX_RADARS);
let stride = 13;
let offset = stride * sel;
let add_offset =
|x| unsafe { ::std::mem::transmute::<u8, SystemState>((x as u8) + offset as u8) };
RadarContactIndices {
found: add_offset(SystemState::Radar0ContactFound),
class: add_offset(SystemState::Radar0ContactClass),
position: [
add_offset(SystemState::Radar0ContactPositionX),
add_offset(SystemState::Radar0ContactPositionY),
],
velocity: [
add_offset(SystemState::Radar0ContactVelocityX),
add_offset(SystemState::Radar0ContactVelocityY),
],
rssi: add_offset(SystemState::Radar0ContactRssi),
snr: add_offset(SystemState::Radar0ContactSnr),
}
}
pub fn current_radar_contact_indices() -> RadarContactIndices {
let sel = super::read_system_state(SystemState::SelectedRadar) as usize;
radar_contact_indices(sel)
}
}
pub fn select_radar(index: usize) {
let index = index.clamp(0, radar_internal::MAX_RADARS - 1);
write_system_state(SystemState::SelectedRadar, index as f64);
}
pub fn radar_heading() -> f64 {
read_system_state(radar_internal::current_radar_control_indices().heading)
}
pub fn set_radar_heading(heading: f64) {
write_system_state(
radar_internal::current_radar_control_indices().heading,
heading,
);
}
pub fn radar_width() -> f64 {
read_system_state(radar_internal::current_radar_control_indices().width)
}
pub fn set_radar_width(width: f64) {
write_system_state(radar_internal::current_radar_control_indices().width, width);
}
pub fn radar_min_distance() -> f64 {
read_system_state(radar_internal::current_radar_control_indices().min_distance)
}
pub fn set_radar_min_distance(dist: f64) {
write_system_state(
radar_internal::current_radar_control_indices().min_distance,
dist,
);
}
pub fn radar_max_distance() -> f64 {
read_system_state(radar_internal::current_radar_control_indices().max_distance)
}
pub fn set_radar_max_distance(dist: f64) {
write_system_state(
radar_internal::current_radar_control_indices().max_distance,
dist,
);
}
pub fn radar_ecm_mode() -> EcmMode {
read_system_state(radar_internal::current_radar_control_indices().ecm_mode).into()
}
pub fn set_radar_ecm_mode(mode: EcmMode) {
write_system_state(
radar_internal::current_radar_control_indices().ecm_mode,
mode as u32 as f64,
);
}
#[derive(Clone, Debug)]
pub struct ScanResult {
pub class: Class,
pub position: Vec2,
pub velocity: Vec2,
pub rssi: f64,
pub snr: f64,
}
pub fn scan() -> Option<ScanResult> {
let indices = radar_internal::current_radar_contact_indices();
if read_system_state(indices.found) == 0.0 {
return None;
}
Some(ScanResult {
class: Class::from_f64(read_system_state(indices.class)),
position: vec2(
read_system_state(indices.position[0]),
read_system_state(indices.position[1]),
),
velocity: vec2(
read_system_state(indices.velocity[0]),
read_system_state(indices.velocity[1]),
),
rssi: read_system_state(indices.rssi),
snr: read_system_state(indices.snr),
})
}
#[doc(hidden)]
pub mod radio_internal {
use super::SystemState;
pub const MAX_RADIOS: usize = 8;
pub struct RadioIndices {
pub channel: SystemState,
pub send: SystemState,
pub receive: SystemState,
pub data: [SystemState; 4],
}
pub fn radio_indices(sel: usize) -> RadioIndices {
assert!(sel < MAX_RADIOS);
let stride = 7;
let offset = stride * sel;
let add_offset =
|x| unsafe { ::std::mem::transmute::<u8, SystemState>((x as u8) + offset as u8) };
RadioIndices {
channel: add_offset(SystemState::Radio0Channel),
send: add_offset(SystemState::Radio0Send),
receive: add_offset(SystemState::Radio0Receive),
data: [
add_offset(SystemState::Radio0Data0),
add_offset(SystemState::Radio0Data1),
add_offset(SystemState::Radio0Data2),
add_offset(SystemState::Radio0Data3),
],
}
}
}
pub fn select_radio(index: usize) {
let index = index.clamp(0, radio_internal::MAX_RADIOS - 1);
write_system_state(SystemState::SelectedRadio, index as f64);
}
pub fn set_radio_channel(channel: usize) {
write_system_state(
radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize)
.channel,
channel as f64,
);
}
pub fn get_radio_channel() -> usize {
read_system_state(
radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize)
.channel,
) as usize
}
pub fn send(msg: Message) {
let idxs =
radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
write_system_state(idxs.send, 1.0);
write_system_state(idxs.data[0], msg[0]);
write_system_state(idxs.data[1], msg[1]);
write_system_state(idxs.data[2], msg[2]);
write_system_state(idxs.data[3], msg[3]);
}
pub fn receive() -> Option<Message> {
let idxs =
radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
if read_system_state(idxs.receive) != 0.0 {
Some([
read_system_state(idxs.data[0]),
read_system_state(idxs.data[1]),
read_system_state(idxs.data[2]),
read_system_state(idxs.data[3]),
])
} else {
None
}
}
pub fn send_bytes(msg: &[u8]) {
let mut bytes = [[0; 8]; 4];
bytes
.iter_mut()
.flatten()
.zip(msg)
.for_each(|(b, m)| *b = *m);
let idxs =
radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
write_system_state(idxs.send, 1.0);
write_system_state_u64(idxs.data[0], u64::from_ne_bytes(bytes[0]));
write_system_state_u64(idxs.data[1], u64::from_ne_bytes(bytes[1]));
write_system_state_u64(idxs.data[2], u64::from_ne_bytes(bytes[2]));
write_system_state_u64(idxs.data[3], u64::from_ne_bytes(bytes[3]));
}
pub fn receive_bytes() -> Option<[u8; 32]> {
let idxs =
radio_internal::radio_indices(read_system_state(SystemState::SelectedRadio) as usize);
if read_system_state(idxs.receive) != 0.0 {
let mut bytes = [0; 32];
bytes[0..8].copy_from_slice(&read_system_state_u64(idxs.data[0]).to_ne_bytes());
bytes[8..16].copy_from_slice(&read_system_state_u64(idxs.data[1]).to_ne_bytes());
bytes[16..24].copy_from_slice(&read_system_state_u64(idxs.data[2]).to_ne_bytes());
bytes[24..32].copy_from_slice(&read_system_state_u64(idxs.data[3]).to_ne_bytes());
Some(bytes)
} else {
None
}
}
#[deprecated]
pub fn max_acceleration() -> Vec2 {
vec2(
read_system_state(SystemState::MaxForwardAcceleration),
read_system_state(SystemState::MaxBackwardAcceleration),
)
}
pub fn max_forward_acceleration() -> f64 {
read_system_state(SystemState::MaxForwardAcceleration)
}
pub fn max_backward_acceleration() -> f64 {
read_system_state(SystemState::MaxBackwardAcceleration)
}
pub fn max_lateral_acceleration() -> f64 {
read_system_state(SystemState::MaxLateralAcceleration)
}
pub fn max_angular_acceleration() -> f64 {
read_system_state(SystemState::MaxAngularAcceleration)
}
pub fn current_tick() -> u32 {
read_system_state(SystemState::CurrentTick) as u32
}
pub fn current_time() -> f64 {
read_system_state(SystemState::CurrentTick) * TICK_LENGTH
}
pub fn activate_ability(ability: Ability) {
let mut active_abilities =
ActiveAbilities(read_system_state_u64(SystemState::ActivateAbility));
active_abilities.set_ability(ability);
write_system_state_u64(SystemState::ActivateAbility, active_abilities.0);
}
pub fn deactivate_ability(ability: Ability) {
let mut active_abilities =
ActiveAbilities(read_system_state_u64(SystemState::ActivateAbility));
active_abilities.unset_ability(ability);
write_system_state_u64(SystemState::ActivateAbility, active_abilities.0);
}
pub fn active_abilities() -> ActiveAbilities {
ActiveAbilities(read_system_state_u64(SystemState::ActivateAbility))
}
pub fn target() -> Vec2 {
vec2(
read_system_state(radar_internal::current_radar_contact_indices().position[0]),
read_system_state(radar_internal::current_radar_contact_indices().position[1]),
)
}
pub fn target_velocity() -> Vec2 {
vec2(
read_system_state(radar_internal::current_radar_contact_indices().velocity[0]),
read_system_state(radar_internal::current_radar_contact_indices().velocity[1]),
)
}
}
#[doc(hidden)]
#[macro_use]
pub mod dbg {
use super::{Line, Text};
use crate::sys::write_system_state;
use crate::vec::*;
use std::f64::consts::TAU;
use std::ptr;
static mut TEXT_BUFFER: String = String::new();
static mut LINE_BUFFER: Vec<Line> = Vec::new();
static mut DRAWN_TEXT_BUFFER: Vec<Text> = Vec::new();
#[macro_export]
macro_rules! debug {
($($arg:tt)*) => {
$crate::dbg::write(std::format_args!($($arg)*))
};
}
#[allow(unused)]
#[doc(hidden)]
pub fn write(args: std::fmt::Arguments) {
use std::fmt::Write;
unsafe {
let buf = ptr::addr_of_mut!(TEXT_BUFFER);
let _ = std::fmt::write(&mut *buf, args);
(*buf).push('\n');
}
}
pub fn rgb(r: u8, g: u8, b: u8) -> u32 {
let r = r as u32;
let g = g as u32;
let b = b as u32;
r << 16 | g << 8 | b
}
pub fn draw_line(a: Vec2, b: Vec2, color: u32) {
unsafe {
let buf = ptr::addr_of_mut!(LINE_BUFFER);
(*buf).push(Line {
x0: a.x,
y0: a.y,
x1: b.x,
y1: b.y,
color,
});
}
}
#[deprecated]
#[doc(hidden)]
pub fn debug_line(a: Vec2, b: Vec2, color: u32) {
draw_line(a, b, color)
}
pub fn draw_polygon(center: Vec2, radius: f64, sides: i32, angle: f64, color: u32) {
let delta_angle = TAU / sides as f64;
let sin = delta_angle.sin();
let cos = delta_angle.cos();
let rotation: maths_rs::Mat2d = maths_rs::prelude::MatNew2::new(cos, -sin, sin, cos);
let mut p = vec2(radius, 0.0).rotate(angle);
for _ in 0..sides {
let p2 = rotation * p;
draw_line(center + p, center + p2, color);
p = p2;
}
}
#[deprecated]
#[doc(hidden)]
pub fn debug_polygon(center: Vec2, radius: f64, sides: i32, angle: f64, color: u32) {
draw_polygon(center, radius, sides, angle, color)
}
pub fn draw_triangle(center: Vec2, radius: f64, color: u32) {
let x = f64::sqrt(3.0) * radius / 2.0;
let y = radius / 2.0;
let points = [
center + vec2(0.0, radius),
center + vec2(-x, -y),
center + vec2(x, -y),
];
for i in 0..3 {
draw_line(points[i], points[(i + 1) % 3], color);
}
}
#[deprecated]
#[doc(hidden)]
pub fn debug_triangle(center: Vec2, radius: f64, color: u32) {
draw_triangle(center, radius, color)
}
pub fn draw_square(center: Vec2, radius: f64, color: u32) {
let offset = radius / f64::sqrt(2.0);
let mut p = vec2(offset, offset);
for _ in 0..4 {
let p2 = vec2(-p.y, p.x);
draw_line(center + p, center + p2, color);
p = p2;
}
}
#[deprecated]
#[doc(hidden)]
pub fn debug_square(center: Vec2, radius: f64, color: u32) {
draw_square(center, radius, color)
}
pub fn draw_diamond(center: Vec2, radius: f64, color: u32) {
let mut p = vec2(radius, 0.0);
for _ in 0..4 {
let p2 = vec2(-p.y, p.x);
draw_line(center + p, center + p2, color);
p = p2;
}
}
#[deprecated]
#[doc(hidden)]
pub fn debug_diamond(center: Vec2, radius: f64, color: u32) {
draw_diamond(center, radius, color)
}
#[macro_export]
macro_rules! draw_text {
($topleft:expr, $color:expr, $($arg:tt)*) => {
$crate::dbg::draw_text_internal($topleft, $color, std::format_args!($($arg)*))
};
}
#[allow(unused)]
#[doc(hidden)]
pub fn draw_text_internal(topleft: Vec2, color: u32, args: std::fmt::Arguments) {
use std::fmt::Write;
let mut text = String::new();
let _ = std::fmt::write(&mut text, args);
let buf = unsafe { &mut *ptr::addr_of_mut!(DRAWN_TEXT_BUFFER) };
let mut text_buf = [0u8; 11];
text_buf
.iter_mut()
.zip(text.bytes())
.for_each(|(d, s)| *d = s);
buf.push(Text {
x: topleft.x,
y: topleft.y,
color,
length: text.len().min(text_buf.len()) as u8,
text: text_buf,
});
}
#[doc(hidden)]
pub fn update() {
{
let slice = unsafe { &mut *ptr::addr_of_mut!(TEXT_BUFFER) }.as_bytes();
write_system_state(
super::SystemState::DebugTextPointer,
slice.as_ptr() as u32 as f64,
);
write_system_state(
super::SystemState::DebugTextLength,
slice.len() as u32 as f64,
);
}
{
let slice = unsafe { &mut *ptr::addr_of_mut!(LINE_BUFFER) }.as_slice();
write_system_state(
super::SystemState::DebugLinesPointer,
slice.as_ptr() as u32 as f64,
);
write_system_state(
super::SystemState::DebugLinesLength,
slice.len() as u32 as f64,
);
}
{
let slice = unsafe { &mut *ptr::addr_of_mut!(DRAWN_TEXT_BUFFER) }.as_slice();
write_system_state(
super::SystemState::DrawnTextPointer,
slice.as_ptr() as u32 as f64,
);
write_system_state(
super::SystemState::DrawnTextLength,
slice.len() as u32 as f64,
);
}
}
#[doc(hidden)]
pub fn reset() {
unsafe {
TEXT_BUFFER.clear();
LINE_BUFFER.clear();
DRAWN_TEXT_BUFFER.clear();
}
}
}
mod deprecated {
use super::api::*;
use super::sys::write_system_state;
use super::SystemState;
#[deprecated]
pub fn aim_gun(index: usize, heading: f64) {
aim(index, heading);
}
#[deprecated]
pub fn fire_gun(index: usize) {
fire(index);
}
#[deprecated]
pub fn launch_missile(index: usize, _unused: f64) {
use super::Class::*;
let state_index = match (class(), index) {
(Fighter, 0) => SystemState::Fire1,
(Frigate, 0) => SystemState::Fire3,
(Cruiser, 0) => SystemState::Fire1,
(Cruiser, 1) => SystemState::Fire2,
(Cruiser, 2) => SystemState::Fire3,
_ => return,
};
write_system_state(state_index, 1.0);
}
#[deprecated]
pub fn orders() -> f64 {
0.0
}
}
pub mod prelude {
#[doc(inline)]
pub use super::api::*;
#[doc(inline)]
pub use super::dbg::*;
#[doc(hidden)]
pub use super::deprecated::*;
#[doc(inline)]
pub use super::math::*;
#[doc(inline)]
pub use super::rng::*;
#[doc(inline)]
pub use super::vec::*;
#[doc(inline)]
pub use super::{Ability, Class, EcmMode, Message};
#[doc(inline)]
pub use crate::{debug, draw_text};
pub use byteorder;
pub use maths_rs;
pub use oorandom;
}