using System; using System.Collections.Generic; using System.Linq; namespace Roblox { /// /// Describes an object in Roblox's Parent->Child hierarchy. /// Instances can have sets of properties loaded from *.rbxl/*.rbxm files. /// public class Instance { public string ClassName = ""; public List Properties = new List(); private List Children = new List(); private Instance rawParent; public string Name => ReadProperty("Name", ClassName); public override string ToString() => Name; /// /// Returns true if this Instance is an ancestor to the provided Instance. /// /// The instance whose descendance will be tested against this Instance. public bool IsAncestorOf(Instance descendant) { while (descendant != null) { if (descendant == this) return true; descendant = descendant.Parent; } return false; } /// /// Returns true if this Instance is a descendant of the provided Instance. /// /// The instance whose ancestry will be tested against this Instance. public bool IsDescendantOf(Instance ancestor) { return ancestor.IsAncestorOf(this); } /// /// The parent of this Instance, or null if the instance is the root of a tree. /// Setting the value of this property will throw an exception if: /// - The value is set to itself. /// - The value is a descendant of the Instance. /// public Instance Parent { get { return rawParent; } set { if (IsAncestorOf(value)) throw new Exception("Parent would result in circular reference."); if (Parent == this) throw new Exception("Attempt to set parent to self"); if (rawParent != null) rawParent.Children.Remove(this); value.Children.Add(this); rawParent = value; } } public IEnumerable GetChildren() { var current = Children.ToArray(); return current.AsEnumerable(); } /// /// Returns the first Instance whose Name is the provided string name. If the instance is not found, this returns null. /// /// The name of the instance to find. /// The instance that was found with this name, or null. public Instance FindFirstChild(string name) { Instance result = null; var query = Children.Where(child => child.Name == name); if (query.Count() > 0) result = query.First(); return result; } /// /// Looks for a property with the specified property name, and returns it as an object. /// The resulting value may be null if the property is not serialized. /// You can use the templated ReadProperty overload to fetch it as a specific type with a default value provided. /// /// The name of the property to be fetched from this Instance. /// An object reference to the value of the specified property, if it exists. /// public object ReadProperty(string propertyName) { Property property = null; var query = Properties.Where((prop) => prop.Name.ToLower() == propertyName.ToLower()); if (query.Count() > 0) property = query.First(); return (property != null ? property.Value : null); } /// /// Looks for a property with the specified property name, and returns it as the specified type. /// If it cannot be converted, the provided nullFallback value will be returned instead. /// /// The value type to convert to when finding the specified property name. /// The name of the property to be fetched from this Instance. /// A fallback value to be returned if casting to T fails, or the property is not found. /// public T ReadProperty(string propertyName, T nullFallback) { try { object result = ReadProperty(propertyName); return (T)result; } catch (Exception e) { return nullFallback; } } /// /// Looks for a property with the specified property name. If found, it will try to set the value of the referenced outValue to its value. /// Returns true if the property was found and its value was casted to the referenced outValue. If it returns false, the outValue has not been set. /// /// The value type to convert to when finding the specified property name. /// The name of the property to be fetched from this Instance. /// The value to write to if the property can be casted to T correctly. public bool TryReadProperty(string propertyName, ref T outValue) { try { object result = ReadProperty(propertyName); outValue = (T)result; return true; } catch { return false; } } /// /// Adds a property by reference to this Instance's property list. /// This is used during the file loading procedure. /// /// A reference to the property that will be added. public void AddProperty(ref Property prop) { Properties.Add(prop); } } }