2020-10-26 08:34:54 +00:00
|
|
|
use std::cmp::Ordering;
|
2020-12-06 14:50:31 +00:00
|
|
|
use std::fmt;
|
2020-10-26 08:34:54 +00:00
|
|
|
|
2020-12-06 15:41:12 +00:00
|
|
|
use lazy_static::lazy_static;
|
2021-03-29 21:19:07 +00:00
|
|
|
use num_traits::{abs, Num};
|
2020-12-06 14:50:31 +00:00
|
|
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
use serde::de::{SeqAccess, Visitor};
|
|
|
|
use serde::ser::SerializeTupleStruct;
|
2020-08-18 16:46:07 +00:00
|
|
|
|
2020-08-31 18:33:01 +00:00
|
|
|
use crate::bitfield;
|
2021-05-05 18:36:21 +00:00
|
|
|
use crate::texture_set::G_MAG;
|
2020-08-31 18:33:01 +00:00
|
|
|
|
2020-11-01 19:05:29 +00:00
|
|
|
/// Multiply cave story degrees (0-255, which corresponds to 0°-360°) with this to get
|
|
|
|
/// respective value in radians.
|
2020-11-17 10:46:51 +00:00
|
|
|
pub const CDEG_RAD: f64 = std::f64::consts::PI / 128.0;
|
2020-12-06 15:41:12 +00:00
|
|
|
lazy_static! {
|
|
|
|
pub static ref VERSION_BANNER: String = {
|
|
|
|
let version = option_env!("DRS_BUILD_VERSION_OVERRIDE").unwrap_or(env!("CARGO_PKG_VERSION"));
|
2021-04-03 20:15:17 +00:00
|
|
|
format!("doukutsu-rs {}", version)
|
2020-12-06 15:41:12 +00:00
|
|
|
};
|
|
|
|
}
|
2020-11-01 19:05:29 +00:00
|
|
|
|
2020-08-31 18:33:01 +00:00
|
|
|
bitfield! {
|
2020-09-11 16:30:18 +00:00
|
|
|
#[derive(Clone, Copy)]
|
2021-03-29 21:19:07 +00:00
|
|
|
#[repr(C)]
|
2020-09-04 23:08:33 +00:00
|
|
|
pub struct Flag(u32);
|
2020-08-31 18:33:01 +00:00
|
|
|
impl Debug;
|
|
|
|
|
2020-12-06 23:42:19 +00:00
|
|
|
/// 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;
|
|
|
|
/// Unknown purpose (corresponds to flag & 0x40)
|
|
|
|
pub flag_x40, set_flag_x40: 6;
|
|
|
|
/// Unknown purpose (corresponds to flag & 0x80)
|
|
|
|
pub flag_x80, set_flag_x80: 7;
|
|
|
|
/// Set if entity is in water. (corresponds to flag & 0x100)
|
|
|
|
pub in_water, set_in_water: 8;
|
2020-09-12 00:42:44 +00:00
|
|
|
pub weapon_hit_block, set_weapon_hit_block: 9; // 0x200
|
2020-08-31 18:33:01 +00:00
|
|
|
pub hit_by_spike, set_hit_by_spike: 10; // 0x400
|
|
|
|
pub water_splash_facing_right, set_water_splash_facing_right: 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
|
2021-05-05 16:34:23 +00:00
|
|
|
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
|
2020-08-31 18:33:01 +00:00
|
|
|
}
|
|
|
|
|
2021-03-09 14:05:38 +00:00
|
|
|
impl Flag {
|
|
|
|
pub fn hit_anything(&self) -> bool {
|
|
|
|
(self.0 & 0x2ff) != 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-31 18:33:01 +00:00
|
|
|
bitfield! {
|
2020-09-11 16:30:18 +00:00
|
|
|
#[derive(Clone, Copy)]
|
2021-03-29 21:19:07 +00:00
|
|
|
#[repr(C)]
|
2020-09-04 23:08:33 +00:00
|
|
|
pub struct Equipment(u16);
|
2020-08-31 18:33:01 +00:00
|
|
|
impl Debug;
|
|
|
|
|
2020-09-25 12:55:28 +00:00
|
|
|
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
|
2020-08-31 18:33:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bitfield! {
|
2020-09-11 16:30:18 +00:00
|
|
|
#[derive(Clone, Copy)]
|
2021-03-29 21:19:07 +00:00
|
|
|
#[repr(C)]
|
2020-09-04 23:08:33 +00:00
|
|
|
pub struct Condition(u16);
|
2020-08-31 18:33:01 +00:00
|
|
|
impl Debug;
|
|
|
|
|
2020-08-31 19:46:03 +00:00
|
|
|
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
|
2020-10-26 08:34:54 +00:00
|
|
|
pub increase_acceleration, set_increase_acceleration: 5; // 0x20
|
2020-08-31 19:46:03 +00:00
|
|
|
pub cond_x40, set_cond_x40: 6; // 0x40
|
|
|
|
pub alive, set_alive: 7; // 0x80
|
2020-11-01 19:05:29 +00:00
|
|
|
|
|
|
|
// engine specific flags
|
2020-12-25 22:39:41 +00:00
|
|
|
pub drs_boss, set_drs_boss: 15;
|
2020-08-31 18:33:01 +00:00
|
|
|
}
|
|
|
|
|
2020-09-09 13:06:11 +00:00
|
|
|
bitfield! {
|
2020-11-17 02:42:45 +00:00
|
|
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
2021-03-29 21:19:07 +00:00
|
|
|
#[repr(C)]
|
2020-09-09 13:06:11 +00:00
|
|
|
pub struct ControlFlags(u16);
|
|
|
|
impl Debug;
|
2020-09-12 00:42:44 +00:00
|
|
|
|
2020-09-25 12:55:28 +00:00
|
|
|
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
|
2020-09-12 21:43:27 +00:00
|
|
|
|
|
|
|
// engine specific flags
|
2020-11-30 13:31:40 +00:00
|
|
|
pub friendly_fire, set_friendly_fire: 14;
|
2020-09-12 21:43:27 +00:00
|
|
|
pub wind, set_wind: 15;
|
2020-09-09 13:06:11 +00:00
|
|
|
}
|
|
|
|
|
2020-12-04 18:58:59 +00:00
|
|
|
bitfield! {
|
|
|
|
#[derive(Clone, Copy)]
|
2021-03-29 21:19:07 +00:00
|
|
|
#[repr(C)]
|
2020-12-04 18:58:59 +00:00
|
|
|
pub struct BulletFlag(u16);
|
|
|
|
impl Debug;
|
|
|
|
pub flag_x01, set_flag_x01: 0; // 0x01
|
|
|
|
pub flag_x02, set_flag_x02: 1; // 0x02
|
2021-05-05 16:34:23 +00:00
|
|
|
pub no_collision_checks, set_no_collision_checks: 2; // 0x04
|
|
|
|
pub bounce_from_walls, set_bounce_from_walls: 3; // 0x08
|
2020-12-04 18:58:59 +00:00
|
|
|
pub flag_x10, set_flag_x10: 4; // 0x10
|
|
|
|
pub flag_x20, set_flag_x20: 5; // 0x20
|
2021-05-05 16:34:23 +00:00
|
|
|
pub can_destroy_snack, set_can_destroy_snack: 6; // 0x40
|
2020-12-04 18:58:59 +00:00
|
|
|
pub flag_x80, set_flag_x80: 7; // 0x80
|
|
|
|
}
|
|
|
|
|
2020-08-18 16:46:07 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2020-08-28 01:41:14 +00:00
|
|
|
#[repr(u8)]
|
|
|
|
pub enum FadeDirection {
|
|
|
|
Left = 0,
|
|
|
|
Up,
|
|
|
|
Right,
|
|
|
|
Down,
|
|
|
|
Center,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FadeDirection {
|
|
|
|
pub fn from_int(val: usize) -> Option<FadeDirection> {
|
|
|
|
match val {
|
|
|
|
0 => { Some(FadeDirection::Left) }
|
|
|
|
1 => { Some(FadeDirection::Up) }
|
|
|
|
2 => { Some(FadeDirection::Right) }
|
|
|
|
3 => { Some(FadeDirection::Down) }
|
|
|
|
4 => { Some(FadeDirection::Center) }
|
|
|
|
_ => { None }
|
|
|
|
}
|
|
|
|
}
|
2020-09-05 01:14:58 +00:00
|
|
|
|
|
|
|
pub 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 }
|
|
|
|
}
|
|
|
|
}
|
2020-08-28 01:41:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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)]
|
2020-08-18 16:46:07 +00:00
|
|
|
pub enum Direction {
|
|
|
|
Left = 0,
|
2020-08-28 01:41:14 +00:00
|
|
|
Up,
|
|
|
|
Right,
|
|
|
|
Bottom,
|
2020-09-30 03:11:25 +00:00
|
|
|
FacingPlayer,
|
2020-08-18 16:46:07 +00:00
|
|
|
}
|
|
|
|
|
2020-08-29 06:59:46 +00:00
|
|
|
pub const FILE_TYPES: [&str; 3] = [".png", ".bmp", ".pbm"];
|
|
|
|
|
2020-08-18 16:46:07 +00:00
|
|
|
impl Direction {
|
|
|
|
pub fn from_int(val: usize) -> Option<Direction> {
|
|
|
|
match val {
|
|
|
|
0 => { Some(Direction::Left) }
|
|
|
|
1 => { Some(Direction::Up) }
|
|
|
|
2 => { Some(Direction::Right) }
|
|
|
|
3 => { Some(Direction::Bottom) }
|
|
|
|
_ => { None }
|
|
|
|
}
|
|
|
|
}
|
2020-08-20 18:31:47 +00:00
|
|
|
|
2020-09-30 03:11:25 +00:00
|
|
|
pub fn from_int_facing(val: usize) -> Option<Direction> {
|
|
|
|
match val {
|
|
|
|
0 => { Some(Direction::Left) }
|
|
|
|
1 => { Some(Direction::Up) }
|
|
|
|
2 => { Some(Direction::Right) }
|
|
|
|
3 => { Some(Direction::Bottom) }
|
|
|
|
4 => { Some(Direction::FacingPlayer) }
|
|
|
|
_ => { None }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-20 18:31:47 +00:00
|
|
|
pub fn opposite(&self) -> Direction {
|
|
|
|
match self {
|
|
|
|
Direction::Left => { Direction::Right }
|
|
|
|
Direction::Up => { Direction::Bottom }
|
|
|
|
Direction::Right => { Direction::Left }
|
|
|
|
Direction::Bottom => { Direction::Up }
|
2020-09-30 03:11:25 +00:00
|
|
|
Direction::FacingPlayer => unreachable!(),
|
2020-08-20 18:31:47 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-23 02:17:24 +00:00
|
|
|
|
2021-01-01 01:46:01 +00:00
|
|
|
pub fn vector_x(&self) -> i32 {
|
2020-08-23 02:17:24 +00:00
|
|
|
match self {
|
|
|
|
Direction::Left => { -1 }
|
|
|
|
Direction::Up => { 0 }
|
|
|
|
Direction::Right => { 1 }
|
|
|
|
Direction::Bottom => { 0 }
|
2020-09-30 03:11:25 +00:00
|
|
|
Direction::FacingPlayer => unreachable!(),
|
2020-08-23 02:17:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-01 01:46:01 +00:00
|
|
|
pub fn vector_y(&self) -> i32 {
|
2020-08-23 02:17:24 +00:00
|
|
|
match self {
|
|
|
|
Direction::Left => { 0 }
|
|
|
|
Direction::Up => { -1 }
|
|
|
|
Direction::Right => { 0 }
|
|
|
|
Direction::Bottom => { 1 }
|
2020-09-30 03:11:25 +00:00
|
|
|
Direction::FacingPlayer => unreachable!(),
|
2020-08-23 02:17:24 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-18 16:46:07 +00:00
|
|
|
}
|
|
|
|
|
2021-01-28 22:33:43 +00:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub struct Point<T: Num + PartialOrd + Copy = isize> {
|
|
|
|
pub x: T,
|
|
|
|
pub y: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Num + PartialOrd + Copy> Point<T> {
|
|
|
|
#[inline(always)]
|
|
|
|
pub fn new(x: T, y: T) -> Point<T> {
|
|
|
|
Point {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-06 14:50:31 +00:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2021-03-29 21:19:07 +00:00
|
|
|
#[repr(C)]
|
2020-10-26 08:34:54 +00:00
|
|
|
pub struct Rect<T: Num + PartialOrd + Copy = isize> {
|
2020-08-18 16:46:07 +00:00
|
|
|
pub left: T,
|
|
|
|
pub top: T,
|
|
|
|
pub right: T,
|
|
|
|
pub bottom: T,
|
|
|
|
}
|
|
|
|
|
2020-10-26 08:34:54 +00:00
|
|
|
impl<T: Num + PartialOrd + Copy> Rect<T> {
|
2021-01-28 22:33:43 +00:00
|
|
|
#[inline(always)]
|
2020-09-04 23:08:33 +00:00
|
|
|
pub fn new(left: T, top: T, right: T, bottom: T) -> Rect<T> {
|
2020-08-18 16:46:07 +00:00
|
|
|
Rect {
|
|
|
|
left,
|
|
|
|
top,
|
|
|
|
right,
|
|
|
|
bottom,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-28 22:33:43 +00:00
|
|
|
#[inline(always)]
|
2020-09-04 23:08:33 +00:00
|
|
|
pub fn new_size(x: T, y: T, width: T, height: T) -> Rect<T> {
|
2020-08-18 16:46:07 +00:00
|
|
|
Rect {
|
|
|
|
left: x,
|
|
|
|
top: y,
|
|
|
|
right: x.add(width),
|
|
|
|
bottom: y.add(height),
|
|
|
|
}
|
|
|
|
}
|
2021-01-28 22:33:43 +00:00
|
|
|
|
2020-09-20 01:05:41 +00:00
|
|
|
pub fn width(&self) -> T {
|
2020-10-26 08:34:54 +00:00
|
|
|
if let Some(Ordering::Greater) = self.left.partial_cmp(&self.right) {
|
|
|
|
self.left.sub(self.right)
|
|
|
|
} else {
|
|
|
|
self.right.sub(self.left)
|
|
|
|
}
|
2020-09-20 01:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn height(&self) -> T {
|
2020-10-26 08:34:54 +00:00
|
|
|
if let Some(Ordering::Greater) = self.top.partial_cmp(&self.bottom) {
|
|
|
|
self.top.sub(self.bottom)
|
|
|
|
} else {
|
|
|
|
self.bottom.sub(self.top)
|
|
|
|
}
|
2020-09-20 01:05:41 +00:00
|
|
|
}
|
2020-08-18 16:46:07 +00:00
|
|
|
}
|
2020-09-10 10:26:24 +00:00
|
|
|
|
2020-12-06 14:50:31 +00:00
|
|
|
impl<T: Num + PartialOrd + Copy + Serialize> Serialize for Rect<T> {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
macro_rules! rect_deserialze {
|
|
|
|
($num_type: ident) => {
|
|
|
|
impl<'de> Deserialize<'de> for Rect<$num_type> {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Rect<$num_type>, 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<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
|
|
|
|
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_deserialze!(u8);
|
|
|
|
rect_deserialze!(u16);
|
2021-01-01 01:46:01 +00:00
|
|
|
rect_deserialze!(i32);
|
2020-12-06 14:50:31 +00:00
|
|
|
rect_deserialze!(isize);
|
|
|
|
rect_deserialze!(usize);
|
|
|
|
|
2020-10-29 22:14:53 +00:00
|
|
|
#[inline(always)]
|
2021-05-05 18:36:21 +00:00
|
|
|
pub fn fix9_scale(val: i32) -> f32 {
|
|
|
|
unsafe {
|
|
|
|
let mag = G_MAG as f32;
|
|
|
|
(val as f32 * mag / 512.0).floor() / mag
|
|
|
|
}
|
2020-10-29 22:14:53 +00:00
|
|
|
}
|
2020-10-30 22:47:29 +00:00
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
fn lerp_f64(v1: f64, v2: f64, t: f64) -> f64 {
|
2021-01-16 22:49:43 +00:00
|
|
|
v1 * (1.0 - t) + v2 * t
|
2020-10-30 22:47:29 +00:00
|
|
|
}
|
|
|
|
|
2021-05-05 18:36:21 +00:00
|
|
|
#[inline(always)]
|
|
|
|
fn lerp_f32(v1: f32, v2: f32, t: f32) -> f32 {
|
|
|
|
v1 * (1.0 - t) + v2 * t
|
|
|
|
}
|
|
|
|
|
2021-01-01 01:46:01 +00:00
|
|
|
pub fn interpolate_fix9_scale(old_val: i32, val: i32, frame_delta: f64) -> f32 {
|
2021-05-05 18:36:21 +00:00
|
|
|
if abs(old_val - val) > 0x1800 {
|
2021-01-16 22:49:43 +00:00
|
|
|
return val as f32 / 512.0;
|
2020-11-14 01:24:32 +00:00
|
|
|
}
|
|
|
|
|
2021-05-05 18:36:21 +00:00
|
|
|
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
|
|
|
|
}
|
2020-10-30 22:47:29 +00:00
|
|
|
}
|
2021-01-27 18:20:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
/// 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]`
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Color {
|
2021-01-27 18:20:47 +00:00
|
|
|
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)
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn from_rgb(r: u8, g: u8, b: u8) -> Color {
|
2021-01-27 18:20:47 +00:00
|
|
|
Color::from((r, g, b))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a tuple of four `u8`'s in the range `[0-255]` with the `Color`'s
|
|
|
|
/// components.
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn to_rgba(self) -> (u8, u8, u8, u8) {
|
2021-01-27 18:20:47 +00:00
|
|
|
self.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a tuple of three `u8`'s in the range `[0-255]` with the `Color`'s
|
|
|
|
/// components.
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn to_rgb(self) -> (u8, u8, u8) {
|
2021-01-27 18:20:47 +00:00
|
|
|
self.into()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert a packed `u32` containing `0xRRGGBBAA` into a `Color`
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn from_rgba_u32(c: u32) -> Color {
|
2021-01-27 18:20:47 +00:00
|
|
|
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.
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn from_rgb_u32(c: u32) -> Color {
|
2021-01-27 18:20:47 +00:00
|
|
|
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.
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn to_rgba_u32(self) -> u32 {
|
2021-01-27 18:20:47 +00:00
|
|
|
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.
|
2021-01-28 22:33:43 +00:00
|
|
|
pub fn to_rgb_u32(self) -> u32 {
|
2021-01-27 18:20:47 +00:00
|
|
|
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<Color> 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<Color> 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<Color> 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]
|
|
|
|
}
|
2021-01-28 22:33:43 +00:00
|
|
|
}
|