using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using MCPForUnity.Editor.Helpers; using MCPForUnity.Runtime.Helpers; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEngine; namespace MCPForUnity.Editor.Tools.Cameras { internal static class CameraHelpers { private static bool? _hasCinemachine; private static Type _cmCameraType; private static Type _cmBrainType; internal static bool HasCinemachine { get { if (_hasCinemachine == null) DetectCinemachine(); return _hasCinemachine.Value; } } internal static Type CinemachineCameraType { get { if (_hasCinemachine == null) DetectCinemachine(); return _cmCameraType; } } internal static Type CinemachineBrainType { get { if (_hasCinemachine == null) DetectCinemachine(); return _cmBrainType; } } private static void DetectCinemachine() { _cmCameraType = UnityTypeResolver.ResolveComponent("CinemachineCamera"); _cmBrainType = UnityTypeResolver.ResolveComponent("CinemachineBrain"); _hasCinemachine = _cmCameraType != null && _cmBrainType != null; } internal static string GetCinemachineVersion() { if (!HasCinemachine || _cmCameraType == null) return null; try { var assembly = _cmCameraType.Assembly; var version = assembly.GetName().Version; return version?.ToString(); } catch { return "unknown"; } } internal static GameObject FindTargetGameObject(JObject @params) { var targetToken = @params["target"]; if (targetToken == null) return null; string searchMethod = ParamCoercion.CoerceString( @params["searchMethod"] ?? @params["search_method"], "by_name"); if (targetToken.Type == JTokenType.Integer) { int instanceId = targetToken.Value(); return GameObjectLookup.FindById(instanceId); } string targetStr = targetToken.ToString(); if (int.TryParse(targetStr, out int parsedId)) { var byId = GameObjectLookup.FindById(parsedId); if (byId != null) return byId; } return GameObjectLookup.FindByTarget(targetToken, searchMethod, true); } internal static GameObject ResolveGameObjectRef(object reference) { if (reference == null) return null; if (reference is JToken jt) { if (jt.Type == JTokenType.Integer) return GameObjectLookup.FindById(jt.Value()); if (jt.Type == JTokenType.String) { string str = jt.ToString(); if (int.TryParse(str, out int id)) { var byId = GameObjectLookup.FindById(id); if (byId != null) return byId; } return GameObjectLookup.FindByTarget(jt, "by_name", true); } } if (reference is string s) { if (int.TryParse(s, out int id)) { var byId = GameObjectLookup.FindById(id); if (byId != null) return byId; } var ids = GameObjectLookup.SearchGameObjects( GameObjectLookup.SearchMethod.ByName, s, includeInactive: true, maxResults: 1); return ids.Count > 0 ? GameObjectLookup.FindById(ids[0]) : null; } return null; } internal static Component FindCinemachineCamera(JObject @params) { if (!HasCinemachine) return null; var go = FindTargetGameObject(@params); return go != null ? go.GetComponent(CinemachineCameraType) : null; } internal static Component FindBrain() { if (!HasCinemachine || _cmBrainType == null) return null; return UnityFindObjectsCompat.FindAny(_cmBrainType) as Component; } internal static UnityEngine.Camera FindMainCamera() { var main = UnityEngine.Camera.main; if (main != null) return main; var allCams = UnityFindObjectsCompat.FindAll(); return allCams.Length > 0 ? allCams[0] : null; } internal static JObject ExtractProperties(JObject @params) { var props = @params["properties"] as JObject; if (props != null) return props; var propsStr = ParamCoercion.CoerceString(@params["properties"], null); if (propsStr != null) { try { return JObject.Parse(propsStr); } catch { return null; } } return null; } internal static object GetReflectionProperty(Component component, string propertyName) { if (component == null) return null; var type = component.GetType(); var prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); return prop?.GetValue(component); } /// Read priority int from a CinemachineCamera component via SerializedObject. internal static int ReadCinemachinePriority(Component cmCamera) { if (cmCamera == null) return 0; using var so = new SerializedObject(cmCamera); var priorityProp = so.FindProperty("Priority"); if (priorityProp == null) return 0; var enabledProp = priorityProp.FindPropertyRelative("Enabled"); var valueProp = priorityProp.FindPropertyRelative("m_Value"); if (enabledProp != null && !enabledProp.boolValue) return 0; return valueProp?.intValue ?? 0; } internal static bool SetReflectionProperty(Component component, string propertyName, object value) { if (component == null) return false; var type = component.GetType(); var prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); if (prop == null || !prop.CanWrite) return false; prop.SetValue(component, value); return true; } internal static void SetTransformTarget(Component cmCamera, string propertyName, JToken targetRef) { if (cmCamera == null) return; if (targetRef == null || targetRef.Type == JTokenType.Null) { SetReflectionProperty(cmCamera, propertyName, null); return; } var go = ResolveGameObjectRef(targetRef); if (go != null) SetReflectionProperty(cmCamera, propertyName, go.transform); } internal static Type ResolveComponentType(string typeName) { return UnityTypeResolver.ResolveComponent(typeName); } internal static Component GetPipelineComponent(Component cmCamera, string stageName) { if (cmCamera == null) return null; var type = cmCamera.GetType(); // CinemachineCamera.GetCinemachineComponent(CinemachineCore.Stage stage) var stageEnumType = type.Assembly.GetType("Unity.Cinemachine.CinemachineCore+Stage") ?? type.Assembly.GetType("Unity.Cinemachine.CinemachineCore")?.GetNestedType("Stage"); if (stageEnumType == null) return null; object stageEnum; try { stageEnum = Enum.Parse(stageEnumType, stageName, true); } catch { return null; } var method = type.GetMethod("GetCinemachineComponent", BindingFlags.Public | BindingFlags.Instance, null, new[] { stageEnumType }, null); if (method == null) return null; return method.Invoke(cmCamera, new[] { stageEnum }) as Component; } internal static string GetFallbackSuggestion(string action) { return action switch { "set_body" or "set_aim" => "Use 'set_lens' and 'set_target' for basic camera configuration.", "set_blend" => "Without Cinemachine, switch cameras by enabling/disabling Camera components.", "set_noise" => "Camera shake without Cinemachine requires a custom script.", "ensure_brain" => "CinemachineBrain requires the Cinemachine package. Basic Camera does not need a Brain.", "get_brain_status" => "No CinemachineBrain available. Cinemachine package not installed.", _ => "Install Cinemachine via Window > Package Manager." }; } internal static void MarkDirty(GameObject go) { if (go == null) return; EditorUtility.SetDirty(go); var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); if (prefabStage != null) UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(prefabStage.scene); else UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(go.scene); } } }