diff --git a/AtmosphereAutopilot/AppLauncher.cs b/AtmosphereAutopilot/AppLauncher.cs
index 0baef1d..c26d0a6 100644
--- a/AtmosphereAutopilot/AppLauncher.cs
+++ b/AtmosphereAutopilot/AppLauncher.cs
@@ -26,11 +26,13 @@ class AppLauncherWindow : GUIWindow
{
public AppLauncherWindow() :
base("", 3920049, new Rect(Screen.width - 280, 38, 230, 30))
- { }
+ {
+ hasCloseButton = draggable = false;
+ }
public void set_left(int left)
{
- window.x = left;
+ windowPosition.x = left;
}
public bool show_while_hover = false;
@@ -69,7 +71,7 @@ protected override void OnGUICustom()
{
if (show_while_hover)
{
- if (!window.Contains(Mouse.screenPos))
+ if (!windowPosition.Contains(Mouse.screenPos))
{
UnShowGUI();
show_while_hover = false;
@@ -79,13 +81,13 @@ protected override void OnGUICustom()
internal void set_x_position(float x)
{
- window.x = Math.Min(x, Screen.width - window.width);
+ windowPosition.x = Math.Min(x, Screen.width - windowPosition.width);
}
internal void set_y_position(float y)
{
- window.y = Mathf.Min(Mathf.Max(y - window.height / 2.0f, 0.0f), Screen.height - window.height);
- window.x = Screen.width - window.width - 42.0f;
+ windowPosition.y = Mathf.Min(Mathf.Max(y - windowPosition.height / 2.0f, 0.0f), Screen.height - windowPosition.height);
+ windowPosition.x = Screen.width - windowPosition.width - 42.0f;
}
}
-}
+}
\ No newline at end of file
diff --git a/AtmosphereAutopilot/AtmosphereAutopilot.cs b/AtmosphereAutopilot/AtmosphereAutopilot.cs
index e54bd1b..c86a44d 100644
--- a/AtmosphereAutopilot/AtmosphereAutopilot.cs
+++ b/AtmosphereAutopilot/AtmosphereAutopilot.cs
@@ -22,6 +22,7 @@ You should have received a copy of the GNU General Public License
using UnityEngine;
using System.Reflection;
using KSP.UI.Screens;
+using UnityEngine.Profiling;
//using ToolbarWrapper;
namespace AtmosphereAutopilot
@@ -41,6 +42,8 @@ public sealed class AtmosphereAutopilot: MonoBehaviour
// Hotkey manager
internal AutoHotkey hotkeyManager;
+
+ public AtmosphereAutopilotSerialized serialized = new AtmosphereAutopilotSerialized();
///
/// Get AtmosphereAutopilot addon class instance
@@ -172,15 +175,21 @@ void OnApplicationUnpause()
void serialize_active_modules()
{
+ AutoSerialization.Serialize(this.serialized, "AtmosphereAutopilotSerialized", KSPUtil.ApplicationRootPath + "GameData/AtmosphereAutopilot/Global_settings.txt",
+ typeof(GlobalSerializable));
+
if (ActiveVessel == null)
return;
+
foreach (var module in autopilot_module_lists[ActiveVessel].Values)
module.Serialize();
+
hotkeyManager.Serialize();
}
void OnDestroy()
{
+ //This seems to not be called reliably on shutdown, it's best to do serialization etc in sceneSwitch as well
serialize_active_modules();
AtmosphereAutopilot.Instance.BackgroundThread.Stop();
}
@@ -191,7 +200,7 @@ void sceneSwitch(GameScenes scenes)
clean_modules();
if (scenes != GameScenes.FLIGHT)
{
- mainMenuClose ();
+ mainMenuClose();
ActiveVessel = null;
AtmosphereAutopilot.Instance.BackgroundThread.Pause();
}
@@ -256,10 +265,31 @@ public Dictionary getVesselModules(Vessel v)
#region AppLauncherSection
+
+ public sealed class AtmosphereAutopilotSerialized {
+ [GlobalSerializable("compact_gui")]
+ public bool compact_gui = false;
+ }
+
+ //These values are not serialized, only deserialized, so they can be read from the file during scene changes without being overwritten.
+ //Use AtmosphereAutopilotSerialized for values that should be serialized during scene changes.
[GlobalSerializable("use_neo_gui")]
public bool use_neo_gui = false;
+ [GlobalSerializable("master_switch_key_toggles_gui")]
+ public bool master_switch_key_toggles_gui = true;
+
+ [GlobalSerializable("secondary_wing_level_snap_angle")]
+ public float secondary_wing_level_snap_angle = 45.0f;
+
+ [GlobalSerializable("scroll_wheel_number_field_increment_vertical")]
+ public float scroll_wheel_number_field_increment_vertical = 0.5f;
+
+ [GlobalSerializable("scroll_wheel_number_field_increment_horizontal")]
+ public float scroll_wheel_number_field_increment_horizontal = 0.1f;
+
+
ApplicationLauncherButton launcher_btn;
private Texture launcher_btn_textore_off = null;
@@ -288,10 +318,13 @@ internal AssetBundle prefabs
// Called when applauncher is ready for population
void onAppLauncherLoad()
{
- // deserialize use_neo_gui flag
+ // deserialize gui options
AutoSerialization.Deserialize(this, "AtmosphereAutopilot",
KSPUtil.ApplicationRootPath + "GameData/AtmosphereAutopilot/Global_settings.txt",
typeof(GlobalSerializable), null);
+ AutoSerialization.Deserialize(this.serialized, "AtmosphereAutopilotSerialized",
+ KSPUtil.ApplicationRootPath + "GameData/AtmosphereAutopilot/Global_settings.txt",
+ typeof(GlobalSerializable), null);
if (prefabs == null)
{
@@ -487,12 +520,14 @@ void OnGUI()
GUIStyles.Init();
styles_init = true;
}
+
if (ActiveVessel == null)
return;
if (!autopilot_module_lists.ContainsKey(ActiveVessel))
return;
if (!HighLogic.LoadedSceneIsFlight)
return;
+
GUIStyles.set_colors();
applauncher.OnGUI();
foreach (var pair in autopilot_module_lists[ActiveVessel])
@@ -514,13 +549,19 @@ void OnShowUI()
}
#endregion
+
+ const string lockName = "AircraftAutopilotCameraLock";
+
+ bool shouldUnlockCamera = true;
void Update()
{
- if (!HighLogic.LoadedSceneIsFlight)
- return;
- if (ActiveVessel == null)
+ if (shouldUnlockCamera) InputLockManager.RemoveControlLock(lockName);
+ else shouldUnlockCamera = true;
+
+ if (!HighLogic.LoadedSceneIsFlight || ActiveVessel == null)
return;
+
if (autopilot_module_lists.ContainsKey(ActiveVessel))
{
var module_list = autopilot_module_lists[ActiveVessel].Values.ToList();
@@ -528,6 +569,22 @@ void Update()
if (module.Active || module is TopModuleManager)
module.OnUpdate();
}
+
+ //if (Input.GetKeyDown(KeyCode.Escape)) GUI.FocusControl(null); //Doesn't work because focused text fields block all input, and there seems to be no way around that with immediate-mode GUI other than using Input.eatKeyPressOnTextFieldFocus, which would probably cause all text-field input to also be sent tothe main game
+ }
+
+ public void LockCameraControlsOnHover() {
+ if (Event.current.type == EventType.Repaint && shouldUnlockCamera && GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition)) {
+ InputLockManager.SetControlLock(ControlTypes.CAMERACONTROLS, lockName);
+ shouldUnlockCamera = false;
+ }
+ }
+
+ public void LockCameraControls() {
+ if (shouldUnlockCamera) {
+ InputLockManager.SetControlLock(ControlTypes.CAMERACONTROLS, lockName);
+ shouldUnlockCamera = false;
+ }
}
}
}
diff --git a/AtmosphereAutopilot/AtmosphereAutopilot.csproj b/AtmosphereAutopilot/AtmosphereAutopilot.csproj
index a9e1666..a4bbb53 100644
--- a/AtmosphereAutopilot/AtmosphereAutopilot.csproj
+++ b/AtmosphereAutopilot/AtmosphereAutopilot.csproj
@@ -33,6 +33,7 @@
4
bin\Release\AtmosphereAutopilot.XML
true
+ 1591
diff --git a/AtmosphereAutopilot/AutoHotkey.cs b/AtmosphereAutopilot/AutoHotkey.cs
index 8849f86..09f1a9f 100644
--- a/AtmosphereAutopilot/AutoHotkey.cs
+++ b/AtmosphereAutopilot/AutoHotkey.cs
@@ -63,7 +63,6 @@ public HotkeyField(FieldInfo field, AutoHotkeyAttr attr)
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
int new_editing_index = editing_index;
@@ -83,7 +82,6 @@ protected override void _drawGUI(int id)
}
GUILayout.EndVertical();
- GUI.DragWindow();
// now perform input binding
editing_index = new_editing_index;
@@ -117,24 +115,24 @@ protected override void _drawGUI(int id)
[GlobalSerializable("window_x")]
public float WindowLeft
{
- get { return window.xMin; }
+ get { return windowPosition.xMin; }
set
{
- float width = window.width;
- window.xMin = value;
- window.xMax = window.xMin + width;
+ float width = windowPosition.width;
+ windowPosition.xMin = value;
+ windowPosition.xMax = windowPosition.xMin + width;
}
}
[GlobalSerializable("window_y")]
public float WindowTop
{
- get { return window.yMin; }
+ get { return windowPosition.yMin; }
set
{
- float height = window.height;
- window.yMin = value;
- window.yMax = window.yMin + height;
+ float height = windowPosition.height;
+ windowPosition.yMin = value;
+ windowPosition.yMax = windowPosition.yMin + height;
}
}
diff --git a/AtmosphereAutopilot/AutoSerialization.cs b/AtmosphereAutopilot/AutoSerialization.cs
index 7236414..2182cc2 100644
--- a/AtmosphereAutopilot/AutoSerialization.cs
+++ b/AtmosphereAutopilot/AutoSerialization.cs
@@ -133,15 +133,13 @@ public static void Serialize(object obj, string node_name, string filename, Type
ConfigNode fileNode = ConfigNode.Load(filename);
if (fileNode == null)
fileNode = new ConfigNode();
- else
- fileNode.RemoveNode(node_name);
ConfigNode node = new ConfigNode(node_name);
SerializeToNode(node, obj, attribute_type);
if (OnSerialize != null)
OnSerialize(node, attribute_type);
if (node.HasData)
{
- fileNode.AddNode(node);
+ fileNode.SetNode(node.name, node, true);
fileNode.Save(filename);
}
}
diff --git a/AtmosphereAutopilot/Common.cs b/AtmosphereAutopilot/Common.cs
index 7887a50..86fbda3 100644
--- a/AtmosphereAutopilot/Common.cs
+++ b/AtmosphereAutopilot/Common.cs
@@ -62,6 +62,7 @@ public static Quaternion normalizeQuaternion(Quaternion quat)
/// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm
///
/// Rotation
+ ///
/// Transformation matrix representing rotation
public static void rotationMatrix(Quaternion q, Matrix mat)
{
diff --git a/AtmosphereAutopilot/GUI/AutoGui.cs b/AtmosphereAutopilot/GUI/AutoGui.cs
index b936496..e0c3be4 100644
--- a/AtmosphereAutopilot/GUI/AutoGui.cs
+++ b/AtmosphereAutopilot/GUI/AutoGui.cs
@@ -93,19 +93,23 @@ public abstract class GUIWindow : IWindow
string wndname;
int wnd_id;
bool gui_shown = false;
- protected Rect window;
+ protected Rect windowPosition;
+
+ protected bool hasCloseButton = true, draggable = true;
+
+ public const float WINDOW_TITLE_BAR_HEIGHT = 17.0f; //For the close button and window dragging
///
/// Create window instance.
///
/// Window header
/// Unique for Unity engine id
- /// Initial window position rectangle
- internal GUIWindow(string wndname, int wnd_id, Rect window)
+ /// Initial window position rectangle
+ internal GUIWindow(string wndname, int wnd_id, Rect windowPosition)
{
this.wndname = wndname;
this.wnd_id = wnd_id;
- this.window = window;
+ this.windowPosition = windowPosition;
}
///
@@ -127,10 +131,14 @@ public void OnGUI()
return;
// forbid windows not on screen
- window.xMin = Common.Clampf(window.xMin, 5.0f - window.width, Screen.width - 5.0f);
- window.yMin = Common.Clampf(window.yMin, 5.0f - window.height, Screen.height - 5.0f);
-
- window = GUILayout.Window(wnd_id, window, _drawGUI, wndname);
+ windowPosition.xMin = Common.Clampf(windowPosition.xMin, 5.0f - windowPosition.width, Screen.width - 5.0f);
+ windowPosition.yMin = Common.Clampf(windowPosition.yMin, 5.0f - windowPosition.height, Screen.height - 5.0f);
+
+ windowPosition = GUILayout.Window(wnd_id, windowPosition, (int windowId) => {
+ if (hasCloseButton) close_button();
+ _drawGUI(windowId);
+ if (draggable && Event.current.button == 0 /* LMB */) GUI.DragWindow(new Rect(0, 0, 9000000.0f, WINDOW_TITLE_BAR_HEIGHT));
+ }, wndname);
OnGUICustom();
}
@@ -139,7 +147,8 @@ public void OnGUI()
///
protected void close_button()
{
- Rect close_btn_rect = new Rect(window.width - 16.0f, 1.0f, 15.0f, 16.0f);
+ const float BUTTON_SIZE = WINDOW_TITLE_BAR_HEIGHT - 1.0f;
+ Rect close_btn_rect = new Rect(windowPosition.width - BUTTON_SIZE, 1.0f, BUTTON_SIZE - 1.0f, BUTTON_SIZE);
bool close = GUI.Button(close_btn_rect, "x", GUIStyles.toggleButtonStyle);
if (close)
this.UnShowGUI();
@@ -151,7 +160,7 @@ protected void close_button()
protected virtual void OnGUICustom() { }
///
- // Executed on every OnGUI regardless of gui_shown or UIHidden
+ /// Executed on every OnGUI regardless of gui_shown or UIHidden
///
protected virtual void OnGUICustomAlways() { }
@@ -231,6 +240,48 @@ public static void AutoDrawObject(object obj)
draw_element(field, obj);
}
+ public static void HandleToggleButton(bool currentToggleValue, string label, GUIStyle style, Action onLeftClick, Action onRightClick) {
+ bool newToggleValue = GUILayout.Toggle(currentToggleValue, label, style);
+ if (newToggleValue != currentToggleValue) {
+ if (Event.current.button == 1 /* RMB */) onRightClick(newToggleValue);
+ else onLeftClick(newToggleValue);
+ }
+ }
+
+ //GUILayoutUtility.GetLastRect is supposed to only work properly in repaint events, so we might just be getting lucky here, or the engine's changed but documentation hasn't
+ public static float GetNumberTextBoxScrollWheelChange() {
+ if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition)) {
+ if (Event.current.type == EventType.Repaint) AtmosphereAutopilot.Instance.LockCameraControls();
+ else if (Event.current.type == EventType.ScrollWheel) {
+ bool smallIncrement = Input.GetKey(KeyCode.RightControl), //Alt causes the mouse wheel to FoV-zoom, so we don't want to use that
+ largeIncrement = Input.GetKey(KeyCode.RightShift);
+ return (Event.current.delta.y / 3.0f * (smallIncrement ? 0.1f : largeIncrement ? 1.0f : AtmosphereAutopilot.Instance.scroll_wheel_number_field_increment_vertical)) +
+ (Event.current.delta.x / 3.0f * (smallIncrement ? 0.05f : largeIncrement ? 0.2f : AtmosphereAutopilot.Instance.scroll_wheel_number_field_increment_horizontal)); //Unity seems to not support horizontal scroll wheel :(
+ }
+ }
+ return 0;
+ }
+
+ //See GetNumberTextBoxScrollWheelChange for a potential bug with this
+ public static bool CheckForRightClick() {
+ if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition)) {
+ if (Event.current.type == EventType.Repaint) AtmosphereAutopilot.Instance.LockCameraControls();
+ else return Event.current.type == EventType.MouseDown && Event.current.button == 1;
+ }
+ return false;
+ }
+
+ //See GetNumberTextBoxScrollWheelChange for a potential bug with this
+ public static void CheckForClick(Callback onLeftClick, Callback onRightClick) {
+ if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition)) {
+ if (Event.current.type == EventType.Repaint) AtmosphereAutopilot.Instance.LockCameraControls();
+ else if (Event.current.type == EventType.MouseDown) {
+ if (Event.current.button == 0) onLeftClick();
+ else if (Event.current.button == 1) onRightClick();
+ }
+ }
+ }
+
#region FieldPropertyUniversal
diff --git a/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldFloat.cs b/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldFloat.cs
index 0d63285..7422311 100644
--- a/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldFloat.cs
+++ b/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldFloat.cs
@@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License
//
using System;
+using System.Runtime.CompilerServices;
using UnityEngine;
namespace AtmosphereAutopilot
@@ -71,6 +72,18 @@ float init_value
input_str = val.ToString(format);
format_str = format;
}
+
+ public static DelayedFieldFloat operator +(DelayedFieldFloat lhs, float rhs) {
+ if (rhs == 0) return lhs;
+
+ lhs.ParseImmediately();
+ lhs.val += rhs;
+ lhs.input_str = lhs.val.ToString(lhs.format_str);
+ return lhs;
+ }
+ public static DelayedFieldFloat operator -(DelayedFieldFloat lhs, float rhs) {
+ return lhs + (-rhs);
+ }
public static implicit operator float(DelayedFieldFloat f)
{
@@ -147,5 +160,20 @@ public static DelayedFieldFloat Parse(string str)
return new DelayedFieldFloat(new_val, format_str);
}
}
+
+ //Doesn't update the string, do that yourself after
+ private void ParseImmediately() {
+ if (changed && float.TryParse(input_str, out float v)) {
+ changed = false;
+ time = 0.0f;
+ val = v;
+ }
+ }
+
+ public void InvertValue() {
+ ParseImmediately();
+ val = -val;
+ input_str = val.ToString(format_str);
+ }
}
}
\ No newline at end of file
diff --git a/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldGeoCoordinates.cs b/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldGeoCoordinates.cs
index 50712fd..da09ad3 100644
--- a/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldGeoCoordinates.cs
+++ b/AtmosphereAutopilot/GUI/DelayedField/DelayedFieldGeoCoordinates.cs
@@ -38,6 +38,7 @@ public enum CoordFormat { Decimal, NS, EW };
///
/// Initial value
///
+ ///
public DelayedFieldGeoCoordinates(
float init_value,
string format,
diff --git a/AtmosphereAutopilot/GUI/GUIStyles.cs b/AtmosphereAutopilot/GUI/GUIStyles.cs
index fefe02a..414290f 100644
--- a/AtmosphereAutopilot/GUI/GUIStyles.cs
+++ b/AtmosphereAutopilot/GUI/GUIStyles.cs
@@ -29,8 +29,17 @@ public static class GUIStyles
public static GUIStyle labelStyleLeft { get; private set; }
public static GUIStyle labelStyleCenter { get; private set; }
public static GUIStyle labelStyleRight { get; private set; }
+ public static GUIStyle smallLabelStyleLeft { get; private set; }
+ public static GUIStyle smallLabelStyleLeftRed { get; private set; }
+ public static GUIStyle microLabelStyleLeft { get; private set; }
+ public static GUIStyle microLabelStyleLeftRed { get; private set; }
+ public static GUIStyle nanoLabelStyleRight { get; private set; }
+ public static GUIStyle nanoLabelStyleRightGreen { get; private set; }
+ public static GUIStyle nanoLabelStyleRightRed { get; private set; }
public static GUIStyle textBoxStyle { get; private set; }
+ public static GUIStyle largeTextBoxStyle { get; private set; }
public static GUIStyle toggleButtonStyle { get; private set; }
+ public static GUIStyle compactToggleButtonStyle { get; private set; }
public static GUIStyle toggleButtonStyleRight { get; private set; }
public static GUIStyle hoverLabel { get; private set; }
@@ -57,6 +66,39 @@ internal static void Init()
labelStyleCenter.fontSize = 11;
labelStyleCenter.margin = new RectOffset(2, 2, 1, 1);
labelStyleCenter.padding = new RectOffset(1, 1, 1, 1);
+
+ smallLabelStyleLeft = new GUIStyle(labelStyleLeft);
+ smallLabelStyleLeft.fontSize = 9;
+ smallLabelStyleLeft.fontStyle = FontStyle.Bold;
+ smallLabelStyleLeft.margin = new RectOffset(1, 1, 1, 1);
+ smallLabelStyleLeft.padding = new RectOffset(1, 1, 1, 1);
+ smallLabelStyleLeft.stretchHeight = true;
+ smallLabelStyleLeft.stretchWidth = false;
+
+ smallLabelStyleLeftRed = new GUIStyle(smallLabelStyleLeft);
+ smallLabelStyleLeftRed.normal.textColor = Color.red;
+
+ microLabelStyleLeft = new GUIStyle(smallLabelStyleLeft);
+ microLabelStyleLeft.fontSize = 7;
+ microLabelStyleLeft.fontStyle = FontStyle.Normal;
+
+ microLabelStyleLeftRed = new GUIStyle(microLabelStyleLeft);
+ microLabelStyleLeftRed.normal.textColor = Color.red;
+
+ nanoLabelStyleRight = new GUIStyle(microLabelStyleLeft);
+ nanoLabelStyleRight.alignment = TextAnchor.MiddleRight;
+ nanoLabelStyleRight.fontSize = 6;
+ nanoLabelStyleRight.normal.textColor = Color.white;
+ nanoLabelStyleRight.margin = new RectOffset(1, 1, 1, 1);
+ nanoLabelStyleRight.padding = new RectOffset(1, 1, 1, 1);
+ nanoLabelStyleRight.stretchHeight = true;
+ nanoLabelStyleRight.stretchWidth = true;
+
+ nanoLabelStyleRightGreen = new GUIStyle(nanoLabelStyleRight);
+ nanoLabelStyleRightGreen.normal.textColor = Color.green;
+
+ nanoLabelStyleRightRed = new GUIStyle(nanoLabelStyleRight);
+ nanoLabelStyleRightRed.normal.textColor = Color.red;
hoverLabel = new GUIStyle(skin.label);
hoverLabel.alignment = TextAnchor.MiddleCenter;
@@ -72,25 +114,32 @@ internal static void Init()
textBoxStyle.alignment = TextAnchor.MiddleCenter;
textBoxStyle.fontSize = 11;
textBoxStyle.margin = new RectOffset(2, 2, 1, 1);
+
+ largeTextBoxStyle = new GUIStyle(textBoxStyle);
+ largeTextBoxStyle.fontSize = 12;
+ largeTextBoxStyle.margin = new RectOffset(2, 2, 1, 1);
+ largeTextBoxStyle.padding = new RectOffset(2, 2, 2, 2);
toggleButtonStyle = new GUIStyle(skin.button);
toggleButtonStyle.alignment = TextAnchor.MiddleCenter;
toggleButtonStyle.margin = new RectOffset(4, 4, 1, 1);
toggleButtonStyle.padding = new RectOffset(4, 4, 2, 2);
+ toggleButtonStyle.fontSize = 12;
var button_pressed_style = toggleButtonStyle.onActive;
- button_pressed_style.textColor = Color.green;
+ button_pressed_style.textColor = toggleButtonStyle.onHover.textColor = Color.green;
toggleButtonStyle.normal.textColor = Color.white;
- toggleButtonStyle.fontSize = 12;
toggleButtonStyle.onNormal = button_pressed_style;
toggleButtonStyle.stretchWidth = true;
-
- toggleButtonStyleRight = new GUIStyle(skin.button);
+
+ compactToggleButtonStyle = new GUIStyle(toggleButtonStyle);
+ //compactToggleButtonStyle.margin = new RectOffset(4, 4, 1, 1);
+ //compactToggleButtonStyle.padding = new RectOffset(4, 4, 2, 2);
+ //compactToggleButtonStyle.fontSize = 12;
+ compactToggleButtonStyle.stretchWidth = false;
+ compactToggleButtonStyle.fixedWidth = 36;
+
+ toggleButtonStyleRight = new GUIStyle(toggleButtonStyle);
toggleButtonStyleRight.alignment = TextAnchor.MiddleRight;
- toggleButtonStyleRight.margin = new RectOffset(4, 4, 1, 1);
- toggleButtonStyleRight.padding = new RectOffset(4, 4, 2, 2);
- toggleButtonStyleRight.onNormal = button_pressed_style;
- toggleButtonStyleRight.fontSize = 12;
- toggleButtonStyleRight.stretchWidth = true;
}
static Color old_background, old_color, old_content;
diff --git a/AtmosphereAutopilot/Global_settings.txt b/AtmosphereAutopilot/Global_settings.txt
index 37d4f6a..50b6d42 100644
--- a/AtmosphereAutopilot/Global_settings.txt
+++ b/AtmosphereAutopilot/Global_settings.txt
@@ -1,6 +1,14 @@
AtmosphereAutopilot
{
use_neo_gui = false
+ master_switch_key_toggles_gui = true
+ secondary_wing_level_snap_angle = 45.0f
+ scroll_wheel_number_field_increment_vertical = 0.5
+ scroll_wheel_number_field_increment_horizontal = 0.1
+}
+AtmosphereAutopilotSerialized
+{
+ compact_gui = false
}
Cruise_Flight
{
@@ -145,7 +153,7 @@ Sideslip_controller
AoA-hold_controller
{
use_keys = True
- hotkey_desired_aoa_sens = 3
+ hotkey_desired_aoa_sens = 2.5
window_x = 54
window_y = 243
}
diff --git a/AtmosphereAutopilot/Math/Matrix.cs b/AtmosphereAutopilot/Math/Matrix.cs
index 758358b..52ee7fc 100644
--- a/AtmosphereAutopilot/Math/Matrix.cs
+++ b/AtmosphereAutopilot/Math/Matrix.cs
@@ -615,22 +615,22 @@ private static Matrix StrassenMultiply(Matrix A, Matrix B) // Sma
R = new Matrix(A.rows, B.cols); // result
- /// C11
+ // C11
for (int i = 0; i < Math.Min(h, R.rows); i++) // rows
for (int j = 0; j < Math.Min(h, R.cols); j++) // cols
R[i, j] = mField[0, 1 + 1][i, j] + mField[0, 1 + 4][i, j] - mField[0, 1 + 5][i, j] + mField[0, 1 + 7][i, j];
- /// C12
+ // C12
for (int i = 0; i < Math.Min(h, R.rows); i++) // rows
for (int j = h; j < Math.Min(2 * h, R.cols); j++) // cols
R[i, j] = mField[0, 1 + 3][i, j - h] + mField[0, 1 + 5][i, j - h];
- /// C21
+ // C21
for (int i = h; i < Math.Min(2 * h, R.rows); i++) // rows
for (int j = 0; j < Math.Min(h, R.cols); j++) // cols
R[i, j] = mField[0, 1 + 2][i - h, j] + mField[0, 1 + 4][i - h, j];
- /// C22
+ // C22
for (int i = h; i < Math.Min(2 * h, R.rows); i++) // rows
for (int j = h; j < Math.Min(2 * h, R.cols); j++) // cols
R[i, j] = mField[0, 1 + 1][i - h, j - h] - mField[0, 1 + 2][i - h, j - h] + mField[0, 1 + 3][i - h, j - h] + mField[0, 1 + 6][i - h, j - h];
@@ -670,22 +670,22 @@ private static void StrassenMultiplyRun(Matrix A, Matrix B, Matrix C, int l, Mat
AplusBintoC(B, 0, h, B, h, h, f[l, 1], h);
StrassenMultiplyRun(f[l, 0], f[l, 1], f[l, 1 + 7], l + 1, f); // (A12 - A22) * (B21 + B22);
- /// C11
+ // C11
for (int i = 0; i < h; i++) // rows
for (int j = 0; j < h; j++) // cols
C[i, j] = f[l, 1 + 1][i, j] + f[l, 1 + 4][i, j] - f[l, 1 + 5][i, j] + f[l, 1 + 7][i, j];
- /// C12
+ // C12
for (int i = 0; i < h; i++) // rows
for (int j = h; j < size; j++) // cols
C[i, j] = f[l, 1 + 3][i, j - h] + f[l, 1 + 5][i, j - h];
- /// C21
+ // C21
for (int i = h; i < size; i++) // rows
for (int j = 0; j < h; j++) // cols
C[i, j] = f[l, 1 + 2][i - h, j] + f[l, 1 + 4][i - h, j];
- /// C22
+ // C22
for (int i = h; i < size; i++) // rows
for (int j = h; j < size; j++) // cols
C[i, j] = f[l, 1 + 1][i - h, j - h] - f[l, 1 + 2][i - h, j - h] + f[l, 1 + 3][i - h, j - h] + f[l, 1 + 6][i - h, j - h];
diff --git a/AtmosphereAutopilot/Math/OnlineLinTrainerWindow.cs b/AtmosphereAutopilot/Math/OnlineLinTrainerWindow.cs
index 7e8d666..1359456 100644
--- a/AtmosphereAutopilot/Math/OnlineLinTrainerWindow.cs
+++ b/AtmosphereAutopilot/Math/OnlineLinTrainerWindow.cs
@@ -45,7 +45,6 @@ protected override void _drawGUI(int id)
GUILayout.Label(String.Join(",", linmodel.pars.Select(v => v.ToString("G5")).ToArray()), GUIStyles.labelStyleCenter);
}
GUILayout.EndVertical();
- GUI.DragWindow();
}
}
}
diff --git a/AtmosphereAutopilot/Models/FlightModel/FlightModel.cs b/AtmosphereAutopilot/Models/FlightModel/FlightModel.cs
index ea7cb5f..25a544b 100644
--- a/AtmosphereAutopilot/Models/FlightModel/FlightModel.cs
+++ b/AtmosphereAutopilot/Models/FlightModel/FlightModel.cs
@@ -47,7 +47,7 @@ internal FlightModel(Vessel v) :
}
initialize_lin_tainers();
//integrator = vessel.GetComponent();
- window.width = 240.0f; // some vector components need love
+ windowPosition.width = 240.0f; // some vector components need love
}
//FlightIntegrator integrator;
@@ -221,18 +221,16 @@ public override bool Deserialize()
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
for (int i = 0; i < 3; i++)
{
GUILayout.Label("=======" + axis_names[i] + "=======");
- GUILayout.Label("ang vel = " + angular_v_buf[i].getLast().ToString("G8"), GUIStyles.labelStyleLeft);
- GUILayout.Label("ang acc = " + angular_acc_buf[i].getLast().ToString("G8"), GUIStyles.labelStyleLeft);
+ GUILayout.Label("ang vel = " + (angular_v_buf[i].getLast() * rad2dgr).ToString("G8"), GUIStyles.labelStyleLeft);
+ GUILayout.Label("ang acc = " + (angular_acc_buf[i].getLast() * rad2dgr).ToString("G8"), GUIStyles.labelStyleLeft);
GUILayout.Label("AoA = " + (aoa_buf[i].getLast() * rad2degree).ToString("G8"), GUIStyles.labelStyleLeft);
}
AutoGUI.AutoDrawObject(this);
GUILayout.EndVertical();
- GUI.DragWindow();
}
OnlineLinTrainerWindow pitch_lin_wnd, roll_lin_wnd, yaw_lin_wnd,
@@ -242,8 +240,8 @@ protected override void _drawGUI(int id)
void initialize_trainer_windows()
{
- Rect lin_wnd_rect = window;
- lin_wnd_rect.xMin = window.xMin - 190.0f;
+ Rect lin_wnd_rect = windowPosition;
+ lin_wnd_rect.xMin = windowPosition.xMin - 190.0f;
lin_wnd_rect.xMax = lin_wnd_rect.xMin + 190.0f;
trainer_windows[0] = pitch_lin_wnd = new OnlineLinTrainerWindow(pitch_trainer, "pitch trainer", 908999, lin_wnd_rect);
trainer_windows[1] = roll_lin_wnd = new OnlineLinTrainerWindow(roll_trainer, "roll trainer", 908998, lin_wnd_rect);
diff --git a/AtmosphereAutopilot/Modules/AngularAccAdaptiveController.cs b/AtmosphereAutopilot/Modules/AngularAccAdaptiveController.cs
index e347eed..470196a 100644
--- a/AtmosphereAutopilot/Modules/AngularAccAdaptiveController.cs
+++ b/AtmosphereAutopilot/Modules/AngularAccAdaptiveController.cs
@@ -89,8 +89,8 @@ public override float ApplyControl(FlightCtrlState cntrl, float target_value)
if (write_telemetry)
{
desire_acc_writer.Write(target_value.ToString("G8") + ',');
- acc_writer.Write(acc.ToString("G8") + ',');
- v_writer.Write(imodel.AngularVel(axis).ToString("G8") + ',');
+ acc_writer.Write((acc * rad2dgr).ToString("G8") + ',');
+ v_writer.Write((imodel.AngularVel(axis) * rad2dgr).ToString("G8") + ',');
//prediction_writer.Write(target_value.ToString("G8") + ',');
aoa_writer.Write(imodel.AoA(axis).ToString("G8") + ',');
airspd_writer.Write((imodel.up_srf_v + imodel.fwd_srf_v).magnitude.ToString("G8") + ',');
diff --git a/AtmosphereAutopilot/Modules/AngularVelAdaptiveController.cs b/AtmosphereAutopilot/Modules/AngularVelAdaptiveController.cs
index 409d192..bb06ce1 100644
--- a/AtmosphereAutopilot/Modules/AngularVelAdaptiveController.cs
+++ b/AtmosphereAutopilot/Modules/AngularVelAdaptiveController.cs
@@ -32,7 +32,6 @@ public abstract class AngularVelAdaptiveController : AutopilotModule
protected FlightModel imodel;
protected AngularAccAdaptiveController acc_controller;
- public bool user_controlled = false;
///
/// Create controller instance
@@ -41,7 +40,6 @@ public abstract class AngularVelAdaptiveController : AutopilotModule
/// Name of controller
/// unique for types window id
/// Pitch = 0, roll = 1, yaw = 2
- /// Flight model instance for adaptive control
protected AngularVelAdaptiveController(Vessel vessel, string module_name,
int wnd_id, int axis)
: base(vessel, wnd_id, module_name)
@@ -89,6 +87,9 @@ protected override void OnDeactivate()
[GlobalSerializable("precision_mode_factor")]
//[AutoGuiAttr("precision_mode_factor", true)]
public float precision_mode_factor = 0.33f;
+
+ [AutoGuiAttr("user_controlled", false)]
+ public bool user_controlled = false;
[AutoGuiAttr("prev_input", false, "G6")]
protected float prev_input;
@@ -97,6 +98,8 @@ protected override void OnDeactivate()
/// Main control function
///
/// Control state to change
+ ///
+ ///
public float ApplyControl(FlightCtrlState cntrl, float target_value, float target_acc = 0.0f)
{
vel = imodel.AngularVel(axis); // get angular velocity
@@ -114,13 +117,13 @@ public float ApplyControl(FlightCtrlState cntrl, float target_value, float targe
float delta_input = Common.Clampf(user_input - prev_input, clamp);
user_input = prev_input + delta_input;
prev_input = user_input;
- desired_v = user_input * max_v_construction;
+ desired_v = user_input * (ignore_max_v ? 9000000.0f : max_v_construction);
user_controlled = true;
}
else
{
// control from above
- desired_v = Common.Clampf(target_value, max_v_construction);
+ desired_v = ignore_max_v ? target_value : Common.Clampf(target_value, max_v_construction);
}
desired_v = process_desired_v(desired_v, user_controlled); // moderation stage
@@ -148,13 +151,15 @@ public float ApplyControl(FlightCtrlState cntrl, float target_value, float targe
return output_acc;
}
+ public bool ignore_max_v = false, ignore_max_g = false;
+
[VesselSerializable("max_v_construction")]
- [AutoGuiAttr("Max v construction", true, "G6")]
- public float max_v_construction = 0.7f;
+ [AutoGuiAttr("Max v construction", true, "G4")]
+ public float max_v_construction = 40.0f * dgr2rad; //About 0.7 rad/sec
public string max_v_construction_as_text
{
- set => this.max_v_construction = float.TryParse(value, out float v) ? v : this.max_v_construction;
- get => this.max_v_construction.ToString("G6");
+ set => this.max_v_construction = float.TryParse(value, out float v) ? Math.Max(v * dgr2rad, 0) : this.max_v_construction;
+ get => (this.max_v_construction * rad2dgr).ToString("G4");
}
protected virtual float process_desired_v(float des_v, bool user_input) { return des_v; }
@@ -236,6 +241,8 @@ protected PitchYawAngularVelocityController(Vessel vessel, string module_name,
[AutoGuiAttr("moder_cutoff_ias", true, "G4")]
public float moder_cutoff_ias = 10.0f;
+ public bool ignore_max_aoa = false;
+
protected Matrix state_mat = new Matrix(4, 1);
protected Matrix input_mat = new Matrix(1, 1);
@@ -243,7 +250,7 @@ protected PitchYawAngularVelocityController(Vessel vessel, string module_name,
protected override float process_desired_v(float des_v, bool user_input)
{
- float rad_max_aoa = max_aoa * dgr2rad;
+ float rad_max_aoa = (ignore_max_aoa ? 179.9f : max_aoa) * dgr2rad;
res_max_aoa = 100.0f;
res_min_aoa = -100.0f;
res_equilibr_v_upper = 0.0f;
@@ -258,7 +265,7 @@ protected override float process_desired_v(float des_v, bool user_input)
{
moderated = true;
- if (abs_cur_aoa < rad_max_aoa * 1.5f)
+ if (!ignore_max_v && abs_cur_aoa < rad_max_aoa * 1.5f)
{
// We're in linear regime so we can update our limitations
@@ -365,7 +372,7 @@ protected override float process_desired_v(float des_v, bool user_input)
}
// Lift acceleration moderation section
- if (moderate_g && imodel.dyn_pressure > moder_cutoff_ias * moder_cutoff_ias)
+ if (moderate_g && !ignore_max_g && imodel.dyn_pressure > moder_cutoff_ias * moder_cutoff_ias)
{
moderated = true;
@@ -450,7 +457,7 @@ protected override float process_desired_v(float des_v, bool user_input)
if (old_dyn_max_v != 0.0f)
transit_max_v = old_dyn_max_v;
else
- old_dyn_max_v = max_v_construction;
+ old_dyn_max_v = ignore_max_v ? 9000000.0f : max_v_construction;
}
else
{
@@ -458,15 +465,15 @@ protected override float process_desired_v(float des_v, bool user_input)
// we need to artificially increase it
if (new_dyn_max_v < res_equilibr_v_upper * 1.2 || new_dyn_max_v < -res_equilibr_v_lower * 1.2)
new_dyn_max_v = 1.2f * Math.Max(Math.Abs(res_equilibr_v_upper), Math.Abs(res_equilibr_v_lower));
- new_dyn_max_v = Common.Clampf(new_dyn_max_v, max_v_construction);
+ if (!ignore_max_v) new_dyn_max_v = Common.Clampf(new_dyn_max_v, max_v_construction);
transit_max_v = (float)Common.simple_filter(new_dyn_max_v, transit_max_v, moder_filter);
old_dyn_max_v = transit_max_v;
}
}
else
- transit_max_v = max_v_construction;
+ transit_max_v = ignore_max_v ? 9000000.0f : max_v_construction;
- // if the user is in charge, let's hold surface-relative angular elocity
+ // if the user is in charge, let's hold surface-relative angular velocity
float v_offset = 0.0f;
if (user_input && vessel.obt_speed > 1.0)
{
@@ -627,7 +634,7 @@ protected override float get_desired_acc(float des_v)
public float max_aoa = 15.0f;
public string max_aoa_as_text
{
- set => this.max_aoa = float.TryParse(value, out float v) ? v : this.max_aoa;
+ set => this.max_aoa = float.TryParse(value, out float v) ? Math.Max(v, 0) : this.max_aoa;
get => this.max_aoa.ToString("G6");
}
@@ -660,7 +667,7 @@ public sealed class RollAngularVelocityController : AngularVelAdaptiveController
internal RollAngularVelocityController(Vessel vessel)
: base(vessel, "Roll ang vel controller", 1234445, ROLL)
{
- max_v_construction = 3.0f;
+ max_v_construction = 180.0f * dgr2rad; //Previously 3.0 rad/sec, about 172 deg/sec
}
public override void InitializeDependencies(Dictionary modules)
@@ -688,6 +695,7 @@ public override void InitializeDependencies(Dictionary mo
[AutoGuiAttr("Snap angle", true, "G4")]
public float leveler_snap_angle = 3.0f;
+ public bool snapping_to_level = false;
[AutoGuiAttr("angle_btw_hor", false, "G5")]
float angle_btw_hor;
@@ -706,6 +714,8 @@ protected override float process_desired_v(float des_v, bool user_input)
{
float cur_aoa = imodel.AoA(YAW);
+ float current_max_v = ignore_max_v ? 9000000.0f : max_v_construction;
+
// let's find maximum angular v on 0.0 AoA and 0.0 Yaw input from model
if (Math.Abs(cur_aoa) < 0.3 && imodel.dyn_pressure > 100.0)
{
@@ -723,8 +733,8 @@ protected override float process_desired_v(float des_v, bool user_input)
// adequacy check
if (new_max_input_v < new_min_input_v || new_max_input_v < 0.0 || new_min_input_v > 0.0)
{
- new_max_input_v = max_v_construction;
- new_min_input_v = -max_v_construction;
+ new_max_input_v = current_max_v;
+ new_min_input_v = -current_max_v;
}
new_max_input_v = Mathf.Max(min_abs_angv, new_max_input_v);
new_min_input_v = Mathf.Min(-min_abs_angv, new_min_input_v);
@@ -733,14 +743,14 @@ protected override float process_desired_v(float des_v, bool user_input)
}
else
{
- max_input_v = max_v_construction;
- min_input_v = -max_v_construction;
+ max_input_v = current_max_v;
+ min_input_v = -current_max_v;
}
}
else
{
- max_input_v = max_v_construction;
- min_input_v = -max_v_construction;
+ max_input_v = current_max_v;
+ min_input_v = -current_max_v;
}
// wing level snapping
@@ -758,6 +768,8 @@ protected override float process_desired_v(float des_v, bool user_input)
angle_btw_hor_sin = -Vector3.Dot(roll_vector, vessel.ReferenceTransform.up);
if (Math.Abs(angle_btw_hor_sin) <= Math.Sin(leveler_snap_angle * dgr2rad))
{
+ snapping_to_level = true;
+
angle_btw_hor = Mathf.Asin(angle_btw_hor_sin);
float dt = TimeWarp.fixedDeltaTime;
@@ -772,34 +784,36 @@ protected override float process_desired_v(float des_v, bool user_input)
(float)Math.Sqrt(transit_max_angle * acc);
if (!float.IsNaN(new_dyn_max_v))
{
- new_dyn_max_v = Common.Clampf(new_dyn_max_v, max_v_construction);
+ if (!ignore_max_v) new_dyn_max_v = Common.Clampf(new_dyn_max_v, max_v_construction);
transit_max_v = (float)Common.simple_filter(new_dyn_max_v, transit_max_v, moder_filter);
snapping_vel = snapping_Kp * angle_btw_hor / transit_max_angle * transit_max_v;
if (Math.Abs(snapping_vel) > Math.Abs(angle_btw_hor) / dt)
snapping_vel = angle_btw_hor / dt;
}
- }
+ } else snapping_to_level = false;
}
}
// desired_v moderation section
- if (des_v >= 0.0f)
- {
- float normalized_des_v = user_input ? des_v / max_v_construction : des_v / Math.Min(max_input_v, max_v_construction);
- if (float.IsInfinity(normalized_des_v) || float.IsNaN(normalized_des_v))
- normalized_des_v = 0.0f;
- normalized_des_v = Common.Clampf(normalized_des_v, 1.0f);
- float scaled_restrained_v = Math.Min(max_input_v, max_v_construction);
- des_v = normalized_des_v * scaled_restrained_v;
- }
- else
- {
- float normalized_des_v = user_input ? des_v / -max_v_construction : des_v / Math.Max(min_input_v, -max_v_construction);
- if (float.IsInfinity(normalized_des_v) || float.IsNaN(normalized_des_v))
- normalized_des_v = 0.0f;
- normalized_des_v = Common.Clampf(normalized_des_v, 1.0f);
- float scaled_restrained_v = Math.Max(min_input_v, -max_v_construction);
- des_v = normalized_des_v * scaled_restrained_v;
+ if (!ignore_max_v) {
+ if (des_v >= 0.0f)
+ {
+ float normalized_des_v = user_input ? des_v / max_v_construction : des_v / Math.Min(max_input_v, max_v_construction);
+ if (float.IsInfinity(normalized_des_v) || float.IsNaN(normalized_des_v))
+ normalized_des_v = 0.0f;
+ normalized_des_v = Common.Clampf(normalized_des_v, 1.0f);
+ float scaled_restrained_v = Math.Min(max_input_v, max_v_construction);
+ des_v = normalized_des_v * scaled_restrained_v;
+ }
+ else
+ {
+ float normalized_des_v = user_input ? des_v / -max_v_construction : des_v / Math.Max(min_input_v, -max_v_construction);
+ if (float.IsInfinity(normalized_des_v) || float.IsNaN(normalized_des_v))
+ normalized_des_v = 0.0f;
+ normalized_des_v = Common.Clampf(normalized_des_v, 1.0f);
+ float scaled_restrained_v = Math.Max(min_input_v, -max_v_construction);
+ des_v = normalized_des_v * scaled_restrained_v;
+ }
}
return des_v + snapping_vel;
diff --git a/AtmosphereAutopilot/Modules/AoAController.cs b/AtmosphereAutopilot/Modules/AoAController.cs
index 8e61f7f..f70c71f 100644
--- a/AtmosphereAutopilot/Modules/AoAController.cs
+++ b/AtmosphereAutopilot/Modules/AoAController.cs
@@ -106,6 +106,9 @@ protected override void OnDeactivate()
public bool user_controlled = true;
+ public bool IgnoreMaxAoA { get { return v_controller.ignore_max_aoa; } set { v_controller.ignore_max_aoa = value; } }
+ public float MaxAoA { get { return v_controller.max_aoa; } }
+
///
/// Main control function
///
@@ -135,7 +138,7 @@ public float ApplyControl(FlightCtrlState cntrl, float target_value, float targe
user_controlled = true;
}
else
- desired_aoa = (float)Common.Clamp(target_value, v_controller.res_min_aoa, v_controller.res_max_aoa);
+ desired_aoa = Common.Clampf(target_value, v_controller.res_min_aoa, v_controller.res_max_aoa);
// Let's find equilibrium angular v on desired_aoa
LinearSystemModel model = lin_model_gen;
diff --git a/AtmosphereAutopilot/Modules/AoAHoldController.cs b/AtmosphereAutopilot/Modules/AoAHoldController.cs
index efdf738..42b745a 100644
--- a/AtmosphereAutopilot/Modules/AoAHoldController.cs
+++ b/AtmosphereAutopilot/Modules/AoAHoldController.cs
@@ -48,25 +48,17 @@ public override void InitializeDependencies(Dictionary mo
thrust_c = modules[typeof(ProgradeThrustController)] as ProgradeThrustController;
}
- bool aoa_moderation_saved = false;
-
protected override void OnActivate()
{
imodel.Activate();
aoa_c.Activate();
yaw_c.Activate();
roll_c.Activate();
- // save moderation states
- aoa_moderation_saved = pitch_c.moderate_aoa;
thrust_c.Activate();
MessageManager.post_status_message("AoA-hold enabled");
// initialize desired AoA from the current one
desired_aoa.Value = imodel.AoA(PITCH) * rad2dgr;
- // if current AoA is large, assume that the user does not want
- // moderation
- if (aoa_moderation_saved && Mathf.Abs(desired_aoa) > pitch_c.max_aoa)
- moderation_switch = false;
}
protected override void OnDeactivate()
@@ -75,14 +67,12 @@ protected override void OnDeactivate()
aoa_c.Deactivate();
yaw_c.Deactivate();
roll_c.Deactivate();
- // restore moderation states
- pitch_c.moderate_aoa = aoa_moderation_saved;
thrust_c.Deactivate();
MessageManager.post_status_message("AoA-hold disabled");
}
// degrees
- public DelayedFieldFloat desired_aoa = new DelayedFieldFloat(0.0f, "G4");
+ public DelayedFieldFloat desired_aoa = new DelayedFieldFloat(0.0f, "G2");
[GlobalSerializable("use_keys")]
[AutoGuiAttr("use keys", true)]
@@ -107,7 +97,7 @@ public bool moderation_switch
[AutoGuiAttr("hotkey sensitivity", true, "G4")]
[GlobalSerializable("hotkey_desired_aoa_sens")]
- public static float hotkey_desired_aoa_sens = 3.0f; // degrees/sec
+ public static float hotkey_desired_aoa_sens = 2.5f; // degrees/sec
public override void ApplyControl(FlightCtrlState cntrl)
{
@@ -121,12 +111,8 @@ public override void ApplyControl(FlightCtrlState cntrl)
ControlUtils.neutralize_user_input(cntrl, PITCH);
aoa_c.user_controlled = false;
- if (pitch_c.moderate_aoa)
- {
- // limit desired AoA with craft's "input" AoA limit.
- desired_aoa.Value = Common.Clampf(desired_aoa, pitch_c.max_aoa);
- }
- aoa_c.ApplyControl(cntrl, desired_aoa * dgr2rad, 0.0f);
+ aoa_c.IgnoreMaxAoA = Mathf.Abs(desired_aoa.Value) > aoa_c.MaxAoA;
+ aoa_c.ApplyControl(cntrl, desired_aoa.Value * dgr2rad, 0.0f);
side_c.user_controlled = true;
side_c.ApplyControl(cntrl, 0.0f, 0.0f);
roll_c.user_controlled = true;
@@ -175,13 +161,11 @@ protected override void OnGUICustomAlways()
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
AutoGUI.AutoDrawObject(this);
desired_aoa.DisplayLayout(GUIStyles.textBoxStyle);
thrust_c.SpeedCtrlGUIBlock();
GUILayout.EndVertical();
- GUI.DragWindow();
}
}
}
diff --git a/AtmosphereAutopilot/Modules/AutopilotModule.cs b/AtmosphereAutopilot/Modules/AutopilotModule.cs
index 4b16e7c..b131755 100644
--- a/AtmosphereAutopilot/Modules/AutopilotModule.cs
+++ b/AtmosphereAutopilot/Modules/AutopilotModule.cs
@@ -40,13 +40,25 @@ public abstract class AutopilotModule : GUIWindow, ISerializable
protected Vessel vessel = null;
protected bool enabled = false;
- protected string module_name;
+ protected string module_name, module_compact_name;
+
+ protected static readonly Dictionary compact_name_map = new Dictionary {
+ //{ "Mouse Director", "MD" }, //I'm a keyboard pleb, so I exclude this from the compact UI by commenting it out here
+ { "Cruise Flight controller", "CF" },
+ { "AoA-hold controller", "AoA" },
+ { "Standard Fly-By-Wire", "FBW" },
+ };
protected AutopilotModule(Vessel v, int wnd_id, string module_name):
base(module_name, wnd_id, new Rect(50.0f, 80.0f, 200.0f, 50.0f))
{
vessel = v;
this.module_name = module_name;
+ try {
+ this.module_compact_name = compact_name_map[this.module_name];
+ } catch (KeyNotFoundException) {
+ this.module_compact_name = "";
+ }
}
///
@@ -62,8 +74,9 @@ public void Activate()
OnActivate();
enabled = true;
}
-
+
public string ModuleName { get { return module_name; } }
+ public string ModuleCompactName { get { return module_compact_name; } }
protected abstract void OnActivate();
@@ -156,36 +169,34 @@ protected virtual void OnSerialize(ConfigNode node, Type attribute_type) { }
#region GUI
[GlobalSerializable("window_x")]
- protected float WindowLeft { get { return window.xMin; }
+ protected float WindowLeft { get { return windowPosition.xMin; }
set
{
- float width = window.width;
- window.xMin = value;
- window.xMax = window.xMin + width;
+ float width = windowPosition.width;
+ windowPosition.xMin = value;
+ windowPosition.xMax = windowPosition.xMin + width;
}
}
[GlobalSerializable("window_y")]
- protected float WindowTop { get { return window.yMin; }
+ protected float WindowTop { get { return windowPosition.yMin; }
set
{
- float height = window.height;
- window.yMin = value;
- window.yMax = window.yMin + height;
+ float height = windowPosition.height;
+ windowPosition.yMin = value;
+ windowPosition.yMax = windowPosition.yMin + height;
}
}
//[GlobalSerializable("window_width")]
- protected float WindowWidth { get { return window.width; } set { window.width = value; } }
+ protected float WindowWidth { get { return windowPosition.width; } set { windowPosition.width = value; } }
///
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
AutoGUI.AutoDrawObject(this);
GUILayout.EndVertical();
- GUI.DragWindow();
}
#endregion
diff --git a/AtmosphereAutopilot/Modules/CruiseController.cs b/AtmosphereAutopilot/Modules/CruiseController.cs
index bd45e5e..b893704 100644
--- a/AtmosphereAutopilot/Modules/CruiseController.cs
+++ b/AtmosphereAutopilot/Modules/CruiseController.cs
@@ -475,7 +475,6 @@ void start_picking_waypoint()
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
// cruise flight control modes
@@ -624,10 +623,9 @@ protected override void _drawGUI(int id)
AutoGUI.AutoDrawObject(this);
}
else if (adv_o)
- window.height = 100.0f;
+ windowPosition.height = 100.0f;
GUILayout.EndVertical();
- GUI.DragWindow();
}
public override void OnUpdate()
@@ -643,7 +641,7 @@ public override void OnUpdate()
return;
}
// Thanks MechJeb!
- if (Input.GetMouseButtonDown(0) && !window.Contains(Input.mousePosition))
+ if (Input.GetMouseButtonDown(0) && !windowPosition.Contains(Input.mousePosition))
{
Ray mouseRay = PlanetariumCamera.Camera.ScreenPointToRay(Input.mousePosition);
mouseRay.origin = ScaledSpace.ScaledToLocalSpace(mouseRay.origin);
diff --git a/AtmosphereAutopilot/Modules/DirectorController.cs b/AtmosphereAutopilot/Modules/DirectorController.cs
index 56bec32..b57a6f9 100644
--- a/AtmosphereAutopilot/Modules/DirectorController.cs
+++ b/AtmosphereAutopilot/Modules/DirectorController.cs
@@ -114,6 +114,7 @@ public string strength_as_text
///
/// Main control function
///
+ ///
/// Desired velocity direction in surface reference frame.
/// Desired acceleration.
public void ApplyControl(FlightCtrlState state, Vector3d desired_vel, Vector3d desired_acceleration)
diff --git a/AtmosphereAutopilot/Modules/GravityTurnFlyByWire.cs b/AtmosphereAutopilot/Modules/GravityTurnFlyByWire.cs
index 47ccb6f..2cba5f2 100644
--- a/AtmosphereAutopilot/Modules/GravityTurnFlyByWire.cs
+++ b/AtmosphereAutopilot/Modules/GravityTurnFlyByWire.cs
@@ -90,7 +90,6 @@ protected override void _drawGUI(int id)
GUILayout.Space(5.0f);
AutoGUI.AutoDrawObject(this);
GUILayout.EndVertical();
- GUI.DragWindow();
}
}
}
diff --git a/AtmosphereAutopilot/Modules/MouseDirector.cs b/AtmosphereAutopilot/Modules/MouseDirector.cs
index d09be7c..4daa68d 100644
--- a/AtmosphereAutopilot/Modules/MouseDirector.cs
+++ b/AtmosphereAutopilot/Modules/MouseDirector.cs
@@ -108,12 +108,10 @@ public override void OnUpdate()
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
AutoGUI.AutoDrawObject(this);
thrust_c.SpeedCtrlGUIBlock();
GUILayout.EndVertical();
- GUI.DragWindow();
}
public class CenterIndicator : MonoBehaviour
diff --git a/AtmosphereAutopilot/Modules/StandardFlyByWire.cs b/AtmosphereAutopilot/Modules/StandardFlyByWire.cs
index 1bdce40..a61c252 100644
--- a/AtmosphereAutopilot/Modules/StandardFlyByWire.cs
+++ b/AtmosphereAutopilot/Modules/StandardFlyByWire.cs
@@ -240,7 +240,6 @@ public override void ApplyControl(FlightCtrlState cntrl)
protected override void _drawGUI(int id)
{
- close_button();
GUILayout.BeginVertical();
foreach (var module in gui_list)
{
@@ -254,7 +253,6 @@ protected override void _drawGUI(int id)
AutoGUI.AutoDrawObject(this);
tc.SpeedCtrlGUIBlock();
GUILayout.EndVertical();
- GUI.DragWindow();
}
}
}
diff --git a/AtmosphereAutopilot/Modules/TopModuleManager.cs b/AtmosphereAutopilot/Modules/TopModuleManager.cs
index f10aa4b..bb86dbd 100644
--- a/AtmosphereAutopilot/Modules/TopModuleManager.cs
+++ b/AtmosphereAutopilot/Modules/TopModuleManager.cs
@@ -19,6 +19,8 @@ You should have received a copy of the GNU General Public License
using System.Linq;
using UnityEngine;
using System.Reflection;
+using KSP.UI.Screens;
+using static GameParameters;
namespace AtmosphereAutopilot
{
@@ -44,6 +46,17 @@ internal TopModuleManager(Vessel vessel)
// settings window
CraftSettingsWindow settings_wnd;
+ private AoAHoldController aoahc = null;
+ private StandardFlyByWire sfbw = null;
+
+ private PitchAngularVelocityController pvc = null;
+ private RollAngularVelocityController rvc = null;
+ private YawAngularVelocityController yvc = null;
+
+ private float initialLevelerSnapAngle = 3.0f;
+
+ private static readonly float[] aoaHoldPresets = new float[] { -40.0f, -25.0f, -15.0f, -5.0f, -2.5f, -1.0f, 0, 1.0f, 2.5f, 5.0f, 15.0f, 25.0f, 40.0f };
+
public void create_context()
{
// We need to create all those modules. Module type needs to define constructor of
@@ -74,11 +87,19 @@ public void create_context()
if (HighLevelControllers.Count <= 0)
throw new InvalidOperationException("No high-level autopilot modules were found");
- else
- active_controller = HighLevelControllers[typeof(StandardFlyByWire)];
+ else {
+ pvc = cur_ves_modules[typeof(PitchAngularVelocityController)] as PitchAngularVelocityController;
+ rvc = cur_ves_modules[typeof(RollAngularVelocityController)] as RollAngularVelocityController;
+ yvc = cur_ves_modules[typeof(YawAngularVelocityController)] as YawAngularVelocityController;
+ aoahc = HighLevelControllers[typeof(AoAHoldController)] as AoAHoldController;
+ sfbw = HighLevelControllers[typeof(StandardFlyByWire)] as StandardFlyByWire;
+ active_controller = sfbw;
+ }
// map settings window to modules
settings_wnd.map_modues();
+
+ initialLevelerSnapAngle = rvc.leveler_snap_angle == AtmosphereAutopilot.Instance.secondary_wing_level_snap_angle ? 3.0f : rvc.leveler_snap_angle; //todo - we could instead store both of these per-vessel-design, currently neither are
}
protected override void OnActivate()
@@ -114,46 +135,151 @@ public override void ApplyControl(FlightCtrlState state)
protected override void _drawGUI(int id)
{
- close_button();
- GUILayout.BeginVertical();
- Active = GUILayout.Toggle(Active, "MASTER SWITCH", GUIStyles.toggleButtonStyle);
- bool show_settings = GUILayout.Toggle(settings_wnd.IsShown(), "Craft settings", GUIStyles.toggleButtonStyle);
- if (show_settings)
- settings_wnd.ShowGUI();
- else
- settings_wnd.UnShowGUI();
- GUILayout.Space(10);
- foreach (var controller in HighLevelControllers.Values)
+ if (GUI.Button(new Rect(1.0f, 1.0f, WINDOW_TITLE_BAR_HEIGHT - 2.0f, WINDOW_TITLE_BAR_HEIGHT - 1.0f),//windowPosition.width - 16.0f - 15.0f - 2.0f, 1.0f, 15.0f, 16.0f),
+ AtmosphereAutopilot.Instance.serialized.compact_gui ? "f" : "c", GUIStyles.toggleButtonStyle))
{
+ AtmosphereAutopilot.Instance.serialized.compact_gui = !AtmosphereAutopilot.Instance.serialized.compact_gui;
+ windowPosition.height = 0;
+ windowPosition.width = 200;
+ }
+
+ GUILayout.BeginVertical();
+ if (AtmosphereAutopilot.Instance.serialized.compact_gui) {
+ const float TEXT_BOX_WIDTH = 34.0f;
+
+ bool fullyInitialized = sfbw != null;
+
+ GUILayout.Space(3);
+
GUILayout.BeginHorizontal();
- bool pressed = GUILayout.Toggle(active_controller == controller, controller.ModuleName,
- GUIStyles.toggleButtonStyle, GUILayout.Width(155.0f), GUILayout.ExpandWidth(false));
- if (pressed && !controller.Active)
- {
- if (Active)
- {
- // we activate new module
- bool activation = true;
- if (active_controller != null)
- {
- activation = false;
- active_controller.Deactivate();
+
+ AutoGUI.HandleToggleButton(Active, "MS", GUIStyles.compactToggleButtonStyle,
+ (bool toggledOn) => { Active = toggledOn; }, (bool toggledOn) => { settings_wnd.ToggleGUI(); });
+
+ if (fullyInitialized) {
+ GUILayout.Space(10);
+
+ foreach (var controller in HighLevelControllers.Values) {
+ if (controller.ModuleCompactName.Length > 0) {
+ bool controllerWasActive = active_controller == controller;
+ AutoGUI.HandleToggleButton(controllerWasActive, controller.ModuleCompactName, GUIStyles.compactToggleButtonStyle,
+ (bool toggledOn) => {
+ OnModuleButtonActive(controller);
+ if (controller.ModuleCompactName == "CF") { //Kinda need the whole thing for cruise-flight mode
+ if (controllerWasActive) controller.ToggleGUI();
+ else controller.ShowGUI();
+ }
+ }, (bool toggledOn) => { controller.ToggleGUI(); });
}
- controller.Activate();
- if (!activation)
- (cur_ves_modules[typeof(FlightModel)] as FlightModel).sequential_dt = true;
}
- active_controller = controller;
+
+ GUILayout.Space(10);
+
+ if (sfbw != null) {
+ AutoGUI.HandleToggleButton(sfbw.Coord_turn, "CT", GUIStyles.compactToggleButtonStyle,
+ (bool toggledOn) => { sfbw.Coord_turn = toggledOn; },
+ (bool toggledOn) => {
+ if (Input.GetKey(KeyCode.LeftAlt)) yvc.moderate_aoa = !yvc.moderate_aoa; //Toggle sideslip moderation
+ else cur_ves_modules?[typeof(FlightModel)]?.ToggleGUI();
+ });
+ if (!yvc.moderate_aoa && Event.current.type == EventType.Repaint) GUI.Label(GUILayoutUtility.GetLastRect(), "SS", GUIStyles.nanoLabelStyleRightRed);
+ }
+ if (rvc != null) {
+ AutoGUI.HandleToggleButton(rvc.wing_leveler, "Lvl", GUIStyles.compactToggleButtonStyle,
+ (bool toggledOn) => { rvc.wing_leveler = toggledOn; },
+ (bool toggledOn) => {
+ if (rvc.leveler_snap_angle == AtmosphereAutopilot.Instance.secondary_wing_level_snap_angle) rvc.leveler_snap_angle = initialLevelerSnapAngle;
+ else {
+ initialLevelerSnapAngle = rvc.leveler_snap_angle;
+ rvc.leveler_snap_angle = AtmosphereAutopilot.Instance.secondary_wing_level_snap_angle;
+ }
+ });
+ if (Event.current.type == EventType.Repaint) GUI.Label(GUILayoutUtility.GetLastRect(), rvc.leveler_snap_angle.ToString(), rvc.snapping_to_level ? GUIStyles.nanoLabelStyleRightGreen : GUIStyles.nanoLabelStyleRight);
+ }
+
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(4);
+
+ GUILayout.BeginHorizontal();
+
+ if (aoahc != null) {
+ GUILayout.Label("AoA:", GUIStyles.smallLabelStyleLeft, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true));
+ AutoGUI.CheckForClick(() => {
+ if (Input.GetKey(KeyCode.LeftAlt)) aoahc.desired_aoa.Value = GetNextPreset(aoaHoldPresets, aoahc.desired_aoa.Value);
+ else aoahc.desired_aoa.InvertValue();
+ }, () => {
+ if (Input.GetKey(KeyCode.LeftAlt)) aoahc.desired_aoa.Value = GetPreviousPreset(aoaHoldPresets, aoahc.desired_aoa.Value);
+ else aoahc.desired_aoa.Value = 0;
+ });
+ var aoaLabelScroll = AutoGUI.GetNumberTextBoxScrollWheelChange();
+ if (aoaLabelScroll > 0) aoahc.desired_aoa.Value = GetPreviousPreset(aoaHoldPresets, aoahc.desired_aoa.Value);
+ else if (aoaLabelScroll < 0) aoahc.desired_aoa.Value = GetNextPreset(aoaHoldPresets, aoahc.desired_aoa.Value);
+
+ aoahc.desired_aoa.DisplayLayout(GUIStyles.largeTextBoxStyle, GUILayout.Width(TEXT_BOX_WIDTH), GUILayout.ExpandWidth(false));
+ aoahc.desired_aoa -= AutoGUI.GetNumberTextBoxScrollWheelChange();
+ /*if (AutoGUI.CheckForRightClick()) { //See "//Doesn't work because focused text fields block all input" :(
+ aoahc.desired_aoa.Value = Input.GetKey(KeyCode.LeftAlt) ? GetPreviousPreset(aoaHoldPresets, aoahc.desired_aoa.Value) : GetNextPreset(aoaHoldPresets, aoahc.desired_aoa.Value);
+ GUI.FocusControl(null);
+ }*/
+ GUILayout.Space(3);
+
+ bool limitsDisabled = pvc.ignore_max_v || pvc.ignore_max_g ||
+ rvc.ignore_max_v || rvc.ignore_max_g ||
+ yvc.ignore_max_v || yvc.ignore_max_g ||
+ !pvc.moderate_aoa;
+ GUILayout.Label("Lims:", limitsDisabled ? GUIStyles.smallLabelStyleLeftRed : GUIStyles.smallLabelStyleLeft, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true));
+ if (AutoGUI.CheckForRightClick()) {
+ pvc.ignore_max_v = pvc.ignore_max_g =
+ rvc.ignore_max_v = rvc.ignore_max_g =
+ yvc.ignore_max_v = yvc.ignore_max_g = !limitsDisabled;
+ pvc.moderate_aoa = limitsDisabled;
+ }
+ GUILayout.Space(2);
+ GUILayout.Label("Pitch\nRate", pvc.ignore_max_v ? GUIStyles.microLabelStyleLeftRed : GUIStyles.microLabelStyleLeft, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true));
+ if (AutoGUI.CheckForRightClick()) pvc.ignore_max_v = pvc.ignore_max_g = !pvc.ignore_max_v;
+ pvc.max_v_construction_as_text = GUILayout.TextField(pvc.max_v_construction_as_text, GUIStyles.largeTextBoxStyle, GUILayout.Width(TEXT_BOX_WIDTH), GUILayout.ExpandWidth(false));
+ pvc.max_v_construction -= AutoGUI.GetNumberTextBoxScrollWheelChange() * dgr2rad;
+ GUILayout.Space(1);
+ GUILayout.Label("Roll\nRate", rvc.ignore_max_v ? GUIStyles.microLabelStyleLeftRed : GUIStyles.microLabelStyleLeft, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true));
+ if (AutoGUI.CheckForRightClick()) rvc.ignore_max_v = rvc.ignore_max_g = !rvc.ignore_max_v;
+ rvc.max_v_construction_as_text = GUILayout.TextField(rvc.max_v_construction_as_text, GUIStyles.largeTextBoxStyle, GUILayout.Width(TEXT_BOX_WIDTH), GUILayout.ExpandWidth(false));
+ rvc.max_v_construction -= AutoGUI.GetNumberTextBoxScrollWheelChange() * dgr2rad;
+ GUILayout.Space(1);
+ GUILayout.Label("AoA", pvc.moderate_aoa ? GUIStyles.microLabelStyleLeft : GUIStyles.microLabelStyleLeftRed, GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true));
+ if (AutoGUI.CheckForRightClick()) pvc.moderate_aoa = !pvc.moderate_aoa;
+ pvc.max_aoa_as_text = GUILayout.TextField(pvc.max_aoa_as_text, GUIStyles.largeTextBoxStyle, GUILayout.Width(TEXT_BOX_WIDTH), GUILayout.ExpandWidth(false));
+ pvc.max_aoa -= AutoGUI.GetNumberTextBoxScrollWheelChange();
+ }
}
- bool is_shown = GUILayout.Toggle(controller.IsShown(), "GUI", GUIStyles.toggleButtonStyle);
- if (is_shown)
- controller.ShowGUI();
- else
- controller.UnShowGUI();
+
GUILayout.EndHorizontal();
+ } else {
+ Active = GUILayout.Toggle(Active, "MASTER SWITCH", GUIStyles.toggleButtonStyle);
+
+ bool show_settings = GUILayout.Toggle(settings_wnd.IsShown(), "Craft settings", GUIStyles.toggleButtonStyle);
+ if (show_settings)
+ settings_wnd.ShowGUI();
+ else
+ settings_wnd.UnShowGUI();
+
+ GUILayout.Space(10);
+
+ foreach (var controller in HighLevelControllers.Values)
+ {
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Toggle(active_controller == controller, controller.ModuleName,
+ GUIStyles.toggleButtonStyle, GUILayout.Width(155.0f), GUILayout.ExpandWidth(false))) OnModuleButtonActive(controller);
+
+ bool is_shown = GUILayout.Toggle(controller.IsShown(), "GUI", GUIStyles.toggleButtonStyle);
+ if (is_shown)
+ controller.ShowGUI();
+ else
+ controller.UnShowGUI();
+ GUILayout.EndHorizontal();
+ }
}
GUILayout.EndVertical();
- GUI.DragWindow();
}
protected override void OnGUICustom()
@@ -197,6 +323,22 @@ public StateController activateAutopilot(Type controllerType)
return null;
}
+ private void OnModuleButtonActive(StateController controller) {
+ if (!controller.Active) {
+ if (Active) {
+ // we activate new module
+ bool activation = true;
+ if (active_controller != null) {
+ activation = false;
+ active_controller.Deactivate();
+ }
+ controller.Activate();
+ if (!activation) (cur_ves_modules[typeof(FlightModel)] as FlightModel).sequential_dt = true;
+ }
+ active_controller = controller;
+ }
+ }
+
[GlobalSerializable("master_switch_key")]
[AutoHotkeyAttr("Master switch")]
static KeyCode master_switch_key = KeyCode.P;
@@ -211,9 +353,40 @@ public override void OnUpdate()
else
{
Active = !Active;
+ if (AtmosphereAutopilot.Instance.master_switch_key_toggles_gui) {
+ if (Active) ShowGUI();
+ else UnShowGUI();
+ }
AtmosphereAutopilot.Instance.mainMenuGUIUpdate();
}
}
+
+ private float GetPreviousPreset(float[] presets, float currentValue) {
+ for (int i = presets.Length - 1; i >= 0; i--) {
+ if (presets[i] < currentValue) return presets[i];
+ }
+ //return presets[presets.Length - 1]; //Loops around
+ return currentValue;
+ }
+
+ private float GetNextPreset(float[] presets, float currentValue) {
+ foreach (float preset in presets) {
+ if (preset > currentValue) return preset;
+ }
+ //return presets[0]; //Loops around
+ return currentValue;
+ }
+
+ public override void Serialize()
+ {
+ base.Serialize();
+ settings_wnd.Serialize();
+ }
+
+ public override bool Deserialize()
+ {
+ return base.Deserialize() && settings_wnd.Deserialize();
+ }
#region SettingsWindow
@@ -337,24 +510,23 @@ protected override void _drawGUI(int id)
{
// let's delete this profile
profiles.Remove(s);
- window.height = 0.0f; // compact window
+ windowPosition.height = 0.0f; // compact window
Serialize();
}
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
- GUI.DragWindow();
}
[GlobalSerializable("window_x")]
- protected float WindowLeft { get { return window.xMin; } set { window.xMin = value; } }
+ protected float WindowLeft { get { return windowPosition.xMin; } set { windowPosition.xMin = value; } }
[GlobalSerializable("window_y")]
- protected float WindowTop { get { return window.yMin; } set { window.yMin = value; } }
+ protected float WindowTop { get { return windowPosition.yMin; } set { windowPosition.yMin = value; } }
[GlobalSerializable("window_width")]
- protected float WindowWidth { get { return window.width; } set { window.width = value; } }
+ protected float WindowWidth { get { return windowPosition.width; } set { windowPosition.width = value; } }
string profile_name = "";
@@ -487,16 +659,5 @@ void OnDeserialize(ConfigNode node, Type attType)
#endregion SettingsWindow
- public override void Serialize()
- {
- base.Serialize();
- settings_wnd.Serialize();
- }
-
- public override bool Deserialize()
- {
- return base.Deserialize() && settings_wnd.Deserialize();
- }
-
}
}
diff --git a/README.md b/README.md
index af960a3..01e7d3a 100644
--- a/README.md
+++ b/README.md
@@ -21,38 +21,58 @@ ModuleManager.
## How to build:
You need to build two dlls: AtmosphereAutopilot.UI.dll and AtmosphereAutopilot.dll. Both can be built from MS Visual studio on Windows using AtmosphereAutopilot.sln.
-On Linux you need to run build.sh from root directory, wich requires the following packages:
+
+On Linux you need to run build.sh from root directory, which requires the following packages:
* monodevelop (look for msbuild binary availability)
* zip
+
Results will be in AtmosphereAutopilot/bin/Release folder.
# General description
-Atmosphere autopilot is a modular atmospheric flight control system library. It's meant to be a foundation for multiple high-level programs - "Autopilots", wich will aid KSP player in one way or another, implying atmospheric flight. Autopilots are mutually exclusive - only one or none at all may be active at the active vessel at one given moment. They are provided by the library with means of automatic reflection-based serialization\deserialization and ugly, but lazy and customizable GUI interaction.
+Atmosphere autopilot is a modular atmospheric flight control system library. It's meant to be a foundation for multiple high-level programs - "Autopilots", which will aid KSP player in one way or another, implying atmospheric flight. Autopilots are mutually exclusive - only one or none at all may be active at the active vessel at one given moment. They are provided by the library with means of automatic reflection-based serialization\deserialization and ugly, but lazy and customizable GUI interaction.
-Autopilots are modular entities. They can use basic, provided by main library components (like controllers and models), or they can define their own components and share them with other Autopilots. Those components will be called "Autopilot modules", or simply - "Modules". Every sealed child of AutopilotModule wich is not a StateController is treated by the library like a Module. Sealed StateController children are treated as Autopilots.
+Autopilots are modular entities. They can use basic, provided by main library components (like controllers and models), or they can define their own components and share them with other Autopilots. Those components will be called "Autopilot modules", or simply - "Modules". Every sealed child of AutopilotModule which is not a StateController is treated by the library like a Module. Sealed StateController children are treated as Autopilots.
Stock and FAR aerodynamics are supported.
# GUI concept
-AA icon is placed in Application Launcher toolbar during flight. It's contents will visualize a list of all Autopilots and Modules, created for active vessel. For every vessel "Autopilot Module Manager" will be created regardless. Turning on a "MASTER SWITCH" on it's window will create required context of Autopilots and Modules for this vessel. Under the master switch all Autopilots will be listed, for the user to choose one of them as an active one. Hotkey for Master switch is letter P, autoPilot. Can be changed in Global_settings.cfg file, Autopilot_module_manager section.
+AA icon is placed in Application Launcher toolbar during flight. Its contents will visualize a list of all Autopilots and Modules, created for active vessel. For every vessel "Autopilot Module Manager" will be created regardless. Turning on a "MASTER SWITCH" on its window will create required context of Autopilots and Modules for this vessel. Under the master switch all Autopilots will be listed, for the user to choose one of them as an active one. Hotkey for Master switch is letter P, autoPilot. Can be changed in Global_settings.cfg file, Autopilot_module_manager section, or from the hotkey manager.
Craft settings window contains shotrcuts to most used moderation and tuning parameters of the craft, as well as provides basic preset functionality. Presets are saved in "Global_settings.cfg"/settings_wnd/profiles section.
-Each Autopilot and Module has it's own GUI window. All of them (even inactive ones) are accessible from AA button in Application Launcher, some of them are accessible from Autopilot window hierarchy (that's up to Autopilot developer to decide, what particular Modules should be accessible from it's GUI). Window positions are serialized (preserved between flights and game sessions) in "Global_settings.cfg" file.
+Each Autopilot and Module has its own GUI window. All of them (even inactive ones) are accessible from AA button in Application Launcher, some of them are accessible from Autopilot window hierarchy (that's up to Autopilot developer to decide, what particular Modules should be accessible from its GUI). Window positions are serialized (preserved between flights and game sessions) in "Global_settings.cfg" file.
# Neo-GUI
-Alternative, more condensed but less powerfull way of representing AppLauncher window can be turned on by setting AtmosphereAutopilot/use_neo_gui to _true_ in Global_settings.txt config file. It is read every scene change, so the shift can be made without shutting KSP down. While it's active, "Autopilot Module Manager" is still accessible using hotkeys. Standard GUI has logical priority over Neo-GUI.
+Alternative, more condensed but less powerful way of representing AppLauncher window can be turned on by setting AtmosphereAutopilot/use_neo_gui to _true_ in Global_settings.txt config file. It is read every scene change, so the shift can be made without shutting KSP down. While it's active, "Autopilot Module Manager" is still accessible using hotkeys. Standard GUI has logical priority over Neo-GUI.
+
+# Compact GUI
+The small _c_ button in the upper-left of the "Autopilot Module Manager" window will toggle a compact mode for that window, providing all of the commonly-needed actions in a single window. Additional settings for this mode can be found in Global_settings.cfg
+
+Top row:
+* "MS" - Master Switch - Right-click to toggle the "Craft Settings" window.
+* "CF" - Cruise Flight mode - Right-click to toggle its controller window. Left-click will also toggle it, since it's essential to the usage of that mode.
+* "AoA" - Angle-of-Attack Hold mode - Right-click to toggle its controller window.
+* "FBW" - Standard Fly-By-Wire mode - Right-click to toggle its controller window.
+* "CT" - Coordinated Turn - Right-click to toggle the "Flight Model" window. Alt+right-click to toggle sideslip moderation.
+* "Lvl" - Snap Wings to Level - Right-click to toggle between a larger snap angle.
+
+Second row:
+* "AoA" - Target angle for the AoA-hold mode. Right-click on the label to set it to 0. Left-click invert the current angle. Use the scroll wheel or alt+left/right click to cycle between presets (0/1/2.5/5/15/40 degrees).
+* "Lims" - Limits for pitch/roll rate and angle-of-attack. Right-click to toggle all limits, or right-click on an individual limit label to only toggle that one.
+
+Use the scroll wheel while hovering over a text field to change its value. Hold right-control or right-alt to use smaller or larger increments.
# Hotkeys
-"Hotkey manager" window is placed into Application Launcher window list. It's contents are registered hotkeys, wich can be changed during runtime.
-There are two main hotkeys:
+"Hotkey manager" window is placed into Application Launcher window list. Its contents are registered hotkeys, which can be changed during runtime.
+
+There are two main hotkeys:
* "Master switch" - toggles Master Switch.
* Shift + "Master switch" - toggles GUI of "Autopilot Module Manager".
Others are very module-specific and will not be described here.
# Craft implications and limitations
-"Control from here" part is facing prograde, with close-to-zero angle of attack bias. Planar symmetry is implied (left and right side of the plane are mirrored), as well as good degree of pitch-yaw and pitch-roll control isolation. Axial engine symmetry is strongly recommended. No wind mods are supported, as well as any mods, wich are changing control surface, rcs and engine gimbaling behaviour.
+"Control from here" part is facing prograde, with close-to-zero angle of attack bias. Planar symmetry is implied (left and right side of the plane are mirrored), as well as good degree of pitch-yaw and pitch-roll control isolation. Axial engine symmetry is strongly recommended. No wind mods are supported, as well as any mods, which are changing control surface, rcs and engine gimbaling behaviour.
**WARNING: DO NOT USE AEROBRAKES AS CONTROL SURFACES, USE THEM ONLY AS BRAKES!**
@@ -60,13 +80,14 @@ Others are very module-specific and will not be described here.
## Standard Fly-By-Wire
In general, FBW (Fly-By-Wire) is an abstraction Autopilot. It is designed to aid in player-controlled flight on generic (space)plane, providing a soft layer between user joystick\keyboard input and control surface outputs.
+
Main goals:
* Auto-trimming.
* AoA and G-force moderation.
* Sideslip handling.
* Fighting oscillations.
-FBW uses three controllers - pitch, roll and yaw. Pitch is handled by "Pitch ang vel controller", roll by "Roll ang vel controller" and yaw is handled by "Sideslip controller" in plane mode, or directly by "Yaw ang vel controller" in "Rocket mode". In Rocket mode pitch and yaw axes are treated the same - it's usefull in case player wants to use FBW for rocket launches. FBW is effective only on small (<25 degrees) AoA values, though control is possible on all regimes. It's just that it's quality will degrade from inadequacy of linearization assumptions. "Moderation" button is toggling all pitch and yaw moderations - usefull for low speed VTOL action or for fighting overmoderation bugs. Pitch moderation is turned off for 2 seconds after taking-off to prevent overmoderation-related crashes.
+FBW uses three controllers - pitch, roll and yaw. Pitch is handled by "Pitch ang vel controller", roll by "Roll ang vel controller" and yaw is handled by "Sideslip controller" in plane mode, or directly by "Yaw ang vel controller" in "Rocket mode". In Rocket mode pitch and yaw axes are treated the same - it's useful in case player wants to use FBW for rocket launches. FBW is effective only on small (<25 degrees) AoA values, though control is possible on all regimes. It's just that its quality will degrade from inadequacy of linearization assumptions. "Moderation" button is toggling all pitch and yaw moderations - usefull for low speed VTOL action or for fighting overmoderation bugs. Pitch moderation is turned off for 2 seconds after taking-off to prevent overmoderation-related crashes.
"Coordinated turn" - pseudo-pitch hold to assist in performing coordinated turns.
@@ -75,21 +96,22 @@ Hotkeys:
* "FBW rocket mode" - default hotkey unassigned.
* "FBW coord turn" - default hotkey unassigned.
-Speed control - throttle automation to maintain speed setpoint. Handeled by "Prograde thrust controller".
+Speed control - throttle automation to maintain speed setpoint. Handled by "Prograde thrust controller".
## Mouse Director
-Mouse Director (MD) is declarative autopilot, crafted with idea to let the user to define desired airspeed direction with camera position. Autopilot then tries to comply with this surface-relative velocity setpoint. MD is inherently-linear, so only relatively small angles of attack are allowed. All AoA moderations are forcefully turned on during it's operation.
+Mouse Director (MD) is declarative autopilot, crafted with idea to let the user to define desired airspeed direction with camera position. Autopilot then tries to comply with this surface-relative velocity setpoint. MD is inherently-linear, so only relatively small angles of attack are allowed. All AoA moderations are forcefully turned on during its operation.
-MD uses "Director controller", wich uses two AoA controllers: pitch "AoA controller" and yaw "Sideslip controller", and "Roll ang vel controller" for roll. Currently, planar asymmetry of a plane is not taken into account (sideslip noise is still too noticeable in zero-lift convergence problem), sideslip is always at zero setpoint. If your craft requires nonzero sideslip to fly straight, MD is not a very good solution right now, use FbW in the _rocket mode_.
+MD uses "Director controller", which uses two AoA controllers: pitch "AoA controller" and yaw "Sideslip controller", and "Roll ang vel controller" for roll. Currently, planar asymmetry of a plane is not taken into account (sideslip noise is still too noticeable in zero-lift convergence problem), sideslip is always at zero setpoint. If your craft requires nonzero sideslip to fly straight, MD is not a very good solution right now, use FbW in the _rocket mode_.
Short GUI description:
-Speed control - throttle automation to maintain speed setpoint. Handeled by "Prograde thrust controller".
+Speed control - throttle automation to maintain speed setpoint. Handled by "Prograde thrust controller".
## Cruise Flight controller
-Cruise Flight (CF) is high-level autopilot, designet for travel automation. Just like MD, CF is inherently-linear, so only relatively small angles of attack are allowed. All AoA moderations are forcefully turned on during it's operation.
+Cruise Flight (CF) is high-level autopilot, designet for travel automation. Just like MD, CF is inherently-linear, so only relatively small angles of attack are allowed. All AoA moderations are forcefully turned on during its operation.
CF uses "Director controller" for controlling velocity vector and "Prograde thrust controller" for throttle automation.
+
Functions:
* Simple leveling.
* Baromethric height and airspeed control.
@@ -100,7 +122,7 @@ Short GUI description:
* _Course_ - follows azimuth setpoint, set in field _desired course_. If altitude is not set, will keep vertical speed at zero. On high latitudes (>80 degrees) will switch to _Level_ mode.
* _Waypoint_ - primitive waypoint following. Designed for pick-and-fly functionality. When activated, _pick waypoint_ button appears under mode tabs, as well as waypoint latitude-longtitude representation and distance to it in straight line (through planet core). Waypoint control is turned off when destination is closer than 200 meters to be followed by _Level_ mode activation.
* _desired course_ - azimuth in degrees to follow in _Course_ mode.
-* _Speed control_ - throttle automation to maintain speed setpoint. Handeled by "Prograde thrust controller
+* _Speed control_ - throttle automation to maintain speed setpoint. Handled by "Prograde thrust controller
* _Vertical motion control_ - activate altitude or vertical speed or ascent angle (FPA) control. Otherwise vertical speed is kept at zero.
* _Altitude_ - hold altitude, meters above sea level.
* _Vertical speed_ - hold vertical speed, meters per second.
@@ -127,16 +149,15 @@ Hotkeys:
* "CF altitude\vertical speed" - toggles between _Altitude_ and _Vertical speed_ modes.
## AoA-hold
-AoA-hold (AoAH) maintains pitch Angle-of-Attack setpoint. Pitch AoA moderation is forcefully turned on during it's operation.
+AoA-hold (AoAH) maintains pitch Angle-of-Attack setpoint. Pitch AoA moderation is forcefully turned on during its operation.
AoAH is very similar to Standard Fly-By-Wire. It uses "AoA controller" for pitch, roll is handled by "Roll ang vel controller" and yaw is handled by "Sideslip controller".
Short GUI description:
-
* _use keys_ - use pitch keys to control AoA setpoint.
* _hotkey sensitivity_ - tweak to manage AoA setpoint change speed.
* _Pitch moderation_ - if enabled, AoA will be limited by craft settings.
-* Speed control - throttle automation to maintain speed setpoint. Handeled by "Prograde thrust controller".
+* Speed control - throttle automation to maintain speed setpoint. Handled by "Prograde thrust controller".
Hotkeys:
* "Pitch keys" - alter pitch AoA setpoint.
@@ -214,7 +235,7 @@ Short GUI description:
Low level dynamics inversion angular acceleration controllers. Input: desired angular acceleration (and yaw output for roll controller). Output: pitch\roll\yaw control state.
Short GUI description:
-* _Csurf output_ - current expected virtual control surface position, wich is usually lagged from control signal.
+* _Csurf output_ - current expected virtual control surface position, which is usually lagged from control signal.
* _write telemetry_ button - primitive logging capability for further matlab analysis. .csv logs are saved in KSP\Resources directory to be read by plotter.m viewer. It is a debug utility.
* _desired acc_ - desired acceleration, passed to this controller from above.
* _model predicted acc_ - predicted by model acceleration for the next frame.
@@ -235,7 +256,7 @@ Short GUI description:
* _max\min g v_ - respective equilibrium angular velocities.
* _max\min aoa v_ - equlibrium angular velocities for set by user AoA limit.
* _moder filter_ - default value - 3.0. Used to filter out rapid changes or oscillations in flight model to provide more smooth boundary condition evolution. Magic number.
-* _quadr Kp_ - default value - 0.3. Contoller uses parabolic descent model of angular velocity to it's desired value. Those descent parameters are governed by this koefficient. Larger values may cause overshoot from wrong control surface lag handling. Lower values will slow down control. Magic number.
+* _quadr Kp_ - default value - 0.3. Contoller uses parabolic descent model of angular velocity to its desired value. Those descent parameters are governed by this koefficient. Larger values may cause overshoot from wrong control surface lag handling. Lower values will slow down control. Magic number.
* _kacc quadr_ - parabollic steepness of control, governed by control utilities authority and craft characteristics. Should be positive.
* _kacc smoothing_ - default value - 10.0. Filter gain for slow and smooth "kacc quadr" evolution. Magic number.
* _relaxation k_ - default value - 1.0. Controller uses relaxed linear descent on very small velocity error regimes. This koefficient governs relaxation frame size.
@@ -245,7 +266,7 @@ Short GUI description:
* _transit max v_ - very rough, but safe estimation of maximum non-overshooting velocity in transit maneuver (from 0.0 AoA to maximum AoA).
* _res max\min aoa_ - AoA limits, that will actually govern controller in the current frame. Are chosed as the most strict ones from previously listed.
* _res max\min v_ - respective equilibrium angular velocities.
-* _scaled aoa_ - how far away current AoA is from it's limit.
+* _scaled aoa_ - how far away current AoA is from its limit.
* _scaled restr v_ - result of moderation algorithm.
* _Moderate AoA_ button - toggle angle of attack moderation. Is necessary for safe flight, but can be turned off, for example, during re-entry to provide maximum-drag profile. Required to be ON, if this controller is governed by upper-level AoA controller.
* _Moderate G-force_ button - toggle G-force moderation. Moderates centifugal acceleration of trajectory, not the full one, so G-meeter on navball will sometimes exceed maximum value (it is a correct behaviour).
@@ -277,7 +298,7 @@ Short GUI description:
* _AoA_ - respective angle of attack in radians.
* _desired aoa_ - processed by controller input in radians.
* _output v_ - controller output.
-* _desired aoa equilibr v_ - equilibrium angular velocity on desired angle of attack. For example, nosedive angular velocity on nose-heavy plane, wich will keep AoA at zero.
+* _desired aoa equilibr v_ - equilibrium angular velocity on desired angle of attack. For example, nosedive angular velocity on nose-heavy plane, which will keep AoA at zero.
* _filter k_ - filter gain to smooth changes in equilibrium v estimation. Default value - 4.0.
* _relaxation frame_ - relaxation frame count, used for close-to desired behaviour. Default value - 2.
* _relaxation factor_ - default value 0.1. Proportional gain of relaxation smoothing.
@@ -286,7 +307,7 @@ Short GUI description:
* _cubic mode_ - true if controller is now in cubic mode.
## Prograde thrust controller
-Hybrid model-reference or PID controller. Input: desired surface velocity. Output: throttle. Can be switched to PID control and manually tuned, if user is not satisfied with it's performance.
+Hybrid model-reference or PID controller. Input: desired surface velocity. Output: throttle. Can be switched to PID control and manually tuned, if user is not satisfied with its performance.
Short GUI description:
* _pid Kp_ - if used in PID mode, it's the proportional PID gain.