mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Implement PhysicalProperties roblox datatype
This commit is contained in:
parent
e57abbe5d9
commit
e57316341b
5 changed files with 290 additions and 14 deletions
|
@ -113,23 +113,28 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> {
|
|||
fn rbx_variant_to_lua(lua: &'lua Lua, variant: &RbxVariant) -> DatatypeConversionResult<Self> {
|
||||
use super::types::*;
|
||||
use RbxVariant as Rbx;
|
||||
use rbx_dom_weak::types as rbx;
|
||||
|
||||
// NOTE: Enum is intentionally left out here, it has a custom
|
||||
// conversion going from instance property > datatype instead,
|
||||
// check `EnumItem::from_instance_property` for specifics
|
||||
/*
|
||||
NOTES:
|
||||
|
||||
1. Enum is intentionally left out here, it has a custom
|
||||
conversion going from instance property > datatype instead,
|
||||
check `EnumItem::from_instance_property` for specifics
|
||||
|
||||
2. PhysicalProperties can only be converted if they are custom
|
||||
physical properties, since default physical properties values
|
||||
depend on what other related properties an instance might have
|
||||
|
||||
*/
|
||||
Ok(match variant.clone() {
|
||||
// Not yet implemented datatypes
|
||||
// Rbx::Font(_) => todo!(),
|
||||
// Rbx::PhysicalProperties(_) => todo!(),
|
||||
|
||||
Rbx::Axes(value) => lua.create_userdata(Axes::from(value))?,
|
||||
Rbx::Faces(value) => lua.create_userdata(Faces::from(value))?,
|
||||
|
||||
Rbx::CFrame(value) => lua.create_userdata(CFrame::from(value))?,
|
||||
Rbx::OptionalCFrame(value) => match value {
|
||||
Some(value) => lua.create_userdata(CFrame::from(value))?,
|
||||
None => lua.create_userdata(CFrame::IDENTITY)?
|
||||
},
|
||||
|
||||
Rbx::BrickColor(value) => lua.create_userdata(BrickColor::from(value))?,
|
||||
Rbx::Color3(value) => lua.create_userdata(Color3::from(value))?,
|
||||
|
@ -152,6 +157,15 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> {
|
|||
Rbx::Vector3(value) => lua.create_userdata(Vector3::from(value))?,
|
||||
Rbx::Vector3int16(value) => lua.create_userdata(Vector3int16::from(value))?,
|
||||
|
||||
Rbx::OptionalCFrame(value) => match value {
|
||||
Some(value) => lua.create_userdata(CFrame::from(value))?,
|
||||
None => lua.create_userdata(CFrame::IDENTITY)?
|
||||
},
|
||||
|
||||
Rbx::PhysicalProperties(rbx::PhysicalProperties::Custom(value)) => {
|
||||
lua.create_userdata(PhysicalProperties::from(value))?
|
||||
},
|
||||
|
||||
v => {
|
||||
return Err(DatatypeConversionError::FromRbxVariant {
|
||||
from: v.variant_name(),
|
||||
|
@ -178,10 +192,6 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> {
|
|||
RbxVariantType::Faces => convert::<Faces, rbx::Faces>,
|
||||
|
||||
RbxVariantType::CFrame => convert::<CFrame, rbx::CFrame>,
|
||||
RbxVariantType::OptionalCFrame => return match self.borrow::<CFrame>() {
|
||||
Ok(value) => Ok(RbxVariant::OptionalCFrame(Some(rbx::CFrame::from(*value)))),
|
||||
Err(e) => Err(lua_userdata_error_to_conversion_error(variant_type, e)),
|
||||
},
|
||||
|
||||
RbxVariantType::BrickColor => convert::<BrickColor, rbx::BrickColor>,
|
||||
RbxVariantType::Color3 => convert::<Color3, rbx::Color3>,
|
||||
|
@ -206,6 +216,20 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> {
|
|||
RbxVariantType::Vector3 => convert::<Vector3, rbx::Vector3>,
|
||||
RbxVariantType::Vector3int16 => convert::<Vector3int16, rbx::Vector3int16>,
|
||||
|
||||
RbxVariantType::OptionalCFrame => return match self.borrow::<CFrame>() {
|
||||
Ok(value) => Ok(RbxVariant::OptionalCFrame(Some(rbx::CFrame::from(*value)))),
|
||||
Err(e) => Err(lua_userdata_error_to_conversion_error(variant_type, e)),
|
||||
},
|
||||
|
||||
RbxVariantType::PhysicalProperties => return match self.borrow::<PhysicalProperties>() {
|
||||
Ok(value) => {
|
||||
let props = rbx::CustomPhysicalProperties::from(*value);
|
||||
let custom = rbx::PhysicalProperties::Custom(props);
|
||||
Ok(RbxVariant::PhysicalProperties(custom))
|
||||
},
|
||||
Err(e) => Err(lua_userdata_error_to_conversion_error(variant_type, e)),
|
||||
},
|
||||
|
||||
_ => return Err(DatatypeConversionError::ToRbxVariant {
|
||||
to: variant_type.variant_name(),
|
||||
from: "userdata",
|
||||
|
|
|
@ -11,6 +11,7 @@ mod faces;
|
|||
mod number_range;
|
||||
mod number_sequence;
|
||||
mod number_sequence_keypoint;
|
||||
mod physical_properties;
|
||||
mod ray;
|
||||
mod rect;
|
||||
mod region3;
|
||||
|
@ -32,6 +33,7 @@ pub use faces::Faces;
|
|||
pub use number_range::NumberRange;
|
||||
pub use number_sequence::NumberSequence;
|
||||
pub use number_sequence_keypoint::NumberSequenceKeypoint;
|
||||
pub use physical_properties::PhysicalProperties;
|
||||
pub use r#enum::Enum;
|
||||
pub use r#enum_item::EnumItem;
|
||||
pub use r#enums::Enums;
|
||||
|
@ -99,6 +101,7 @@ mod tests {
|
|||
number_range: "datatypes/NumberRange",
|
||||
number_sequence: "datatypes/NumberSequence",
|
||||
number_sequence_keypoint: "datatypes/NumberSequenceKeypoint",
|
||||
physical_properties: "datatypes/PhysicalProperties",
|
||||
ray: "datatypes/Ray",
|
||||
rect: "datatypes/Rect",
|
||||
udim: "datatypes/UDim",
|
||||
|
|
183
packages/lib-roblox/src/datatypes/types/physical_properties.rs
Normal file
183
packages/lib-roblox/src/datatypes/types/physical_properties.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use core::fmt;
|
||||
|
||||
use mlua::prelude::*;
|
||||
use rbx_dom_weak::types::CustomPhysicalProperties as RbxCustomPhysicalProperties;
|
||||
|
||||
use super::{super::*, EnumItem};
|
||||
|
||||
/**
|
||||
An implementation of the [PhysicalProperties](https://create.roblox.com/docs/reference/engine/datatypes/PhysicalProperties) Roblox datatype.
|
||||
|
||||
This implements all documented properties, methods & constructors of the PhysicalProperties class as of March 2023.
|
||||
*/
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct PhysicalProperties {
|
||||
pub(crate) density: f32,
|
||||
pub(crate) friction: f32,
|
||||
pub(crate) friction_weight: f32,
|
||||
pub(crate) elasticity: f32,
|
||||
pub(crate) elasticity_weight: f32,
|
||||
}
|
||||
|
||||
impl PhysicalProperties {
|
||||
pub(crate) fn from_material(material_enum_item: &EnumItem) -> Option<PhysicalProperties> {
|
||||
MATERIAL_ENUM_MAP
|
||||
.iter()
|
||||
.find(|props| props.0 == material_enum_item.name)
|
||||
.map(|props| PhysicalProperties {
|
||||
density: props.1,
|
||||
friction: props.2,
|
||||
elasticity: props.3,
|
||||
friction_weight: props.4,
|
||||
elasticity_weight: props.5,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
|
||||
type ArgsMaterial = EnumItem;
|
||||
type ArgsNumbers = (f32, f32, f32, Option<f32>, Option<f32>);
|
||||
datatype_table.set(
|
||||
"new",
|
||||
lua.create_function(|lua, args: LuaMultiValue| {
|
||||
if let Ok(value) = ArgsMaterial::from_lua_multi(args.clone(), lua) {
|
||||
if value.parent.desc.name == "Material" {
|
||||
match PhysicalProperties::from_material(&value) {
|
||||
Some(props) => Ok(props),
|
||||
None => Err(LuaError::RuntimeError(format!(
|
||||
"Found unknown Material '{}'",
|
||||
value.name
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(LuaError::RuntimeError(format!(
|
||||
"Expected argument #1 to be a Material, got {}",
|
||||
value.parent.desc.name
|
||||
)))
|
||||
}
|
||||
} else if let Ok((
|
||||
density,
|
||||
friction,
|
||||
elasticity,
|
||||
friction_weight,
|
||||
elasticity_weight,
|
||||
)) = ArgsNumbers::from_lua_multi(args, lua)
|
||||
{
|
||||
Ok(PhysicalProperties {
|
||||
density,
|
||||
friction,
|
||||
friction_weight: friction_weight.unwrap_or(1.0),
|
||||
elasticity,
|
||||
elasticity_weight: elasticity_weight.unwrap_or(1.0),
|
||||
})
|
||||
} else {
|
||||
// FUTURE: Better error message here using given arg types
|
||||
Err(LuaError::RuntimeError(
|
||||
"Invalid arguments to constructor".to_string(),
|
||||
))
|
||||
}
|
||||
})?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for PhysicalProperties {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("Density", |_, this| Ok(this.density));
|
||||
fields.add_field_method_get("Friction", |_, this| Ok(this.friction));
|
||||
fields.add_field_method_get("FrictionWeight", |_, this| Ok(this.friction_weight));
|
||||
fields.add_field_method_get("Elasticity", |_, this| Ok(this.elasticity));
|
||||
fields.add_field_method_get("ElasticityWeight", |_, this| Ok(this.elasticity_weight));
|
||||
}
|
||||
|
||||
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 PhysicalProperties {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}, {}, {}, {}, {}",
|
||||
self.density,
|
||||
self.friction,
|
||||
self.elasticity,
|
||||
self.friction_weight,
|
||||
self.elasticity_weight
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RbxCustomPhysicalProperties> for PhysicalProperties {
|
||||
fn from(v: RbxCustomPhysicalProperties) -> Self {
|
||||
Self {
|
||||
density: v.density,
|
||||
friction: v.friction,
|
||||
friction_weight: v.friction_weight,
|
||||
elasticity: v.elasticity,
|
||||
elasticity_weight: v.elasticity_weight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PhysicalProperties> for RbxCustomPhysicalProperties {
|
||||
fn from(v: PhysicalProperties) -> Self {
|
||||
RbxCustomPhysicalProperties {
|
||||
density: v.density,
|
||||
friction: v.friction,
|
||||
friction_weight: v.friction_weight,
|
||||
elasticity: v.elasticity,
|
||||
elasticity_weight: v.elasticity_weight,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
NOTE: The material definitions below are generated using the
|
||||
physical_properties_enum_map script in the scripts dir next
|
||||
to src, which can be ran in the Roblox Studio command bar
|
||||
|
||||
*/
|
||||
|
||||
#[rustfmt::skip]
|
||||
const MATERIAL_ENUM_MAP: &[(&str, f32, f32, f32, f32, f32)] = &[
|
||||
("Plastic", 0.70, 0.30, 0.50, 1.00, 1.00),
|
||||
("Wood", 0.35, 0.48, 0.20, 1.00, 1.00),
|
||||
("Slate", 2.69, 0.40, 0.20, 1.00, 1.00),
|
||||
("Concrete", 2.40, 0.70, 0.20, 0.30, 1.00),
|
||||
("CorrodedMetal", 7.85, 0.70, 0.20, 1.00, 1.00),
|
||||
("DiamondPlate", 7.85, 0.35, 0.25, 1.00, 1.00),
|
||||
("Foil", 2.70, 0.40, 0.25, 1.00, 1.00),
|
||||
("Grass", 0.90, 0.40, 0.10, 1.00, 1.50),
|
||||
("Ice", 0.92, 0.02, 0.15, 3.00, 1.00),
|
||||
("Marble", 2.56, 0.20, 0.17, 1.00, 1.00),
|
||||
("Granite", 2.69, 0.40, 0.20, 1.00, 1.00),
|
||||
("Brick", 1.92, 0.80, 0.15, 0.30, 1.00),
|
||||
("Pebble", 2.40, 0.40, 0.17, 1.00, 1.50),
|
||||
("Sand", 1.60, 0.50, 0.05, 5.00, 2.50),
|
||||
("Fabric", 0.70, 0.35, 0.05, 1.00, 1.00),
|
||||
("SmoothPlastic", 0.70, 0.20, 0.50, 1.00, 1.00),
|
||||
("Metal", 7.85, 0.40, 0.25, 1.00, 1.00),
|
||||
("WoodPlanks", 0.35, 0.48, 0.20, 1.00, 1.00),
|
||||
("Cobblestone", 2.69, 0.50, 0.17, 1.00, 1.00),
|
||||
("Air", 0.01, 0.01, 0.01, 1.00, 1.00),
|
||||
("Water", 1.00, 0.00, 0.01, 1.00, 1.00),
|
||||
("Rock", 2.69, 0.50, 0.17, 1.00, 1.00),
|
||||
("Glacier", 0.92, 0.05, 0.15, 2.00, 1.00),
|
||||
("Snow", 0.90, 0.30, 0.03, 3.00, 4.00),
|
||||
("Sandstone", 2.69, 0.50, 0.15, 5.00, 1.00),
|
||||
("Mud", 0.90, 0.30, 0.07, 3.00, 4.00),
|
||||
("Basalt", 2.69, 0.70, 0.15, 0.30, 1.00),
|
||||
("Ground", 0.90, 0.45, 0.10, 1.00, 1.00),
|
||||
("CrackedLava", 2.69, 0.65, 0.15, 1.00, 1.00),
|
||||
("Neon", 0.70, 0.30, 0.20, 1.00, 1.00),
|
||||
("Glass", 2.40, 0.25, 0.20, 1.00, 1.00),
|
||||
("Asphalt", 2.36, 0.80, 0.20, 0.30, 1.00),
|
||||
("LeafyGrass", 0.90, 0.40, 0.10, 2.00, 2.00),
|
||||
("Salt", 2.16, 0.50, 0.05, 1.00, 1.00),
|
||||
("Limestone", 2.69, 0.50, 0.15, 1.00, 1.00),
|
||||
("Pavement", 2.69, 0.50, 0.17, 0.30, 1.00),
|
||||
("ForceField", 2.40, 0.25, 0.20, 1.00, 1.00),
|
||||
];
|
|
@ -29,6 +29,7 @@ fn make_all_datatypes(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
|
|||
("NumberRange", make_dt(lua, NumberRange::make_table)?),
|
||||
("NumberSequence", make_dt(lua, NumberSequence::make_table)?),
|
||||
("NumberSequenceKeypoint", make_dt(lua, NumberSequenceKeypoint::make_table)?),
|
||||
("PhysicalProperties", make_dt(lua, PhysicalProperties::make_table)?),
|
||||
("Ray", make_dt(lua, Ray::make_table)?),
|
||||
("Rect", make_dt(lua, Rect::make_table)?),
|
||||
("UDim", make_dt(lua, UDim::make_table)?),
|
||||
|
|
65
tests/roblox/datatypes/PhysicalProperties.luau
Normal file
65
tests/roblox/datatypes/PhysicalProperties.luau
Normal file
|
@ -0,0 +1,65 @@
|
|||
-- HACK: Make luau happy, with the mlua rust
|
||||
-- crate all globals are also present in _G
|
||||
local PhysicalProperties = _G.PhysicalProperties
|
||||
local Enum = _G.Enum
|
||||
|
||||
-- Constructors & properties
|
||||
|
||||
PhysicalProperties.new(Enum.Material.SmoothPlastic)
|
||||
PhysicalProperties.new(0, 0, 0)
|
||||
PhysicalProperties.new(0, 0, 0, 0, 0)
|
||||
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new()
|
||||
end))
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new(false)
|
||||
end))
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new({})
|
||||
end))
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new(newproxy(true))
|
||||
end))
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new(Enum.Axis.X)
|
||||
end))
|
||||
|
||||
assert(PhysicalProperties.new(1, 2, 3).FrictionWeight == 1)
|
||||
assert(PhysicalProperties.new(1, 2, 3).ElasticityWeight == 1)
|
||||
|
||||
assert(PhysicalProperties.new(1, 2, 3, 4, 5).Density == 1)
|
||||
assert(PhysicalProperties.new(1, 2, 3, 4, 5).Friction == 2)
|
||||
assert(PhysicalProperties.new(1, 2, 3, 4, 5).Elasticity == 3)
|
||||
assert(PhysicalProperties.new(1, 2, 3, 4, 5).FrictionWeight == 4)
|
||||
assert(PhysicalProperties.new(1, 2, 3, 4, 5).ElasticityWeight == 5)
|
||||
|
||||
local function fuzzyEq(n0: number, n1: number)
|
||||
return math.abs(n1 - n0) <= 0.0001
|
||||
end
|
||||
|
||||
local plastic = PhysicalProperties.new(Enum.Material.Plastic)
|
||||
assert(fuzzyEq(plastic.Density, 0.7))
|
||||
assert(fuzzyEq(plastic.Friction, 0.3))
|
||||
assert(fuzzyEq(plastic.Elasticity, 0.5))
|
||||
|
||||
local splastic = PhysicalProperties.new(Enum.Material.SmoothPlastic)
|
||||
assert(fuzzyEq(splastic.Density, 0.7))
|
||||
assert(fuzzyEq(splastic.Friction, 0.2))
|
||||
assert(fuzzyEq(splastic.Elasticity, 0.5))
|
||||
|
||||
local sand = PhysicalProperties.new(Enum.Material.Sand)
|
||||
assert(fuzzyEq(sand.Density, 1.6))
|
||||
assert(fuzzyEq(sand.Friction, 0.5))
|
||||
assert(fuzzyEq(sand.Elasticity, 0.05))
|
||||
assert(fuzzyEq(sand.FrictionWeight, 5))
|
||||
assert(fuzzyEq(sand.ElasticityWeight, 2.5))
|
||||
|
||||
-- Ops
|
||||
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new() + PhysicalProperties.new()
|
||||
end))
|
||||
assert(not pcall(function()
|
||||
return PhysicalProperties.new() / PhysicalProperties.new()
|
||||
end))
|
Loading…
Reference in a new issue