diff --git a/packages/lib-roblox/src/datatypes/conversion.rs b/packages/lib-roblox/src/datatypes/conversion.rs index 9360034..73eb715 100644 --- a/packages/lib-roblox/src/datatypes/conversion.rs +++ b/packages/lib-roblox/src/datatypes/conversion.rs @@ -121,8 +121,6 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> { // check `EnumItem::from_instance_property` for specifics Ok(match variant.clone() { // Not yet implemented datatypes - // Rbx::NumberRange(_) => todo!(), - // Rbx::NumberSequence(_) => todo!(), // Rbx::OptionalCFrame(_) => todo!(), // Rbx::PhysicalProperties(_) => todo!(), // Rbx::Ray(_) => todo!(), @@ -139,6 +137,9 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> { Rbx::Color3uint8(value) => lua.create_userdata(Color3::from(value))?, Rbx::ColorSequence(value) => lua.create_userdata(ColorSequence::from(value))?, + Rbx::NumberRange(value) => lua.create_userdata(NumberRange::from(value))?, + Rbx::NumberSequence(value) => lua.create_userdata(NumberSequence::from(value))?, + Rbx::Rect(value) => lua.create_userdata(Rect::from(value))?, Rbx::UDim(value) => lua.create_userdata(UDim::from(value))?, Rbx::UDim2(value) => lua.create_userdata(UDim2::from(value))?, @@ -186,6 +187,9 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> { RbxVariantType::UDim => convert::, RbxVariantType::UDim2 => convert::, + RbxVariantType::NumberRange => convert::, + RbxVariantType::NumberSequence => convert::, + RbxVariantType::Vector2 => convert::, RbxVariantType::Vector2int16 => convert::, RbxVariantType::Vector3 => convert::, diff --git a/packages/lib-roblox/src/datatypes/types/mod.rs b/packages/lib-roblox/src/datatypes/types/mod.rs index 139c5e0..7206969 100644 --- a/packages/lib-roblox/src/datatypes/types/mod.rs +++ b/packages/lib-roblox/src/datatypes/types/mod.rs @@ -8,6 +8,9 @@ mod r#enum; mod r#enum_item; mod r#enums; mod faces; +mod number_range; +mod number_sequence; +mod number_sequence_keypoint; mod rect; mod udim; mod udim2; @@ -23,6 +26,9 @@ pub use color3::Color3; pub use color_sequence::ColorSequence; pub use color_sequence_keypoint::ColorSequenceKeypoint; pub use faces::Faces; +pub use number_range::NumberRange; +pub use number_sequence::NumberSequence; +pub use number_sequence_keypoint::NumberSequenceKeypoint; pub use r#enum::Enum; pub use r#enum_item::EnumItem; pub use r#enums::Enums; @@ -76,20 +82,23 @@ mod tests { } create_tests! { - axes: "datatypes/Axes", - brick_color: "datatypes/BrickColor", - cframe: "datatypes/CFrame", - color3: "datatypes/Color3", - color_sequence: "datatypes/ColorSequence", - color_sequence_keypoint: "datatypes/ColorSequenceKeypoint", - r#enum: "datatypes/Enum", - faces: "datatypes/Faces", - rect: "datatypes/Rect", - udim: "datatypes/UDim", - udim2: "datatypes/UDim2", - vector2: "datatypes/Vector2", - vector2int16: "datatypes/Vector2int16", - vector3: "datatypes/Vector3", - vector3int16: "datatypes/Vector3int16", + axes: "datatypes/Axes", + brick_color: "datatypes/BrickColor", + cframe: "datatypes/CFrame", + color3: "datatypes/Color3", + color_sequence: "datatypes/ColorSequence", + color_sequence_keypoint: "datatypes/ColorSequenceKeypoint", + r#enum: "datatypes/Enum", + faces: "datatypes/Faces", + number_range: "datatypes/NumberRange", + number_sequence: "datatypes/NumberSequence", + number_sequence_keypoint: "datatypes/NumberSequenceKeypoint", + rect: "datatypes/Rect", + udim: "datatypes/UDim", + udim2: "datatypes/UDim2", + vector2: "datatypes/Vector2", + vector2int16: "datatypes/Vector2int16", + vector3: "datatypes/Vector3", + vector3int16: "datatypes/Vector3int16", } } diff --git a/packages/lib-roblox/src/datatypes/types/number_range.rs b/packages/lib-roblox/src/datatypes/types/number_range.rs new file mode 100644 index 0000000..b54935c --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/number_range.rs @@ -0,0 +1,70 @@ +use core::fmt; + +use mlua::prelude::*; +use rbx_dom_weak::types::NumberRange as RbxNumberRange; + +use super::super::*; + +/** + An implementation of the [NumberRange](https://create.roblox.com/docs/reference/engine/datatypes/NumberRange) Roblox datatype. + + This implements all documented properties, methods & constructors of the NumberRange class as of March 2023. +*/ +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct NumberRange { + pub(crate) min: f32, + pub(crate) max: f32, +} + +impl NumberRange { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + datatype_table.set( + "new", + lua.create_function(|_, (min, max): (f32, Option)| { + Ok(match max { + Some(max) => NumberRange { + min: min.min(max), + max: min.max(max), + }, + None => NumberRange { min, max: min }, + }) + })?, + ) + } +} + +impl LuaUserData for NumberRange { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("Min", |_, this| Ok(this.min)); + fields.add_field_method_get("Max", |_, this| Ok(this.max)); + } + + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); + } +} + +impl fmt::Display for NumberRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}, {}", self.min, self.max) + } +} + +impl From for NumberRange { + fn from(v: RbxNumberRange) -> Self { + Self { + min: v.min, + max: v.max, + } + } +} + +impl From for RbxNumberRange { + fn from(v: NumberRange) -> Self { + Self { + min: v.min, + max: v.max, + } + } +} diff --git a/packages/lib-roblox/src/datatypes/types/number_sequence.rs b/packages/lib-roblox/src/datatypes/types/number_sequence.rs new file mode 100644 index 0000000..60e89a7 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/number_sequence.rs @@ -0,0 +1,119 @@ +use core::fmt; + +use mlua::prelude::*; +use rbx_dom_weak::types::{ + NumberSequence as RbxNumberSequence, NumberSequenceKeypoint as RbxNumberSequenceKeypoint, +}; + +use super::{super::*, NumberSequenceKeypoint}; + +/** + An implementation of the [NumberSequence](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequence) Roblox datatype. + + This implements all documented properties, methods & constructors of the NumberSequence class as of March 2023. +*/ +#[derive(Debug, Clone, PartialEq)] +pub struct NumberSequence { + pub(crate) keypoints: Vec, +} + +impl NumberSequence { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + type ArgsColor = f32; + type ArgsColors = (f32, f32); + type ArgsKeypoints = Vec; + datatype_table.set( + "new", + lua.create_function(|lua, args: LuaMultiValue| { + if let Ok(value) = ArgsColor::from_lua_multi(args.clone(), lua) { + Ok(NumberSequence { + keypoints: vec![ + NumberSequenceKeypoint { + time: 0.0, + value, + envelope: 0.0, + }, + NumberSequenceKeypoint { + time: 1.0, + value, + envelope: 0.0, + }, + ], + }) + } else if let Ok((v0, v1)) = ArgsColors::from_lua_multi(args.clone(), lua) { + Ok(NumberSequence { + keypoints: vec![ + NumberSequenceKeypoint { + time: 0.0, + value: v0, + envelope: 0.0, + }, + NumberSequenceKeypoint { + time: 1.0, + value: v1, + envelope: 0.0, + }, + ], + }) + } else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) { + Ok(NumberSequence { keypoints }) + } else { + // FUTURE: Better error message here using given arg types + Err(LuaError::RuntimeError( + "Invalid arguments to constructor".to_string(), + )) + } + })?, + ) + } +} + +impl LuaUserData for NumberSequence { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("Keypoints", |_, this| Ok(this.keypoints.clone())); + } + + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); + } +} + +impl fmt::Display for NumberSequence { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (index, keypoint) in self.keypoints.iter().enumerate() { + if index < self.keypoints.len() - 1 { + write!(f, "{}, ", keypoint)?; + } else { + write!(f, "{}", keypoint)?; + } + } + Ok(()) + } +} + +impl From for NumberSequence { + fn from(v: RbxNumberSequence) -> Self { + Self { + keypoints: v + .keypoints + .iter() + .cloned() + .map(NumberSequenceKeypoint::from) + .collect(), + } + } +} + +impl From for RbxNumberSequence { + fn from(v: NumberSequence) -> Self { + Self { + keypoints: v + .keypoints + .iter() + .cloned() + .map(RbxNumberSequenceKeypoint::from) + .collect(), + } + } +} diff --git a/packages/lib-roblox/src/datatypes/types/number_sequence_keypoint.rs b/packages/lib-roblox/src/datatypes/types/number_sequence_keypoint.rs new file mode 100644 index 0000000..daee984 --- /dev/null +++ b/packages/lib-roblox/src/datatypes/types/number_sequence_keypoint.rs @@ -0,0 +1,73 @@ +use core::fmt; + +use mlua::prelude::*; +use rbx_dom_weak::types::NumberSequenceKeypoint as RbxNumberSequenceKeypoint; + +use super::super::*; + +/** + An implementation of the [NumberSequenceKeypoint](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequenceKeypoint) Roblox datatype. + + This implements all documented properties, methods & constructors of the NumberSequenceKeypoint class as of March 2023. +*/ +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct NumberSequenceKeypoint { + pub(crate) time: f32, + pub(crate) value: f32, + pub(crate) envelope: f32, +} + +impl NumberSequenceKeypoint { + pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> { + datatype_table.set( + "new", + lua.create_function(|_, (time, value, envelope): (f32, f32, Option)| { + Ok(NumberSequenceKeypoint { + time, + value, + envelope: envelope.unwrap_or_default(), + }) + })?, + )?; + Ok(()) + } +} + +impl LuaUserData for NumberSequenceKeypoint { + fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("Time", |_, this| Ok(this.time)); + fields.add_field_method_get("Value", |_, this| Ok(this.value)); + fields.add_field_method_get("Envelope", |_, this| Ok(this.envelope)); + } + + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq); + methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string); + } +} + +impl fmt::Display for NumberSequenceKeypoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} > {}", self.time, self.value) + } +} + +impl From for NumberSequenceKeypoint { + fn from(v: RbxNumberSequenceKeypoint) -> Self { + Self { + time: v.time, + value: v.value, + envelope: v.envelope, + } + } +} + +impl From for RbxNumberSequenceKeypoint { + fn from(v: NumberSequenceKeypoint) -> Self { + Self { + time: v.time, + value: v.value, + envelope: v.envelope, + } + } +} diff --git a/packages/lib-roblox/src/datatypes/types/udim2.rs b/packages/lib-roblox/src/datatypes/types/udim2.rs index 88a6f12..bd50d95 100644 --- a/packages/lib-roblox/src/datatypes/types/udim2.rs +++ b/packages/lib-roblox/src/datatypes/types/udim2.rs @@ -76,7 +76,7 @@ impl LuaUserData for UDim2 { // Methods methods.add_method("Lerp", |_, this, (goal, alpha): (UDim2, f32)| { let this_x = Vec2::new(this.x.scale, this.x.offset as f32); - let goal_x = Vec2::new(goal.x.scale, this.x.offset as f32); + let goal_x = Vec2::new(goal.x.scale, goal.x.offset as f32); let this_y = Vec2::new(this.y.scale, this.y.offset as f32); let goal_y = Vec2::new(goal.y.scale, goal.y.offset as f32); diff --git a/packages/lib-roblox/src/lib.rs b/packages/lib-roblox/src/lib.rs index d73763b..9387669 100644 --- a/packages/lib-roblox/src/lib.rs +++ b/packages/lib-roblox/src/lib.rs @@ -19,20 +19,23 @@ fn make_all_datatypes(lua: &Lua) -> LuaResult> { use datatypes::types::*; Ok(vec![ // Classes - ("Axes", make_dt(lua, Axes::make_table)?), - ("BrickColor", make_dt(lua, BrickColor::make_table)?), - ("CFrame", make_dt(lua, CFrame::make_table)?), - ("Color3", make_dt(lua, Color3::make_table)?), - ("ColorSequence", make_dt(lua, ColorSequence::make_table)?), - ("ColorSequenceKeypoint", make_dt(lua, ColorSequenceKeypoint::make_table)?), - ("Faces", make_dt(lua, Faces::make_table)?), - ("Rect", make_dt(lua, Rect::make_table)?), - ("UDim", make_dt(lua, UDim::make_table)?), - ("UDim2", make_dt(lua, UDim2::make_table)?), - ("Vector2", make_dt(lua, Vector2::make_table)?), - ("Vector2int16", make_dt(lua, Vector2int16::make_table)?), - ("Vector3", make_dt(lua, Vector3::make_table)?), - ("Vector3int16", make_dt(lua, Vector3int16::make_table)?), + ("Axes", make_dt(lua, Axes::make_table)?), + ("BrickColor", make_dt(lua, BrickColor::make_table)?), + ("CFrame", make_dt(lua, CFrame::make_table)?), + ("Color3", make_dt(lua, Color3::make_table)?), + ("ColorSequence", make_dt(lua, ColorSequence::make_table)?), + ("ColorSequenceKeypoint", make_dt(lua, ColorSequenceKeypoint::make_table)?), + ("Faces", make_dt(lua, Faces::make_table)?), + ("NumberRange", make_dt(lua, NumberRange::make_table)?), + ("NumberSequence", make_dt(lua, NumberSequence::make_table)?), + ("NumberSequenceKeypoint", make_dt(lua, NumberSequenceKeypoint::make_table)?), + ("Rect", make_dt(lua, Rect::make_table)?), + ("UDim", make_dt(lua, UDim::make_table)?), + ("UDim2", make_dt(lua, UDim2::make_table)?), + ("Vector2", make_dt(lua, Vector2::make_table)?), + ("Vector2int16", make_dt(lua, Vector2int16::make_table)?), + ("Vector3", make_dt(lua, Vector3::make_table)?), + ("Vector3int16", make_dt(lua, Vector3int16::make_table)?), // Singletons ("Enum", LuaValue::UserData(Enums::make_singleton(lua)?)), ]) diff --git a/tests/roblox/datatypes/NumberRange.luau b/tests/roblox/datatypes/NumberRange.luau new file mode 100644 index 0000000..455d627 --- /dev/null +++ b/tests/roblox/datatypes/NumberRange.luau @@ -0,0 +1,32 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local NumberRange = _G.NumberRange + +-- Constructors & properties + +NumberRange.new(0) +NumberRange.new(0, 0) + +assert(not pcall(function() + return NumberRange.new() +end)) +assert(not pcall(function() + return NumberRange.new(false) +end)) +assert(not pcall(function() + return NumberRange.new("", "") +end)) +assert(not pcall(function() + return NumberRange.new(newproxy(true)) +end)) + +assert(NumberRange.new(0, 1).Min == 0) +assert(NumberRange.new(1, 1).Min == 1) + +assert(NumberRange.new(0, 0).Max == 0) +assert(NumberRange.new(0, 1).Max == 1) + +-- Swapped args should still set proper min/max + +assert(NumberRange.new(1, 0).Min == 0) +assert(NumberRange.new(1, 0).Max == 1) diff --git a/tests/roblox/datatypes/NumberSequence.luau b/tests/roblox/datatypes/NumberSequence.luau new file mode 100644 index 0000000..d8ec067 --- /dev/null +++ b/tests/roblox/datatypes/NumberSequence.luau @@ -0,0 +1,32 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local NumberSequence = _G.NumberSequence +local NumberSequenceKeypoint = _G.NumberSequenceKeypoint + +-- Constructors & properties + +NumberSequence.new(0) +NumberSequence.new(0, 0) +local sequence = NumberSequence.new({ + NumberSequenceKeypoint.new(0, 1), + NumberSequenceKeypoint.new(0.5, 0.5), + NumberSequenceKeypoint.new(1, 0), +}) + +assert(not pcall(function() + return NumberSequence.new() +end)) +assert(not pcall(function() + return NumberSequence.new(false) +end)) +assert(not pcall(function() + return NumberSequence.new("", "") +end)) +assert(not pcall(function() + return NumberSequence.new(newproxy(true)) +end)) + +assert(#sequence.Keypoints == 3) +assert(sequence.Keypoints[1] == NumberSequenceKeypoint.new(0, 1)) +assert(sequence.Keypoints[2] == NumberSequenceKeypoint.new(0.5, 0.5)) +assert(sequence.Keypoints[3] == NumberSequenceKeypoint.new(1, 0)) diff --git a/tests/roblox/datatypes/NumberSequenceKeypoint.luau b/tests/roblox/datatypes/NumberSequenceKeypoint.luau new file mode 100644 index 0000000..ebbdf63 --- /dev/null +++ b/tests/roblox/datatypes/NumberSequenceKeypoint.luau @@ -0,0 +1,29 @@ +-- HACK: Make luau happy, with the mlua rust +-- crate all globals are also present in _G +local NumberSequenceKeypoint = _G.NumberSequenceKeypoint + +-- Constructors & properties + +NumberSequenceKeypoint.new(0, 0) +NumberSequenceKeypoint.new(1, 0) +NumberSequenceKeypoint.new(0.5, 0.5, 0.5) + +assert(not pcall(function() + return NumberSequenceKeypoint.new() +end)) +assert(not pcall(function() + return NumberSequenceKeypoint.new(false) +end)) +assert(not pcall(function() + return NumberSequenceKeypoint.new("", "") +end)) +assert(not pcall(function() + return NumberSequenceKeypoint.new(newproxy(true)) +end)) + +assert(NumberSequenceKeypoint.new(0, 0, 0).Time == 0) +assert(NumberSequenceKeypoint.new(1, 0, 1).Time == 1) +assert(NumberSequenceKeypoint.new(0, 0, 0).Value == 0) +assert(NumberSequenceKeypoint.new(1, 1, 1).Value == 1) +assert(NumberSequenceKeypoint.new(0, 0, 0).Envelope == 0) +assert(NumberSequenceKeypoint.new(1, 1, 1).Envelope == 1) diff --git a/tests/roblox/datatypes/UDim2.luau b/tests/roblox/datatypes/UDim2.luau index e30ada8..eb69c90 100644 --- a/tests/roblox/datatypes/UDim2.luau +++ b/tests/roblox/datatypes/UDim2.luau @@ -47,6 +47,6 @@ assert(UDim2.new(2, 4, 6, 8) - UDim2.new(1, 1, 1, 1) == UDim2.new(1, 3, 5, 7)) -- Methods -assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.0) == UDim2.new(2, 3, 6, 8)) +assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.0) == UDim2.new(2, 4, 6, 8)) assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.5) == UDim2.new(1.5, 3, 4.5, 6)) assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 1.0) == UDim2.new(1, 2, 3, 4))