public static PedalOption AddFourAxisPedalToSubMenu(string text, Action <Vector2> onUpdate, Texture2D icon = null, string topButtonText = "Up", string rightButtonText = "Right", string downButtonText = "Down", string leftButtonText = "Left") { return(CustomSubMenu.AddFourAxisPuppet(text, onUpdate, icon, false, topButtonText, rightButtonText, downButtonText, leftButtonText)); }
public override void OnApplicationStart() { /* Register settings */ Settings.RegisterConfig(); /* Load audio settings */ WorldAudio.LoadConfig(); /* Load avatar parameters */ Parameters.LoadConfig(); /* Load our custom UI elements */ UiExpansion.LoadUiObjects(); /* TODO: Consider switching to operator+ when everyone had to update the assembly unhollower */ /* The current solution might be prefereable so we are always first */ VRCAvatarManager.field_Private_Static_Action_3_Player_GameObject_VRC_AvatarDescriptor_0 += (Il2CppSystem.Action <Player, GameObject, VRC.SDKBase.VRC_AvatarDescriptor>)OnAvatarInstantiate; /* Register async, awaiting network manager */ MelonCoroutines.Start(RegisterJoinLeaveNotifier()); ExpansionKitApi.GetExpandedMenu(ExpandedMenu.QuickMenu).AddSimpleButton("Player List", PlayerList); ExpansionKitApi.GetExpandedMenu(ExpandedMenu.QuickMenu).AddSimpleButton("WorldCleanup", MainMenu); ExpansionKitApi.GetExpandedMenu(ExpandedMenu.UserQuickMenu).AddSimpleButton("Avatar Toggles", OnUserQuickMenu); /* Hook into setter for parameter properties */ unsafe { var param_prop_bool_set = (IntPtr)typeof(AvatarParameter).GetField("NativeMethodInfoPtr_Method_Public_Virtual_Final_New_set_Void_Boolean_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(param_prop_bool_set, new Action <IntPtr, bool>(Parameters.BoolPropertySetter).Method.MethodHandle.GetFunctionPointer()); Parameters._boolPropertySetterDelegate = Marshal.GetDelegateForFunctionPointer <Parameters.BoolPropertySetterDelegate>(*(IntPtr *)(void *)param_prop_bool_set); var param_prop_int_set = (IntPtr)typeof(AvatarParameter).GetField("NativeMethodInfoPtr_Method_Public_Virtual_Final_New_set_Void_Int32_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(param_prop_int_set, new Action <IntPtr, int>(Parameters.IntPropertySetter).Method.MethodHandle.GetFunctionPointer()); Parameters._intPropertySetterDelegate = Marshal.GetDelegateForFunctionPointer <Parameters.IntPropertySetterDelegate>(*(IntPtr *)(void *)param_prop_int_set); var param_prop_float_set = (IntPtr)typeof(AvatarParameter).GetField("NativeMethodInfoPtr_Method_Public_Virtual_Final_New_set_Void_Single_0", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null); MelonUtils.NativeHookAttach(param_prop_float_set, new Action <IntPtr, float>(Parameters.FloatPropertySetter).Method.MethodHandle.GetFunctionPointer()); Parameters._floatPropertySetterDelegate = Marshal.GetDelegateForFunctionPointer <Parameters.FloatPropertySetterDelegate>(*(IntPtr *)(void *)param_prop_float_set); } AMUtils.AddToModsFolder("Player Toggles", () => { /* Filter inactive avatar objects */ s_PlayerList = s_PlayerList.Where(o => o.Value).ToDictionary(o => o.Key, o => o.Value); /* Order by physical distance to camera */ var query = from player in s_PlayerList orderby Vector3.Distance(player.Value.transform.position, Camera.main.transform.position) select player; /* Only allow a max of 10 players there at once */ /* TODO: Consider adding multiple pages */ var remaining_count = 10; foreach (var entry in query) { var manager = entry.Value.GetComponentInParent <VRCAvatarManager>(); /* Ignore SDK2 & avatars w/o custom expressions */ if (!manager.HasCustomExpressions()) { continue; } var avatar_id = entry.Value.GetComponent <VRC.Core.PipelineManager>().blueprintId; var user_icon = s_Portraits[avatar_id].Get(); /* Source default expression icon */ var menu_icons = ActionMenuDriver.prop_ActionMenuDriver_0.field_Public_MenuIcons_0; var default_expression = menu_icons.defaultExpression; CustomSubMenu.AddSubMenu(entry.Key, () => { if (entry.Value == null || !entry.Value.active) { return; } var parameters = manager.GetAllAvatarParameters(); var filtered = Parameters.FilterDefaultParameters(parameters); var avatar_descriptor = manager.prop_VRCAvatarDescriptor_0; CustomSubMenu.AddToggle("Lock", filtered.Any(Parameters.IsLocked), (state) => { filtered.ForEach(state ? Parameters.Lock : Parameters.Unlock); }, icon: UiExpansion.LockClosedIcon); CustomSubMenu.AddButton("Save", () => Parameters.StoreParameters(manager), icon: UiExpansion.SaveIcon); AvatarParameter FindParameter(string name) { foreach (var parameter in parameters) { if (parameter.field_Private_String_0 == name) { return(parameter); } } return(null); } void ExpressionSubmenu(VRCExpressionsMenu expressions_menu) { if (entry.Value == null || !entry.Value.active) { return; } void FourAxisControl(VRCExpressionsMenu.Control control, Action <Vector2> callback) { CustomSubMenu.AddFourAxisPuppet( control.TruncatedName(), callback, icon: control.icon ?? default_expression, topButtonText: control.labels[0]?.TruncatedName() ?? "Up", rightButtonText: control.labels[1]?.TruncatedName() ?? "Right", downButtonText: control.labels[2]?.TruncatedName() ?? "Down", leftButtonText: control.labels[3]?.TruncatedName() ?? "Left"); } foreach (var control in expressions_menu.controls) { try { switch (control.type) { case VRCExpressionsMenu.Control.ControlType.Button: /* Note: Action Menu "Buttons" are actually Toggles */ /* that set on press and revert on release. */ /* TODO: Add proper implementation. */ case VRCExpressionsMenu.Control.ControlType.Toggle: { var param = FindParameter(control.parameter.name); var current_value = param.GetValue(); var default_value = avatar_descriptor.expressionParameters.FindParameter(control.parameter.name)?.defaultValue ?? 0f; var target_value = control.value; void SetIntFloat(bool state) => param.SetValue(state ? target_value : default_value); void SetBool(bool state) => param.SetValue(state ? 1f : 0f); CustomSubMenu.AddToggle( control.TruncatedName(), current_value == target_value, param.prop_ParameterType_0 == AvatarParameter.ParameterType.Bool ? SetBool : SetIntFloat, icon: control.icon ?? default_expression); break; } case VRCExpressionsMenu.Control.ControlType.SubMenu: { CustomSubMenu.AddSubMenu(control.TruncatedName(), () => ExpressionSubmenu(control.subMenu), icon: control.icon ?? default_expression); break; } case VRCExpressionsMenu.Control.ControlType.TwoAxisPuppet: { var horizontal = FindParameter(control.subParameters[0]?.name); var vertical = FindParameter(control.subParameters[1]?.name); FourAxisControl(control, (value) => { horizontal.SetFloatProperty(value.x); vertical.SetFloatProperty(value.y); }); break; } case VRCExpressionsMenu.Control.ControlType.FourAxisPuppet: { var up = FindParameter(control.subParameters[0]?.name); var down = FindParameter(control.subParameters[1]?.name); var left = FindParameter(control.subParameters[2]?.name); var right = FindParameter(control.subParameters[3]?.name); FourAxisControl(control, (value) => { up.SetFloatProperty(Math.Max(0, value.y)); down.SetFloatProperty(-Math.Min(0, value.y)); left.SetFloatProperty(Math.Max(0, value.x)); right.SetFloatProperty(-Math.Min(0, value.x)); }); break; } case VRCExpressionsMenu.Control.ControlType.RadialPuppet: { var param = FindParameter(control.subParameters[0]?.name); CustomSubMenu.AddRestrictedRadialPuppet(control.TruncatedName(), param.SetValue, startingValue: param.GetValue(), icon: control.icon ?? default_expression); break; } } } catch (Exception e) { MelonLogger.Error(e.StackTrace); } } } ExpressionSubmenu(avatar_descriptor.expressionsMenu); }, icon: user_icon); if (--remaining_count == 0) { break; } } }); MelonLogger.Msg(ConsoleColor.Green, "WorldCleanup ready!"); }
public override void OnApplicationStart() { m_fingersBends = new float[10]; m_fingersSpreads = new float[10]; // Patches var l_patchMethod = new Harmony.HarmonyMethod(typeof(MainClass), "VRCIM_ControllersType"); typeof(VRCInputManager).GetMethods().Where(x => x.Name.StartsWith("Method_Public_Static_Boolean_EnumNPublicSealedvaKeMoCoGaViOcViDaWaUnique_") ).ToList().ForEach(m => Harmony.Patch(m, l_patchMethod)); iconsAssetBundle = AssetBundle.LoadFromMemory_Internal(VRCBonesController.Properties.Resources.customicons, 0); iconsAssetBundle.hideFlags |= HideFlags.DontUnloadUnusedAsset; radialIcon = iconsAssetBundle.LoadAsset_Internal("Assets/Resources/Icons/sound-full.png", Il2CppType.Of <Texture2D>()).Cast <Texture2D>(); radialIcon.hideFlags |= HideFlags.DontUnloadUnusedAsset; toggleIcon = iconsAssetBundle.LoadAsset_Internal("Assets/Resources/Icons/zero.png", Il2CppType.Of <Texture2D>()).Cast <Texture2D>(); toggleIcon.hideFlags |= HideFlags.DontUnloadUnusedAsset; subMenuIcon = iconsAssetBundle.LoadAsset_Internal("Assets/Resources/Icons/file-transfer.png", Il2CppType.Of <Texture2D>()).Cast <Texture2D>(); subMenuIcon.hideFlags |= HideFlags.DontUnloadUnusedAsset; buttonIcon = iconsAssetBundle.LoadAsset_Internal("Assets/Resources/Icons/cloud-data-download.png", Il2CppType.Of <Texture2D>()).Cast <Texture2D>(); buttonIcon.hideFlags |= HideFlags.DontUnloadUnusedAsset; try { AMUtils.AddToModsFolder( "BonesController", delegate { /*CustomSubMenu.AddButton("ShowBones", delegate () * { * foreach(var plr in UnityEngine.Object.FindObjectsOfType<VRCPlayer>()) * { * var obj = plr.gameObject.GetComponent<ShowBones>(); * if (obj != null) * UnityEngine.Object.Destroy(obj); * else * plr.gameObject.AddComponent<ShowBones>().InitForPlayer(plr); * } * });*/ CustomSubMenu.AddSubMenu("Hands", delegate() { CustomSubMenu.AddSubMenu("Right", delegate() { CustomSubMenu.AddFourAxisPuppet("Control pos", ChangeRightHandPos, radialIcon); CustomSubMenu.AddFourAxisPuppet("Control forward/backwards", ChangeRightHandFB, radialIcon, false, "Forward", "", "Backwards", ""); CustomSubMenu.AddButton("Reset", delegate() { rightHandPosition = new Vector3(-120f, 390f, 65.6f); }); }, radialIcon); CustomSubMenu.AddSubMenu("Both", delegate() { CustomSubMenu.AddFourAxisPuppet("Control pos", ChangeBothHandsPos, radialIcon); CustomSubMenu.AddFourAxisPuppet("Control forward/backwards", ChangeBothHandsFB, radialIcon, false, "Forward", "", "Backwards", ""); CustomSubMenu.AddButton("Reset", delegate() { leftHandPosition = new Vector3(120f, 390f, 65.6f); rightHandPosition = new Vector3(-120f, 390f, 65.6f); }); }, radialIcon); CustomSubMenu.AddSubMenu("Left", delegate() { CustomSubMenu.AddFourAxisPuppet("Control pos", ChangeLeftHandPos, radialIcon); CustomSubMenu.AddFourAxisPuppet("Control forward/backwards", ChangeLeftHandFB, radialIcon, false, "Forward", "", "Backwards", ""); CustomSubMenu.AddButton("Reset", delegate() { leftHandPosition = new Vector3(120f, 390f, 65.6f); }); }, radialIcon); }, radialIcon); CustomSubMenu.AddSubMenu("Legs", delegate() { CustomSubMenu.AddSubMenu("Right", delegate() { CustomSubMenu.AddFourAxisPuppet("Control bends", ChangeRightLegBends, radialIcon); CustomSubMenu.AddFourAxisPuppet("Control pos", ChangeRightLegPos, radialIcon); CustomSubMenu.AddButton("Reset", delegate() { l_solver.rightLeg.IKPosition = l_solver.headPosition; }); }, radialIcon); CustomSubMenu.AddSubMenu("Both", delegate() { CustomSubMenu.AddFourAxisPuppet("Control bends", ChangeBothLegsBends, radialIcon); CustomSubMenu.AddFourAxisPuppet("Control pos", ChangeBothLegsPos, radialIcon); CustomSubMenu.AddButton("Reset", delegate() { l_solver.rightLeg.IKPosition = l_solver.headPosition; l_solver.leftLeg.IKPosition = l_solver.headPosition; }); }, radialIcon); CustomSubMenu.AddSubMenu("Left", delegate() { CustomSubMenu.AddFourAxisPuppet("Control bends", ChangeLeftLegBends, radialIcon); CustomSubMenu.AddFourAxisPuppet("Control pos", ChangeLeftLegPos, radialIcon); CustomSubMenu.AddButton("Reset", delegate() { l_solver.rightLeg.IKPosition = l_solver.headPosition; }); }, radialIcon); }, radialIcon); CustomSubMenu.AddSubMenu("Fingers", delegate() { CustomSubMenu.AddSubMenu("Right", delegate() { CustomSubMenu.AddFourAxisPuppet("Control", ChangeFingersRight, radialIcon); CustomSubMenu.AddButton("Reset", delegate() { ResetFingers(); }); }, radialIcon); CustomSubMenu.AddSubMenu("Both", delegate() { CustomSubMenu.AddFourAxisPuppet("Control", ChangeFingersBoth, radialIcon); CustomSubMenu.AddButton("Reset", delegate() { ResetFingers(); }); }, radialIcon); CustomSubMenu.AddSubMenu("Left", delegate() { CustomSubMenu.AddFourAxisPuppet("Control", ChangeFingersLeft, radialIcon); CustomSubMenu.AddButton("Reset", delegate() { ResetFingers(); }); }, radialIcon); }, radialIcon); AMAPI.AddTogglePedalToSubMenu("Enable control", false, ChangeControlState, radialIcon); //CustomSubMenu.AddFourAxisPuppet("Head", ChangeHeadRot, radialIcon); }, subMenuIcon ); }catch (Exception) { MelonLogger.Msg("Failed to load ActionMenuAPI, not installed."); } OnPreferencesSaved(); }