use std::fmt; use std::time::{SystemTime, UNIX_EPOCH}; use lazy_static::lazy_static; use num_traits::{abs, Num}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::de::{SeqAccess, Visitor}; use serde::ser::SerializeTupleStruct; use crate::bitfield; use crate::graphics::texture_set::G_MAG; /// Multiply cave story degrees (0-255, which corresponds to 0°-360°) with this constant to get /// respective value in radians. pub const CDEG_RAD: f64 = std::f64::consts::PI / 128.0; lazy_static! { pub static ref VERSION_BANNER: String = { let version = option_env!("DRS_BUILD_VERSION_OVERRIDE").unwrap_or(env!("CARGO_PKG_VERSION")); format!("doukutsu-rs {}", version) }; } bitfield! { #[derive(Clone, Copy)] #[repr(C)] pub struct Flag(u32); impl Debug; /// Set if left wall was hit. (corresponds to flag & 0x01) pub hit_left_wall, set_hit_left_wall: 0; /// Set if top wall was hit. (corresponds to flag & 0x02) pub hit_top_wall, set_hit_top_wall: 1; /// Set if right wall was hit. (corresponds to flag & 0x04) pub hit_right_wall, set_hit_right_wall: 2; /// Set if bottom wall was hit. (corresponds to flag & 0x08) pub hit_bottom_wall, set_hit_bottom_wall: 3; /// Set if entity stays on right slope. (corresponds to flag & 0x10) pub hit_right_slope, set_hit_right_slope: 4; /// Set if entity stays on left slope. (corresponds to flag & 0x20) pub hit_left_slope, set_hit_left_slope: 5; /// Used only in bullet code, set if a bullet hits upper right slope (corresponds to flag & 0x40) pub hit_upper_right_slope, set_hit_upper_right_slope: 6; /// Used only in bullet code, set if a bullet hits upper left slope (corresponds to flag & 0x80) pub hit_upper_left_slope, set_hit_upper_left_slope: 7; /// Set if entity is in water. (corresponds to flag & 0x100) pub in_water, set_in_water: 8; pub weapon_hit_block, set_weapon_hit_block: 9; // 0x200 pub hit_by_spike, set_hit_by_spike: 10; // 0x400 pub bloody_droplets, set_bloody_droplets: 11; // 0x800 pub force_left, set_force_left: 12; // 0x1000 pub force_up, set_force_up: 13; // 0x2000 pub force_right, set_force_right: 14; // 0x4000 pub force_down, set_force_down: 15; // 0x8000 pub hit_left_higher_half, set_hit_left_higher_half: 16; // 0x10000 pub hit_left_lower_half, set_hit_left_lower_half: 17; // 0x20000 pub hit_right_lower_half, set_hit_right_lower_half: 18; // 0x40000 pub hit_right_higher_half, set_hit_right_higher_half: 19; // 0x80000 } impl Flag { pub fn any_flag(&self) -> bool { self.0 != 0 } pub fn hit_anything(&self) -> bool { (self.0 & 0x2ff) != 0 } } bitfield! { #[derive(Clone, Copy)] #[repr(C)] pub struct Equipment(u16); impl Debug; pub has_booster_0_8, set_booster_0_8: 0; // 0x01 / 0001 pub has_map, set_map: 1; // 0x02 / 0002 pub has_arms_barrier, set_arms_barrier: 2; // 0x04 / 0004 pub has_turbocharge, set_turbocharge: 3; // 0x08 / 0008 pub has_air_tank, set_air_tank: 4; // 0x10 / 0016 pub has_booster_2_0, set_booster_2_0: 5; // 0x20 / 0032 pub has_mimiga_mask, set_mimiga_mask: 6; // 0x40 / 0064 pub has_whimsical_star, set_whimsical_star: 7; // 0x080 / 0128 pub has_nikumaru, set_nikumaru: 8; // 0x100 / 0256 // for custom equips pub unused_1, set_unused_1: 9; // 0x200 / 0512 pub unused_2, set_unused_2: 10; // 0x400 / 1024 pub unused_3, set_unused_3: 11; // 0x800 / 2048 pub unused_4, set_unused_4: 12; // 0x1000 / 4096 pub unused_5, set_unused_5: 13; // 0x2000 / 8192 // bit 14 and 15 aren't accessible via TSC without abusing overflows (won't work in strict mode) pub unused_6, set_unused_6: 14; // 0x4000 / @384 pub unused_7, set_unused_7: 15; // 0x8000 / P768 } bitfield! { #[derive(Clone, Copy)] #[repr(C)] pub struct Condition(u16); impl Debug; pub interacted, set_interacted: 0; // 0x01 pub hidden, set_hidden: 1; // 0x02 pub fallen, set_fallen: 2; // 0x04 pub explode_die, set_explode_die: 3; // 0x08 pub damage_boss, set_damage_boss: 4; // 0x10 pub increase_acceleration, set_increase_acceleration: 5; // 0x20 pub cond_x40, set_cond_x40: 6; // 0x40 pub alive, set_alive: 7; // 0x80 // engine specific flags pub drs_novanish, set_drs_novanish: 14; pub drs_boss, set_drs_boss: 15; } bitfield! { #[derive(Clone, Copy, Serialize, Deserialize)] #[repr(C)] pub struct ControlFlags(u16); impl Debug; pub tick_world, set_tick_world: 0; // 0x01 pub control_enabled, set_control_enabled: 1; // 0x02 pub interactions_disabled, set_interactions_disabled: 2; // 0x04 pub credits_running, set_credits_running: 3; // 0x08 // cs+ switch specific, according to peri: // Flag 0x10 prevents the OK button from restarting the item description event (resets when the cursor is moved) // (it does not prevent the cancel button from exiting the inventory, however) pub ok_button_disabled, set_ok_button_disabled: 4; // 0x10 // engine specific flags pub friendly_fire, set_friendly_fire: 14; } bitfield! { #[derive(Clone, Copy)] #[repr(C)] pub struct BulletFlag(u8); impl Debug; pub flag_x01, set_flag_x01: 0; // 0x01, nowhere in code? pub flag_x02, set_flag_x02: 1; // 0x02, nowhere in code? /// Corresponds to flag & 0x04. If set, bullet will pass through blocks. pub no_collision_checks, set_no_collision_checks: 2; /// Corresponds to flag & 0x08. IF set, bullet will bounce off walls. pub bounce_from_walls, set_bounce_from_walls: 3; /// Corresponds to flag & 0x10. IF set, bullet will not produce projectile dissipation effect when it hits a NPC or boss. pub no_proj_dissipation, set_no_proj_dissipation: 4; /// Corresponds to flag & 0x20. If set, performs checks in block collision check procedure. Kills the bullet if flag 0x40 isn't set. pub check_block_hit, set_check_block_hit: 5; /// Corresponds to flag & 0x40. If set, bullet will destroy snack blocks on hit. pub can_destroy_snack, set_can_destroy_snack: 6; pub flag_x80, set_flag_x80: 7; // 0x80, nowhere in code? } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum FadeDirection { Left = 0, Up, Right, Down, Center, } impl FadeDirection { pub const fn from_int(val: usize) -> Option { match val { 0 => Some(FadeDirection::Left), 1 => Some(FadeDirection::Up), 2 => Some(FadeDirection::Right), 3 => Some(FadeDirection::Down), 4 => Some(FadeDirection::Center), _ => None, } } pub const fn opposite(&self) -> FadeDirection { match self { FadeDirection::Left => FadeDirection::Right, FadeDirection::Up => FadeDirection::Down, FadeDirection::Right => FadeDirection::Left, FadeDirection::Down => FadeDirection::Up, FadeDirection::Center => FadeDirection::Center, } } } #[derive(Debug, PartialEq, Copy, Clone)] #[repr(u8)] pub enum FadeState { Visible, FadeIn(i8, FadeDirection), Hidden, FadeOut(i8, FadeDirection), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Direction { Left = 0, Up, Right, Bottom, FacingPlayer, } pub const FILE_TYPES: [&str; 3] = [".png", ".bmp", ".pbm"]; impl Direction { pub const fn from_int(val: usize) -> Option { match val { 0 => Some(Direction::Left), 1 => Some(Direction::Up), 2 => Some(Direction::Right), 3 => Some(Direction::Bottom), _ => None, } } pub const fn from_int_facing(val: usize) -> Option { match val { 0 => Some(Direction::Left), 1 => Some(Direction::Up), 2 => Some(Direction::Right), 3 => Some(Direction::Bottom), 4 => Some(Direction::FacingPlayer), _ => None, } } pub const fn opposite(&self) -> Direction { match self { Direction::Left => Direction::Right, Direction::Up => Direction::Bottom, Direction::Right => Direction::Left, Direction::Bottom => Direction::Up, Direction::FacingPlayer => Direction::FacingPlayer, } } pub const fn vector_x(&self) -> i32 { match self { Direction::Left => -1, Direction::Up => 0, Direction::Right => 1, Direction::Bottom => 0, Direction::FacingPlayer => 0, } } pub const fn vector_y(&self) -> i32 { match self { Direction::Left => 0, Direction::Up => -1, Direction::Right => 0, Direction::Bottom => 1, Direction::FacingPlayer => 0, } } } #[derive(Debug, Clone, Copy)] #[repr(C)] pub struct Rect { pub left: T, pub top: T, pub right: T, pub bottom: T, } impl Rect { #[inline(always)] pub fn new(left: T, top: T, right: T, bottom: T) -> Rect { Rect { left, top, right, bottom } } #[inline(always)] pub fn new_size(x: T, y: T, width: T, height: T) -> Rect { Rect { left: x, top: y, right: x.add(width), bottom: y.add(height) } } pub fn has_point(&self, x: T, y: T) -> bool { self.left.ge(&x) && self.right.le(&x) && self.top.ge(&y) && self.bottom.le(&y) } pub fn width(&self) -> T { if self.left.gt(&self.right) { self.left.sub(self.right) } else { self.right.sub(self.left) } } pub fn height(&self) -> T { if self.top.gt(&self.bottom) { self.top.sub(self.bottom) } else { self.bottom.sub(self.top) } } } impl Serialize for Rect { fn serialize(&self, serializer: S) -> Result where S: Serializer, { let mut state = serializer.serialize_tuple_struct("Rect", 4)?; state.serialize_field(&self.left)?; state.serialize_field(&self.top)?; state.serialize_field(&self.right)?; state.serialize_field(&self.bottom)?; state.end() } } impl Default for Rect { fn default() -> Self { Rect { left: num_traits::zero(), top: num_traits::zero(), right: num_traits::zero(), bottom: num_traits::zero(), } } } macro_rules! rect_deserialize { ($num_type: ident) => { impl<'de> Deserialize<'de> for Rect<$num_type> { fn deserialize(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { struct RectVisitor; impl<'de> Visitor<'de> for RectVisitor { type Value = Rect<$num_type>; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("Expected Rect structure.") } fn visit_seq(self, mut seq: V) -> Result where V: SeqAccess<'de>, { let invalid_length = || de::Error::invalid_length(0, &self); let left = seq.next_element()?.ok_or_else(invalid_length)?; let top = seq.next_element()?.ok_or_else(invalid_length)?; let right = seq.next_element()?.ok_or_else(invalid_length)?; let bottom = seq.next_element()?.ok_or_else(invalid_length)?; Ok(Rect { left, top, right, bottom }) } } deserializer.deserialize_tuple_struct("Rect", 4, RectVisitor) } } }; } rect_deserialize!(u8); rect_deserialize!(u16); rect_deserialize!(i32); rect_deserialize!(isize); rect_deserialize!(usize); #[inline(always)] pub fn fix9_scale(val: i32) -> f32 { unsafe { let mag = G_MAG as f32; (val as f32 * mag / 512.0).floor() / mag } } #[inline(always)] fn lerp_f64(v1: f64, v2: f64, t: f64) -> f64 { v1 * (1.0 - t) + v2 * t } pub fn interpolate_fix9_scale(old_val: i32, val: i32, frame_delta: f64) -> f32 { if abs(old_val - val) > 0x1800 { return val as f32 / 512.0; } unsafe { let interpolated = lerp_f64(old_val as f64, val as f64, frame_delta) as f32; let mag = G_MAG as f32; (interpolated * mag / 512.0).floor() / mag } } pub fn get_timestamp() -> u64 { let now = SystemTime::now(); now.duration_since(UNIX_EPOCH).unwrap().as_secs() as u64 } /// A RGBA color in the `sRGB` color space represented as `f32`'s in the range `[0.0-1.0]` /// /// For convenience, [`WHITE`](constant.WHITE.html) and [`BLACK`](constant.BLACK.html) are provided. #[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)] pub struct Color { /// Red component pub r: f32, /// Green component pub g: f32, /// Blue component pub b: f32, /// Alpha component pub a: f32, } impl Color { /// Create a new `Color` from four `f32`'s in the range `[0.0-1.0]` pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { Color { r, g, b, a } } /// Create a new `Color` from four `u8`'s in the range `[0-255]` pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color { Color::from((r, g, b, a)) } /// Create a new `Color` from three u8's in the range `[0-255]`, /// with the alpha component fixed to 255 (opaque) pub fn from_rgb(r: u8, g: u8, b: u8) -> Color { Color::from((r, g, b)) } /// Return a tuple of four `u8`'s in the range `[0-255]` with the `Color`'s /// components. pub fn to_rgba(self) -> (u8, u8, u8, u8) { self.into() } /// Return a tuple of three `u8`'s in the range `[0-255]` with the `Color`'s /// components. pub fn to_rgb(self) -> (u8, u8, u8) { self.into() } /// Convert a packed `u32` containing `0xRRGGBBAA` into a `Color` pub fn from_rgba_u32(c: u32) -> Color { let c = c.to_be_bytes(); Color::from((c[0], c[1], c[2], c[3])) } /// Convert a packed `u32` containing `0x00RRGGBB` into a `Color`. /// This lets you do things like `Color::from_rgb_u32(0xCD09AA)` easily if you want. pub fn from_rgb_u32(c: u32) -> Color { let c = c.to_be_bytes(); Color::from((c[1], c[2], c[3])) } /// Convert a `Color` into a packed `u32`, containing `0xRRGGBBAA` as bytes. pub fn to_rgba_u32(self) -> u32 { let (r, g, b, a): (u8, u8, u8, u8) = self.into(); u32::from_be_bytes([r, g, b, a]) } /// Convert a `Color` into a packed `u32`, containing `0x00RRGGBB` as bytes. pub fn to_rgb_u32(self) -> u32 { let (r, g, b, _a): (u8, u8, u8, u8) = self.into(); u32::from_be_bytes([0, r, g, b]) } } impl From<(u8, u8, u8, u8)> for Color { /// Convert a `(R, G, B, A)` tuple of `u8`'s in the range `[0-255]` into a `Color` fn from(val: (u8, u8, u8, u8)) -> Self { let (r, g, b, a) = val; let rf = (f32::from(r)) / 255.0; let gf = (f32::from(g)) / 255.0; let bf = (f32::from(b)) / 255.0; let af = (f32::from(a)) / 255.0; Color::new(rf, gf, bf, af) } } impl From<(u8, u8, u8)> for Color { /// Convert a `(R, G, B)` tuple of `u8`'s in the range `[0-255]` into a `Color`, /// with a value of 255 for the alpha element (i.e., no transparency.) fn from(val: (u8, u8, u8)) -> Self { let (r, g, b) = val; Color::from((r, g, b, 255)) } } impl From<[f32; 4]> for Color { /// Turns an `[R, G, B, A] array of `f32`'s into a `Color` with no format changes. /// All inputs should be in the range `[0.0-1.0]`. fn from(val: [f32; 4]) -> Self { Color::new(val[0], val[1], val[2], val[3]) } } impl From<(f32, f32, f32)> for Color { /// Convert a `(R, G, B)` tuple of `f32`'s in the range `[0.0-1.0]` into a `Color`, /// with a value of 1.0 to for the alpha element (ie, no transparency.) fn from(val: (f32, f32, f32)) -> Self { let (r, g, b) = val; Color::new(r, g, b, 1.0) } } impl From<(f32, f32, f32, f32)> for Color { /// Convert a `(R, G, B, A)` tuple of `f32`'s in the range `[0.0-1.0]` into a `Color` fn from(val: (f32, f32, f32, f32)) -> Self { let (r, g, b, a) = val; Color::new(r, g, b, a) } } impl From for (u8, u8, u8, u8) { /// Convert a `Color` into a `(R, G, B, A)` tuple of `u8`'s in the range of `[0-255]`. fn from(color: Color) -> Self { let r = (color.r * 255.0) as u8; let g = (color.g * 255.0) as u8; let b = (color.b * 255.0) as u8; let a = (color.a * 255.0) as u8; (r, g, b, a) } } impl From for (u8, u8, u8) { /// Convert a `Color` into a `(R, G, B)` tuple of `u8`'s in the range of `[0-255]`, /// ignoring the alpha term. fn from(color: Color) -> Self { let (r, g, b, _) = color.into(); (r, g, b) } } impl From for [f32; 4] { /// Convert a `Color` into an `[R, G, B, A]` array of `f32`'s in the range of `[0.0-1.0]`. fn from(color: Color) -> Self { [color.r, color.g, color.b, color.a] } } pub trait SliceExt { type Item; fn get_two_mut(&mut self, a: usize, b: usize) -> Option<(&mut Self::Item, &mut Self::Item)>; } impl SliceExt for [T] { type Item = T; fn get_two_mut(&mut self, a: usize, b: usize) -> Option<(&mut Self::Item, &mut Self::Item)> { if a == b || a >= self.len() || b >= self.len() { return None; } unsafe { let ar = &mut *(self.get_unchecked_mut(a) as *mut _); let br = &mut *(self.get_unchecked_mut(b) as *mut _); Some((ar, br)) } } }