Editor/Embedded/ValveResourceFormat/ResourceTypes/ModelFlex/FlexRule.cs

Represents a flex rule used by the model flex system. It stores a target flex ID and an array of FlexOp instructions, evaluates those ops against provided controller values using a stack-based FlexRuleContext, and returns the resulting float.

File Access
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeter.EditorTools.Embedded.ValveResourceFormat.Utils;
using HumanoidRetargeter.EditorTools.Embedded.ValveResourceFormat.ResourceTypes.ModelFlex.FlexOps;

namespace HumanoidRetargeter.EditorTools.Embedded.ValveResourceFormat.ResourceTypes.ModelFlex;

    /// <summary>
    /// Represents a flex rule that evaluates flex operations.
    /// </summary>
    public class FlexRule
    {
        /// <summary>
        /// Gets the flex ID.
        /// </summary>
        public int FlexID { get; }
        /// <summary>
        /// Gets the flex operations for this rule.
        /// </summary>
        public FlexOp[] FlexOps { get; }
        private readonly Stack<float> stack = new();

        /// <summary>
        /// Initializes a new instance of the <see cref="FlexRule"/> class.
        /// </summary>
        public FlexRule(int flexID, FlexOp[] flexOps)
        {
            if (flexOps.Length == 0)
            {
                throw new ArgumentException("Flex ops array cannot be empty");
            }

            FlexID = flexID;
            FlexOps = flexOps;
        }

        /// <summary>
        /// Evaluates the flex rule with the given controller values.
        /// </summary>
        public float Evaluate(float[] flexControllerValues)
        {
            var context = new FlexRuleContext(stack, flexControllerValues);

            foreach (var item in FlexOps)
            {
                item.Run(context);
            }

            if (stack.Count != 1)
            {
                throw new InvalidOperationException($"FlexRule stack had {stack.Count} values after evaluation");
            }

            return context.Stack.Pop();
        }
    }