using UnityEngine; using UnityEditor; using System.Collections.Generic; using UnityEditor.SceneManagement; #if NET_LEGACY using System.Linq; #endif // Sci-Fi Ship Controller. Copyright (c) 2018-2021 SCSM Pty Ltd. All rights reserved. namespace SciFiShipController { [CustomEditor(typeof(AutoTargetingModule))] public class AutoTargetingModuleEditor : Editor { #region Custom Editor private variables private AutoTargetingModule autoTargetingModule; private bool isSceneModified = false; private bool isVerifyRequired = false; private bool isDebuggingEnabled = false; private bool isDebugShowQueryResults = false; // formatting and style variables private GUIStyle labelFieldRichText; private GUIStyle helpBoxRichText; private GUIStyle buttonCompact; private GUIStyle foldoutStyleNoLabel; private static GUIStyle toggleCompactButtonStyleNormal = null; // Small Toggle button. e.g. G(izmo) on/off private static GUIStyle toggleCompactButtonStyleToggled = null; private float defaultEditorLabelWidth = 0f; private float defaultEditorFieldWidth = 0f; #endregion #region GUIContent private readonly static GUIContent autoTargetingHeaderContent = new GUIContent("The Auto Targeting Module can be attached to a ShipControlModule or a Surface Turret Module. It works with the Radar system to automatically allocate targets within range of weapons."); private readonly static GUIContent initialiseOnStartContent = new GUIContent("Initialise On Start", "If enabled, the Initialise() will be called as soon as Start() runs. This should be disabled if you are initialising the autotargeting module through code and using the AutoTargetingModule API methods."); private readonly static GUIContent moduleModeContent = new GUIContent("Module Mode", "The mode that the AutoTargetingModule will operate in. This should match the module it is attached to."); private readonly static GUIContent isCheckLOSNewTargetContent = new GUIContent("Check LoS (New Target)", "When acquiring a new target, when enabled, this will verify there is a direct Line of Sight between the weapon and the target."); private readonly static GUIContent shipDisplayModuleContent = new GUIContent("Ship Display Module", "[OPTIONAL] If configured, targeting information can be set to this heads-up display in the scene"); private readonly static GUIContent isTargetsShownOnHUDContent = new GUIContent("Show Targets on HUD", "Show the Targets on the heads-up display"); private readonly static GUIContent updateTargetPeriodicallyContent = new GUIContent("Update Target Periodically", "Whether the target should be reassigned periodically (after a fixed period of time)."); private readonly static GUIContent updateTargetTimeContent = new GUIContent("Update Target Time", "The time to wait (in seconds) before assigning a new target."); private readonly static GUIContent canLoseTargetContent = new GUIContent("Can Lose Target", "Whether the current target can be 'lost' through either loss of line of sight or (for a turret) an inability to lock on to the target."); private readonly static GUIContent targetLostTimeContent = new GUIContent("Target Lost Time", "How long (in seconds) a target must be invalid for it to be lost (prompting a new target to be assigned)."); private readonly static GUIContent isValidTargetRequireLOSContent = new GUIContent("Require LoS", "Whether a target can be 'lost' if line-of-sight is lost."); private readonly static GUIContent isValidTargetRequireTargetLockContent = new GUIContent("Require Target Lock", "Whether a target can be 'lost' if the turret is unable to lock on to it."); #endregion #region GUIContent - Debug private readonly static GUIContent debugModeContent = new GUIContent("Debug Mode", "Use this to troubleshoot the data at runtime in the editor."); private readonly static GUIContent debugIsInitialisedContent = new GUIContent("Is Initialised?"); private readonly static GUIContent debugIsModuleModeValidContent = new GUIContent("Is Module Mode Valid?"); private readonly static GUIContent debugIsTargetsShownOnHUDContent = new GUIContent("Targets Shown on HUD?"); private readonly static GUIContent debugNumberOfTargetsContent = new GUIContent("Targets in Range"); private readonly static GUIContent debugRadarQueryContent = new GUIContent("Current Radar Query"); private readonly static GUIContent debugNoRadarQueryContent = new GUIContent(" None"); private readonly static GUIContent debugRadarQueryFactionsToIncludeContent = new GUIContent(" Factions to Include"); private readonly static GUIContent debugRadarQueryFactionsToExcludeContent = new GUIContent(" Factions to Exclude"); private readonly static GUIContent debugRadarQuerySquadronsToIncludeContent = new GUIContent(" Squadrons to Include"); private readonly static GUIContent debugRadarQuerySquadronsToExcludeContent = new GUIContent(" Squadrons to Exclude"); private readonly static GUIContent debugRadarQueryRangeContent = new GUIContent(" Max Range"); private readonly static GUIContent debugRadarNoContent = new GUIContent("---"); private readonly static GUIContent debugRadarNoResultsContent = new GUIContent("Query returned no results"); private readonly static GUIContent debugRadarShowQueryResultsContent = new GUIContent("Show Results (Expensive)", "Show the first 10 radar blips from the current query"); #endregion #region Serialized Properties private SerializedProperty moduleModeProp; private SerializedProperty shipDisplayModuleProp; private SerializedProperty isTargetsShownOnHUDProp; private SerializedProperty updateTargetPeriodicallyProp; private SerializedProperty canLoseTargetProp; #endregion #region Events public void OnEnable() { autoTargetingModule = (AutoTargetingModule)target; #region Initialise properties moduleModeProp = serializedObject.FindProperty("moduleMode"); shipDisplayModuleProp = serializedObject.FindProperty("shipDisplayModule"); isTargetsShownOnHUDProp = serializedObject.FindProperty("isTargetsShownOnHUD"); updateTargetPeriodicallyProp = serializedObject.FindProperty("updateTargetPeriodically"); canLoseTargetProp = serializedObject.FindProperty("canLoseTarget"); #endregion defaultEditorLabelWidth = 185f; // EditorGUIUtility.labelWidth; defaultEditorFieldWidth = EditorGUIUtility.fieldWidth; // Reset GUIStyles helpBoxRichText = null; labelFieldRichText = null; buttonCompact = null; toggleCompactButtonStyleNormal = null; toggleCompactButtonStyleToggled = null; foldoutStyleNoLabel = null; if (autoTargetingModule != null) { autoTargetingModule.VerifyModule(); } } public void OnDestroy() { // Always unhide Unity tools when losing focus on this gameObject Tools.hidden = false; } /// /// Gets called automatically 10 times per second /// private void OnInspectorUpdate() { // OnInspectorGUI() only registers events when the mouse is positioned over the custom editor window // This code forces OnInspectorGUI() to run every frame, so it registers events even when the mouse // is positioned over the scene view if (autoTargetingModule.allowRepaint) { Repaint(); } } #endregion #region OnInspectorGUI // This function overides what is normally seen in the inspector window // This allows stuff like buttons to be drawn there public override void OnInspectorGUI() { if (autoTargetingModule == null) { return; } #region Initialise autoTargetingModule.allowRepaint = false; isSceneModified = false; EditorGUIUtility.labelWidth = defaultEditorLabelWidth; EditorGUIUtility.fieldWidth = defaultEditorFieldWidth; #endregion #region Configure Buttons and Styles if (helpBoxRichText == null) { helpBoxRichText = new GUIStyle("HelpBox"); helpBoxRichText.richText = true; } if (buttonCompact == null) { buttonCompact = new GUIStyle("Button"); buttonCompact.fontSize = 10; } if (labelFieldRichText == null) { labelFieldRichText = new GUIStyle("Label"); labelFieldRichText.richText = true; } // Set up the toggle buttons styles if (toggleCompactButtonStyleNormal == null) { // Create a new button or else will effect the Button style for other buttons too toggleCompactButtonStyleNormal = new GUIStyle("Button"); toggleCompactButtonStyleToggled = new GUIStyle(toggleCompactButtonStyleNormal); toggleCompactButtonStyleNormal.fontStyle = FontStyle.Normal; toggleCompactButtonStyleToggled.fontStyle = FontStyle.Bold; toggleCompactButtonStyleToggled.normal.background = toggleCompactButtonStyleToggled.active.background; } if (foldoutStyleNoLabel == null) { // When using a no-label foldout, don't forget to set the global // EditorGUIUtility.fieldWidth to a small value like 15, then back // to the original afterward. foldoutStyleNoLabel = new GUIStyle(EditorStyles.foldout); foldoutStyleNoLabel.fixedWidth = 0.01f; } #endregion //DrawDefaultInspector(); #region Header Info GUILayout.BeginVertical("HelpBox"); EditorGUILayout.LabelField("Sci-Fi Ship Controller Version " + ShipControlModule.SSCVersion + " " + ShipControlModule.SSCBetaVersion, labelFieldRichText); GUILayout.EndVertical(); EditorGUILayout.LabelField(autoTargetingHeaderContent, helpBoxRichText); #endregion serializedObject.Update(); EditorGUILayout.BeginVertical(EditorStyles.helpBox); #region General Settings EditorGUILayout.PropertyField(serializedObject.FindProperty("initialiseOnStart"), initialiseOnStartContent); if (!autoTargetingModule.IsModuleModeValid) { EditorGUILayout.HelpBox("The Module Mode needs to match the type of Module it is attached to.", MessageType.Error); } EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(moduleModeProp, moduleModeContent); if (EditorGUI.EndChangeCheck()) { isVerifyRequired = true; } #endregion #region Surface Turret Module Mode if (moduleModeProp.intValue == (int)AutoTargetingModule.ModuleMode.SurfaceTurretModule) { } #endregion #region Ship Control Module Mode else if (moduleModeProp.intValue == (int)AutoTargetingModule.ModuleMode.ShipControlModule) { #region HUD EditorGUILayout.PropertyField(shipDisplayModuleProp, shipDisplayModuleContent); if (shipDisplayModuleProp.objectReferenceValue != null) { EditorGUILayout.PropertyField(isTargetsShownOnHUDProp, isTargetsShownOnHUDContent); } #endregion } #endregion #region Line of Sight Settings EditorGUILayout.PropertyField(serializedObject.FindProperty("isCheckLineOfSightNewTarget"), isCheckLOSNewTargetContent); EditorGUILayout.PropertyField(updateTargetPeriodicallyProp, updateTargetPeriodicallyContent); if (updateTargetPeriodicallyProp.boolValue == true) { EditorGUILayout.PropertyField(serializedObject.FindProperty("updateTargetTime"), updateTargetTimeContent); } EditorGUILayout.PropertyField(canLoseTargetProp, canLoseTargetContent); if (canLoseTargetProp.boolValue == true) { EditorGUILayout.PropertyField(serializedObject.FindProperty("targetLostTime"), targetLostTimeContent); EditorGUILayout.PropertyField(serializedObject.FindProperty("isValidTargetRequireLOS"), isValidTargetRequireLOSContent); EditorGUILayout.PropertyField(serializedObject.FindProperty("isValidTargetRequireTargetLock"), isValidTargetRequireTargetLockContent); } #endregion EditorGUILayout.EndVertical(); serializedObject.ApplyModifiedProperties(); if (isVerifyRequired) { autoTargetingModule.VerifyModule(); isVerifyRequired = false; } #region Debug Mode EditorGUILayout.BeginVertical(EditorStyles.helpBox); isDebuggingEnabled = EditorGUILayout.Toggle(debugModeContent, isDebuggingEnabled); // Debug at runtime in the editor if (isDebuggingEnabled && autoTargetingModule != null) { float rightLabelWidth = 150f; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugIsInitialisedContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); EditorGUILayout.LabelField(autoTargetingModule.IsInitialised ? "Yes" : "No", GUILayout.MaxWidth(rightLabelWidth)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugIsModuleModeValidContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); EditorGUILayout.LabelField(autoTargetingModule.IsModuleModeValid ? "Yes" : "No", GUILayout.MaxWidth(rightLabelWidth)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugNumberOfTargetsContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); EditorGUILayout.LabelField(autoTargetingModule.NumberOfTargetsInRange.ToString(), GUILayout.MaxWidth(rightLabelWidth)); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugIsTargetsShownOnHUDContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); EditorGUILayout.LabelField(autoTargetingModule.IsTargetsShownOnHUD ? "Yes" : "No", GUILayout.MaxWidth(rightLabelWidth)); EditorGUILayout.EndHorizontal(); SSCRadarQuery currentQuery = autoTargetingModule.GetCurrentQuery; EditorGUILayout.LabelField(debugRadarQueryContent); if (currentQuery != null) { // Scripting runtime version .NET 3.5 Equivalent (NET_LEGACY) does not support string.Join(sep, int[]). EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugRadarQueryFactionsToIncludeContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); #if NET_LEGACY EditorGUILayout.LabelField(currentQuery.factionsToInclude == null ? "-" : string.Join(",", currentQuery.factionsToInclude.Select(x=>x.ToString()).ToArray())); #else EditorGUILayout.LabelField(currentQuery.factionsToInclude == null ? "-" : string.Join(",", currentQuery.factionsToInclude)); #endif EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugRadarQuerySquadronsToIncludeContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); #if NET_LEGACY EditorGUILayout.LabelField(currentQuery.squadronsToInclude == null ? "-" : string.Join(",", currentQuery.squadronsToInclude.Select(x=>x.ToString()).ToArray())); #else EditorGUILayout.LabelField(currentQuery.squadronsToInclude == null ? "-" : string.Join(",", currentQuery.squadronsToInclude)); #endif EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugRadarQueryFactionsToExcludeContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); #if NET_LEGACY EditorGUILayout.LabelField(currentQuery.factionsToExclude == null ? "-" : string.Join(",", currentQuery.factionsToExclude.Select(x=>x.ToString()).ToArray())); #else EditorGUILayout.LabelField(currentQuery.factionsToExclude == null ? "-" : string.Join(",", currentQuery.factionsToExclude)); #endif EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugRadarQuerySquadronsToExcludeContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); #if NET_LEGACY EditorGUILayout.LabelField(currentQuery.squadronsToExclude == null ? "-" : string.Join(",", currentQuery.squadronsToExclude.Select(x=>x.ToString()).ToArray())); #else EditorGUILayout.LabelField(currentQuery.squadronsToExclude == null ? "-" : string.Join(",", currentQuery.squadronsToExclude)); #endif EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(debugRadarQueryRangeContent, labelFieldRichText, GUILayout.Width(defaultEditorLabelWidth - 3f)); EditorGUILayout.LabelField(currentQuery.range.ToString("0.0")); EditorGUILayout.EndHorizontal(); } else { EditorGUILayout.LabelField(debugNoRadarQueryContent); } #region Query Results isDebugShowQueryResults = EditorGUILayout.Toggle(debugRadarShowQueryResultsContent, isDebugShowQueryResults); if (isDebugShowQueryResults) { EditorGUI.indentLevel++; SSCEditorHelper.PerformanceImpact(); List blips = autoTargetingModule.GetBlipList; int numBlips = blips == null ? 0 : blips.Count; if (numBlips == 0) { EditorGUILayout.LabelField(debugRadarNoResultsContent); } // Display only the first 20 blips for (int bIdx = 0; bIdx < numBlips && bIdx < 20; bIdx++) { SSCRadarBlip blip = blips[bIdx]; var radarItemType = blip.radarItemType; EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(radarItemType.ToString(), GUILayout.Width(150f)); if (radarItemType == SSCRadarItem.RadarItemType.AIShip || radarItemType == SSCRadarItem.RadarItemType.PlayerShip) { if (blip.shipControlModule == null) { EditorGUILayout.LabelField(debugRadarNoContent); } else { EditorGUILayout.LabelField(blip.shipControlModule.name); } } else if (radarItemType == SSCRadarItem.RadarItemType.GameObject) { if (blip.itemGameObject == null) { EditorGUILayout.LabelField(debugRadarNoContent); } else { EditorGUILayout.LabelField(blip.itemGameObject.name); } } else if (radarItemType == SSCRadarItem.RadarItemType.Custom) { EditorGUILayout.LabelField(debugRadarNoContent); } else if (radarItemType == SSCRadarItem.RadarItemType.Location) { // Could look up the Location with sscManager.GetLocation(blip.guidHash) but that // would require getting or creating the sscManager in the scene. EditorGUILayout.LabelField(blip.guidHash.ToString()); } else if (radarItemType == SSCRadarItem.RadarItemType.ShipDamageRegion) { EditorGUILayout.LabelField(blip.itemGameObject == null ? (blip.shipControlModule == null ? "---" : blip.shipControlModule.name) : blip.itemGameObject.name); } else { EditorGUILayout.LabelField(debugRadarNoContent); } EditorGUILayout.EndHorizontal(); } EditorGUI.indentLevel--; } #endregion } EditorGUILayout.EndVertical(); #endregion #region Mark Scene Dirty if required if (isSceneModified && !Application.isPlaying) { isSceneModified = false; EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene()); } #endregion autoTargetingModule.allowRepaint = true; } #endregion } }