From bcfc7d2f5518fb959cf51f1dbdc8fa019884e7ab Mon Sep 17 00:00:00 2001 From: Kenneth Loeffler Date: Thu, 24 Aug 2023 16:39:13 -0700 Subject: [PATCH] Implement Terrain:GetMaterialColor and Terrain:SetMaterialColor (#93) --- src/roblox/datatypes/types/color3.rs | 26 +++-- src/roblox/datatypes/types/enum_item.rs | 14 +++ src/roblox/instance/mod.rs | 2 + src/roblox/instance/terrain.rs | 127 +++++++++++++++++++++ src/roblox/shared/classes.rs | 1 - src/tests.rs | 1 + tests/roblox/instance/classes/Terrain.luau | 13 +++ 7 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 src/roblox/instance/terrain.rs create mode 100644 tests/roblox/instance/classes/Terrain.luau diff --git a/src/roblox/datatypes/types/color3.rs b/src/roblox/datatypes/types/color3.rs index 8026875..33eba6d 100644 --- a/src/roblox/datatypes/types/color3.rs +++ b/src/roblox/datatypes/types/color3.rs @@ -108,6 +108,20 @@ impl LuaExportsTable<'_> for Color3 { } } +impl<'lua> FromLua<'lua> for Color3 { + fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { + if let LuaValue::UserData(ud) = value { + Ok(*ud.borrow::()?) + } else { + Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "Color3", + message: None, + }) + } + } +} + impl LuaUserData for Color3 { fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) { fields.add_field_method_get("R", |_, this| Ok(this.r)); @@ -287,20 +301,12 @@ impl From for DomColor3 { impl From for Color3 { fn from(v: DomColor3uint8) -> Self { - Self { - r: (v.r as f32) / 255f32, - g: (v.g as f32) / 255f32, - b: (v.b as f32) / 255f32, - } + Color3::from(DomColor3::from(v)) } } impl From for DomColor3uint8 { fn from(v: Color3) -> Self { - Self { - r: v.r.clamp(u8::MIN as f32, u8::MAX as f32) as u8, - g: v.g.clamp(u8::MIN as f32, u8::MAX as f32) as u8, - b: v.b.clamp(u8::MIN as f32, u8::MAX as f32) as u8, - } + DomColor3uint8::from(DomColor3::from(v)) } } diff --git a/src/roblox/datatypes/types/enum_item.rs b/src/roblox/datatypes/types/enum_item.rs index 1eb1c49..8a3633c 100644 --- a/src/roblox/datatypes/types/enum_item.rs +++ b/src/roblox/datatypes/types/enum_item.rs @@ -74,6 +74,20 @@ impl LuaUserData for EnumItem { } } +impl<'lua> FromLua<'lua> for EnumItem { + fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { + if let LuaValue::UserData(ud) = value { + Ok(ud.borrow::()?.to_owned()) + } else { + Err(LuaError::FromLuaConversionError { + from: value.type_name(), + to: "EnumItem", + message: None, + }) + } + } +} + impl fmt::Display for EnumItem { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}.{}", self.parent, self.name) diff --git a/src/roblox/instance/mod.rs b/src/roblox/instance/mod.rs index 1640b1f..2b124f8 100644 --- a/src/roblox/instance/mod.rs +++ b/src/roblox/instance/mod.rs @@ -20,6 +20,7 @@ use crate::{ pub(crate) mod base; pub(crate) mod data_model; +pub(crate) mod terrain; pub(crate) mod workspace; const PROPERTY_NAME_ATTRIBUTES: &str = "Attributes"; @@ -729,6 +730,7 @@ impl LuaUserData for Instance { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { base::add_methods(methods); data_model::add_methods(methods); + terrain::add_methods(methods); } } diff --git a/src/roblox/instance/terrain.rs b/src/roblox/instance/terrain.rs new file mode 100644 index 0000000..2c52acf --- /dev/null +++ b/src/roblox/instance/terrain.rs @@ -0,0 +1,127 @@ +use mlua::prelude::*; +use rbx_dom_weak::types::{MaterialColors, TerrainMaterials, Variant}; + +use crate::roblox::{ + datatypes::types::{Color3, EnumItem}, + shared::classes::{add_class_restricted_method, add_class_restricted_method_mut}, +}; + +use super::Instance; + +pub const CLASS_NAME: &str = "Terrain"; + +fn material_from_name(material_name: &str) -> Option { + match material_name { + "Grass" => Some(TerrainMaterials::Grass), + "Slate" => Some(TerrainMaterials::Slate), + "Concrete" => Some(TerrainMaterials::Concrete), + "Brick" => Some(TerrainMaterials::Brick), + "Sand" => Some(TerrainMaterials::Sand), + "WoodPlanks" => Some(TerrainMaterials::WoodPlanks), + "Rock" => Some(TerrainMaterials::Rock), + "Glacier" => Some(TerrainMaterials::Glacier), + "Snow" => Some(TerrainMaterials::Snow), + "Sandstone" => Some(TerrainMaterials::Sandstone), + "Mud" => Some(TerrainMaterials::Mud), + "Basalt" => Some(TerrainMaterials::Basalt), + "Ground" => Some(TerrainMaterials::Ground), + "CrackedLava" => Some(TerrainMaterials::CrackedLava), + "Asphalt" => Some(TerrainMaterials::Asphalt), + "Cobblestone" => Some(TerrainMaterials::Cobblestone), + "Ice" => Some(TerrainMaterials::Ice), + "LeafyGrass" => Some(TerrainMaterials::LeafyGrass), + "Salt" => Some(TerrainMaterials::Salt), + "Limestone" => Some(TerrainMaterials::Limestone), + "Pavement" => Some(TerrainMaterials::Pavement), + _ => None, + } +} + +pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(methods: &mut M) { + add_class_restricted_method( + methods, + CLASS_NAME, + "GetMaterialColor", + terrain_get_material_color, + ); + + add_class_restricted_method_mut( + methods, + CLASS_NAME, + "SetMaterialColor", + terrain_set_material_color, + ) +} + +fn get_or_create_material_colors(instance: &Instance) -> MaterialColors { + if let Some(Variant::MaterialColors(material_colors)) = instance.get_property("MaterialColors") + { + material_colors + } else { + MaterialColors::default() + } +} + +/** + Returns the color of the given terrain material. + + ### See Also + * [`GetMaterialColor`](https://create.roblox.com/docs/reference/engine/classes/Terrain#GetMaterialColor) + on the Roblox Developer Hub +*/ +fn terrain_get_material_color(_: &Lua, this: &Instance, material: EnumItem) -> LuaResult { + let material_colors = get_or_create_material_colors(this); + + if &material.parent.desc.name != "Material" { + return Err(LuaError::RuntimeError(format!( + "Expected Enum.Material, got Enum.{}", + &material.parent.desc.name + ))); + } + + if let Some(terrain_material) = material_from_name(&material.name) { + Ok(material_colors.get_color(terrain_material).into()) + } else { + Err(LuaError::RuntimeError(format!( + "{} is not a valid Terrain material", + &material.name + ))) + } +} + +/** + Sets the color of the given terrain material. + + ### See Also + * [`SetMaterialColor`](https://create.roblox.com/docs/reference/engine/classes/Terrain#SetMaterialColor) + on the Roblox Developer Hub +*/ +fn terrain_set_material_color( + _: &Lua, + this: &mut Instance, + args: (EnumItem, Color3), +) -> LuaResult<()> { + let mut material_colors = get_or_create_material_colors(this); + let material = args.0; + let color = args.1; + + if &material.parent.desc.name != "Material" { + return Err(LuaError::RuntimeError(format!( + "Expected Enum.Material, got Enum.{}", + &material.parent.desc.name + ))); + } + + let terrain_material = if let Some(terrain_material) = material_from_name(&material.name) { + terrain_material + } else { + return Err(LuaError::RuntimeError(format!( + "{} is not a valid Terrain material", + &material.name + ))); + }; + + material_colors.set_color(terrain_material, color.into()); + this.set_property("MaterialColors", Variant::MaterialColors(material_colors)); + Ok(()) +} diff --git a/src/roblox/shared/classes.rs b/src/roblox/shared/classes.rs index f34db74..90af83a 100644 --- a/src/roblox/shared/classes.rs +++ b/src/roblox/shared/classes.rs @@ -71,7 +71,6 @@ pub(crate) fn add_class_restricted_method<'lua, M: LuaUserDataMethods<'lua, Inst }); } -#[allow(dead_code)] pub(crate) fn add_class_restricted_method_mut< 'lua, M: LuaUserDataMethods<'lua, Instance>, diff --git a/src/tests.rs b/src/tests.rs index dbe5de2..0aa8975 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -146,6 +146,7 @@ create_tests! { roblox_instance_classes_data_model: "roblox/instance/classes/DataModel", roblox_instance_classes_workspace: "roblox/instance/classes/Workspace", + roblox_instance_classes_terrain: "roblox/instance/classes/Terrain", roblox_instance_methods_clear_all_children: "roblox/instance/methods/ClearAllChildren", roblox_instance_methods_clone: "roblox/instance/methods/Clone", diff --git a/tests/roblox/instance/classes/Terrain.luau b/tests/roblox/instance/classes/Terrain.luau new file mode 100644 index 0000000..3902d21 --- /dev/null +++ b/tests/roblox/instance/classes/Terrain.luau @@ -0,0 +1,13 @@ +local roblox = require("@lune/roblox") +local Instance = roblox.Instance +local Color3 = roblox.Color3 +local Enum = roblox.Enum + +local game = Instance.new("DataModel") +local workspace = game:GetService("Workspace") +local terrain = (workspace :: any).Terrain + +assert(terrain:GetMaterialColor(Enum.Material.Grass) == Color3.fromRGB(106, 127, 63)) + +terrain:SetMaterialColor(Enum.Material.Sand, Color3.new(1, 1, 1)) +assert(terrain:GetMaterialColor(Enum.Material.Sand) == Color3.new(1, 1, 1))