From 725627e93d6825110ed70242249df95e8b05f1e5 Mon Sep 17 00:00:00 2001 From: Jamie Leighton Date: Fri, 10 Jun 2016 14:14:51 +1000 Subject: [PATCH 1/6] V0.22.3.0 Fix Bug where KAC Alarms GUI window fails if there is a KAC alarm of type "Crew" --- Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt | 5 ++++- .../REPOSoftTech/DeepFreeze/DeepFreezeContinued.version | 2 +- Source/DFIntMemory.cs | 2 +- Source/DeepFreezeGUI.cs | 2 +- Source/Properties/AssemblyInfo.cs | 4 ++-- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt b/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt index 4f49a49..fc0a00d 100644 --- a/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt +++ b/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt @@ -1,4 +1,7 @@ -V0.22.2.0 "Bug Fixes" +V0.22.3.0 +Fix Bug where KAC Alarms GUI window fails if there is a KAC alarm of type "Crew" + +V0.22.2.0 "Bug Fixes" Fixed bug with Icons not loading on Linux. Efficiency clean-up on code modules (speed and memory use). Fix bug where DeepFreeze is not maintaining and updating Vessel Information for Vessels with Freezer parts, causing Exceptions in the log and vessels not diff --git a/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version b/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version index ce4b59f..10cc879 100644 --- a/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version +++ b/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version @@ -2,7 +2,7 @@ "NAME":"DeepFreeze Continued...", "URL":"http://ksp-avc.cybutek.net/version.php?id=183", "DOWNLOAD":"http://spacedock.info/mod/142/DeepFreeze%20Continued...", -"VERSION":{"MAJOR":0,"MINOR":22,"PATCH":2,"BUILD":0}, +"VERSION":{"MAJOR":0,"MINOR":22,"PATCH":3,"BUILD":0}, "KSP_VERSION":{"MAJOR":1,"MINOR":1,"PATCH":2}, "KSP_VERSION_MIN":{"MAJOR":1,"MINOR":1,"PATCH":2}, "KSP_VERSION_MAX":{"MAJOR":1,"MINOR":1,"PATCH":2} diff --git a/Source/DFIntMemory.cs b/Source/DFIntMemory.cs index 0516fa9..5aecc08 100644 --- a/Source/DFIntMemory.cs +++ b/Source/DFIntMemory.cs @@ -1251,7 +1251,7 @@ private void CheckKACAlarmsUpdate() { if (!DeepFreeze.Instance.DFgameSettings.knownKACAlarms.ContainsKey(entry.ID)) // So we don't already know about it { - if (entry.VesselID != string.Empty) + if (entry.VesselID != string.Empty && entry.AlarmType != KACWrapper.KACAPI.AlarmTypeEnum.Crew) { Guid tmpid = Guid.Empty; try diff --git a/Source/DeepFreezeGUI.cs b/Source/DeepFreezeGUI.cs index 117ed5a..31c194a 100644 --- a/Source/DeepFreezeGUI.cs +++ b/Source/DeepFreezeGUI.cs @@ -931,7 +931,7 @@ private void windowKAC(int id) foreach (KACWrapper.KACAPI.KACAlarm alarm in KACWrapper.KAC.Alarms) { //Only show KAC alarms that are in the DeepFreeze known vessels list. (IE: vessels that have a freezer) - if (alarm.VesselID == "") + if (alarm.VesselID == "" || alarm.AlarmType == KACWrapper.KACAPI.AlarmTypeEnum.Crew) { continue; } diff --git a/Source/Properties/AssemblyInfo.cs b/Source/Properties/AssemblyInfo.cs index d50958c..61501e8 100644 --- a/Source/Properties/AssemblyInfo.cs +++ b/Source/Properties/AssemblyInfo.cs @@ -31,6 +31,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.22.2.0")] -[assembly: AssemblyFileVersion("0.22.2.0")] +[assembly: AssemblyVersion("0.22.3.0")] +[assembly: AssemblyFileVersion("0.22.3.0")] [assembly: KSPAssembly("DeepFreeze", 0, 22)] \ No newline at end of file From 3932455b0bcd18014dda18fe1c80e2c33d00911e Mon Sep 17 00:00:00 2001 From: Jamie Leighton Date: Mon, 13 Jun 2016 09:27:12 +1000 Subject: [PATCH 2/6] Debug symlist on Unix --- Source/Textures.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Textures.cs b/Source/Textures.cs index 2932dc9..8d8655a 100644 --- a/Source/Textures.cs +++ b/Source/Textures.cs @@ -56,6 +56,9 @@ internal static void LoadIconAssets() { try { + Utilities.Log("DeepFreeze AssemblyFolder = " + DeepFreeze._AssemblyFolder); + Utilities.Log("PathIconsPath = " + PathIconsPath); + Utilities.Log("PathToolbarIconsPath = " + PathToolbarIconsPath); LoadImageFromFile(ref TooltipBox, "DFToolTipBox.png", PathIconsPath); LoadImageFromFile(ref BtnRedCross, "DFbtnRedCross.png", PathIconsPath); LoadImageFromFile(ref BtnResize, "DFbtnResize.png", PathIconsPath); From 8cd574d83f5670e9f01cb31cacd099f3c4940c76 Mon Sep 17 00:00:00 2001 From: Jamie Leighton Date: Fri, 17 Jun 2016 10:41:32 +1000 Subject: [PATCH 3/6] :lollipop: Added .gitattributes --- .gitattributes | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain From c2c0d2bea08460b07b4518c723252abdb98f3d97 Mon Sep 17 00:00:00 2001 From: Jamie Leighton Date: Fri, 17 Jun 2016 10:55:12 +1000 Subject: [PATCH 4/6] Initial Dev Commit split common KSP utilities to separate project --- AppLauncherToolBarExtension.cs | 355 ++++++++ LICENSE | 22 + README.md | 16 + RSTutilities.cs | 1400 ++++++++++++++++++++++++++++++++ RectExtentions.cs | 58 ++ ToolBarManager.cs | 876 ++++++++++++++++++++ 6 files changed, 2727 insertions(+) create mode 100644 AppLauncherToolBarExtension.cs create mode 100644 LICENSE create mode 100644 README.md create mode 100644 RSTutilities.cs create mode 100644 RectExtentions.cs create mode 100644 ToolBarManager.cs diff --git a/AppLauncherToolBarExtension.cs b/AppLauncherToolBarExtension.cs new file mode 100644 index 0000000..636f407 --- /dev/null +++ b/AppLauncherToolBarExtension.cs @@ -0,0 +1,355 @@ +/* + * AppLauncherToolBar.cs + * (C) Copyright 2016, Jamie Leighton (JPLRepo) + * REPOSoft Technologies + * Kerbal Space Program is Copyright (C) 2013 Squad. See http://kerbalspaceprogram.com/. This + * project is in no way associated with nor endorsed by Squad. + * + * This file is part of RST Utils. My attempt at creating my own KSP Mod base Architecture. + * + * RST Utils is free software: you can redistribute it and/or modify + * it under the terms of the MIT License + * + * RST Utils is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the MIT License + * along with RST Utils. If not, see . + * + */ +using System; +using KSP.UI.Screens; +using UnityEngine; + +namespace RSTUtils +{ + public class AppLauncherToolBar + { + public static AppLauncherToolBar Instance { get; private set; } + + private bool usingToolbar = false; //Set to true if user is using ToolBar + private IButton button1; //ToolBar button + private string toolBarName; //set to Name for button on ToolBar (modname) + private string toolBarToolTip; // set tooltip for ToolBar button + private string toolBarTexturePath; //The TooBar formatted Texture Path + private GameScenesVisibility toolBarGameScenes; //GameScenes toolbar button can be seen in + + private bool usingStock = false; //Set to true if user is using Stock AppLauncher + private ApplicationLauncherButton stockToolbarButton; // Stock Toolbar Button + private ApplicationLauncher.AppScenes VisibleinScenes; //What scenes is the applauncher button seen in + private UnityEngine.Texture appbtnTexON; //Texture for AppLauncher button when ON + private UnityEngine.Texture appbtnTexOFF; //Texture for AppLauncher button when OFF + private bool showHoverText = false; //Whether to show AppLauncher Hover Text or not. + + private bool _gamePaused; + public Boolean gamePaused + { + get { return _gamePaused; } + private set + { + _gamePaused = value; //Set the private variable + } + } + + private bool _hideUI; + public Boolean hideUI + { + get { return _hideUI; } + private set + { + _hideUI = value; //Set the private variable + } + } + + public bool StockButtonNotNull + { + get { return stockToolbarButton != null; } + } + + public bool ToolBarButtonNotNull + { + get { return button1 != null; } + } + + public bool usingToolBar + { + get { return usingToolbar; } + } + + public bool usingAppLauncher + { + get { return usingStock; } + } + + //GuiVisibility + private bool _Visible; + public Boolean GuiVisible + { + get { return _Visible; } + set + { + _Visible = value; //Set the private variable + } + } + + public bool ShowHoverText + { + get { return showHoverText; } + } + + private void GamePaused() + { + gamePaused = true; + } + + private void GameUnPaused() + { + gamePaused = false; + } + + private void onHideUI() + { + hideUI = true; + } + + private void onShowUI() + { + hideUI = false; + } + /// + /// Constructor for AppLauncherToolBar. You need to construct one of these for your Mod Menu/GUI environment. + /// + /// A string passed into ToolBar indicating the Name of the Mod + /// A string passed into ToolBar to use for the Icon ToolTip + /// A string in ToolBar expected format of the TexturePath for the ToolBarIcon + /// ApplicationLauncher.AppScenes list - logically OR'd + /// Texture reference for the AppLauncher ON Icon + /// Texture reference for the AppLauncher OFF Icon + /// A list of GameScenes use for ToolBar icon visiblity + public AppLauncherToolBar(string toolBarName, string toolBarToolTip, string toolBarTexturePath, + ApplicationLauncher.AppScenes VisibleinScenes, UnityEngine.Texture appbtnTexON, UnityEngine.Texture appbtnTexOFF, params GameScenes[] gameScenes) + { + Instance = this; + if (ToolbarManager.ToolbarAvailable) + { + this.toolBarName = toolBarName; + this.toolBarToolTip = toolBarToolTip; + this.toolBarTexturePath = toolBarTexturePath; + this.toolBarGameScenes = new GameScenesVisibility(gameScenes); + } + this.VisibleinScenes = VisibleinScenes; + this.appbtnTexON = appbtnTexON; + this.appbtnTexOFF = appbtnTexOFF; + } + + private void OnGUIAppLauncherReady() + { + Utilities.Log_Debug("OnGUIAppLauncherReady"); + if (ApplicationLauncher.Ready) + { + Utilities.Log_Debug("Adding AppLauncherButton"); + stockToolbarButton = ApplicationLauncher.Instance.AddModApplication( + onAppLaunchToggle, + onAppLaunchToggle, + onHoverOn, + onHoverOff, + DummyVoid, + DummyVoid, + VisibleinScenes, + appbtnTexOFF); + } + } + + private void DummyVoid() + { + } + + private void onHoverOn() + { + showHoverText = true; + } + private void onHoverOff() + { + showHoverText = false; + } + + public void onAppLaunchToggle() + { + GuiVisible = !GuiVisible; + if (stockToolbarButton != null) + { + stockToolbarButton.SetTexture(GuiVisible ? appbtnTexON : appbtnTexOFF); + } + } + + private void DestroyToolBar() + { + if (ToolbarManager.ToolbarAvailable) + { + if (button1 != null) + button1.Destroy(); + } + } + + private void CreateToolBar() + { + if (ToolbarManager.ToolbarAvailable) + { + button1 = ToolbarManager.Instance.add(toolBarName, "button1"); + button1.TexturePath = toolBarTexturePath; + button1.ToolTip = toolBarToolTip; + button1.Visibility = toolBarGameScenes; + button1.OnClick += e => GuiVisible = !GuiVisible; + } + } + + private void DestroyStockButton() + { + GameEvents.onGUIApplicationLauncherReady.Remove(OnGUIAppLauncherReady); + if (stockToolbarButton != null) + { + ApplicationLauncher.Instance.RemoveModApplication(stockToolbarButton); + stockToolbarButton = null; + } + } + + private void CreateStockButton() + { + Utilities.Log_Debug("Adding onGUIAppLauncher callbacks"); + if (ApplicationLauncher.Ready) + { + if (stockToolbarButton == null) + OnGUIAppLauncherReady(); + } + else + GameEvents.onGUIApplicationLauncherReady.Add(OnGUIAppLauncherReady); + } + + private void OnGameSceneLoadRequestedForAppLauncher(GameScenes SceneToLoad) + { + if (stockToolbarButton != null) + { + ApplicationLauncherButton[] lstButtons = UnityEngine.Object.FindObjectsOfType(); + Utilities.Log_Debug("TSTMenu AppLauncher: Destroying Button-Button Count:" + lstButtons.Length); + ApplicationLauncher.Instance.RemoveModApplication(stockToolbarButton); + stockToolbarButton = null; + } + } + + /// + /// This Class is not using MonoBehaviour but has a Start Method that must be called. + /// Call this in your Start Method for a Mod GUI/Menu Class. + /// + /// True if we are to use the Stock Applauncher, False to use ToolBar mod + public void Start(bool stock) + { + DestroyToolBar(); + if (ToolbarManager.ToolbarAvailable && !stock) + { + // Set up ToolBar button + CreateToolBar(); + usingToolbar = true; + usingStock = false; + } + else + { + // Set up the stock toolbar + CreateStockButton(); + usingToolbar = false; + usingStock = true; + } + GameEvents.onGameSceneLoadRequested.Add(OnGameSceneLoadRequestedForAppLauncher); + GameEvents.onGamePause.Add(GamePaused); + GameEvents.onGameUnpause.Add(GameUnPaused); + GameEvents.onHideUI.Add(onHideUI); + GameEvents.onShowUI.Add(onShowUI); + } + + /// + /// This Class is not using MonoBehaviour but has a Destroy Method that must be called. + /// Call this in your OnDestroy Method for a Mod GUI/Menu Class. + /// + public void Destroy() + { + DestroyToolBar(); + + // Stock toolbar + Utilities.Log_Debug("Removing onGUIAppLauncher callbacks"); + + DestroyStockButton(); + + if (GuiVisible) GuiVisible = !GuiVisible; + GameEvents.onGameSceneLoadRequested.Remove(OnGameSceneLoadRequestedForAppLauncher); + GameEvents.onGamePause.Remove(GamePaused); + GameEvents.onGameUnpause.Remove(GameUnPaused); + GameEvents.onHideUI.Remove(onHideUI); + GameEvents.onShowUI.Remove(onShowUI); + } + + /// + /// Sets the ToolBar Icon visible or not. To be extended in future to not require calling from Mod. + /// Currently it is because I haven't incorporated the mod's Setting for Whether the user wants to use Stock AppLauncher or Toolbar. + /// + /// True if set to visible, false will turn it off + public void setToolBarBtnVisibility(bool visible) + { + button1.Visible = visible; + } + + /// + /// Sets the Applauncher Icon visible or not. To be extended in future to not require calling from Mod. + /// Currently it is because I haven't incorporated the mod's Setting for Whether the user wants to use Stock AppLauncher or Toolbar. + /// + /// True if set to visible, false will turn it off + public void setAppLSceneVisibility(ApplicationLauncher.AppScenes visibleinScenes) + { + VisibleinScenes = visibleinScenes; + stockToolbarButton.VisibleInScenes = VisibleinScenes; + } + + /// + /// Call this to change from AppLauncher to Toobar or vice-versa. + /// Will Destroy ToolBar or AppLauncher Icon and create a new one. + /// + /// True if using AppLauncher, False if using ToolBar + public void chgAppIconStockToolBar(bool stock) + { + if (!stock && ToolbarManager.ToolbarAvailable) + { + DestroyStockButton(); + DestroyToolBar(); + CreateToolBar(); + usingToolbar = true; + usingStock = false; + + } + else + { + DestroyToolBar(); + DestroyStockButton(); + CreateStockButton(); + usingToolbar = false; + usingStock = true; + } + } + + /// + /// Change the ToolBar TexturePath - to say change the Icon + /// + /// string in ToolBar TexturePath format + public void setToolBarTexturePath(string icontoSet) + { + button1.TexturePath = icontoSet; + } + + /// + /// Change the AppLauncher Icon Texture - to say change the Icon + /// + /// Texture to set Icon to + public void setAppLauncherTexture(Texture icontoSet) + { + stockToolbarButton.SetTexture(icontoSet); + } + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1c821c9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The Createive Commons Attribution-NonCommercial-ShareAlike 4.0 International license (CC BY-NC-SA 4.0) + +Copyright (c) 2015 JPLRepo + +You are free to: + Share — copy and redistribute the material in any medium or format + Adapt — remix, transform, and build upon the material + The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. + NonCommercial — You may not use the material for commercial purposes. + ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. + No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits. + +Notices: + You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation. + No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material. + +Human Readable license: https://creativecommons.org/licenses/by-nc-sa/4.0/ +Legalcode: https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode + diff --git a/README.md b/README.md new file mode 100644 index 0000000..0efade9 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +REPOSoftTech Utilities +====================== + +This is a collection of common software utilities used throughout my Kerbal Space Program mods. + + +For support please refer to the KSP forum thread and raise an issue here on GitHub. + +License +========== +These Utilities are licensed under a Creative Commons 4.0 Share-Alike Attribution Non-Commercial license. + +Mod Contributors: +=========== +JPLRepo - Plugin design and development, C# coding, Implementation and releases. + diff --git a/RSTutilities.cs b/RSTutilities.cs new file mode 100644 index 0000000..aa63728 --- /dev/null +++ b/RSTutilities.cs @@ -0,0 +1,1400 @@ + + +using HighlightingSystem; +/** +* REPOSoftTech KSP Utilities +* (C) Copyright 2015, Jamie Leighton +* +* Kerbal Space Program is Copyright (C) 2013 Squad. See http://kerbalspaceprogram.com/. This +* project is in no way associated with nor endorsed by Squad. +* +* +* Licensed under the Attribution-NonCommercial-ShareAlike (CC BY-NC-SA 4.0) creative commons license. +* See for full details (except where else specified in this file). +* +*/ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using UnityEngine; +using Object = UnityEngine.Object; +using Random = System.Random; + +namespace RSTUtils +{ + public enum GameState + { + FLIGHT = 0, + EDITOR = 1, + EVA = 2, + SPACECENTER = 3, + OTHER = 4 + } + + internal static class Utilities + { + public static int randomSeed = new Random().Next(); + private static int _nextrandomInt = randomSeed; + + public static int getnextrandomInt() + { + _nextrandomInt ++; + return _nextrandomInt; + } + + private static GameState state; + + //Set the Game State mode indicator, 0 = inflight, 1 = editor, 2 on EVA or F2 + public static bool GameModeisFlight + { + get + { + state = SetModeFlag(); + if (state == GameState.FLIGHT) return true; + return false; + } + } + + public static bool GameModeisEditor + { + get + { + state = SetModeFlag(); + if (state == GameState.EDITOR) return true; + return false; + } + } + + public static bool GameModeisEVA + { + get + { + state = SetModeFlag(); + if (state == GameState.EVA) return true; + return false; + } + } + + public static bool GameModeisSpaceCenter + { + get + { + state = SetModeFlag(); + if (state == GameState.SPACECENTER) return true; + return false; + } + } + + public static GameState GameMode + { + get + { + return SetModeFlag(); + } + } + + public static GameState SetModeFlag() + { + //Set the mode flag, 0 = inflight, 1 = editor, 2 on EVA or F2 + if (HighLogic.LoadedScene == GameScenes.SPACECENTER) + { + return GameState.SPACECENTER; + } + //if (FlightGlobals.fetch != null && FlightGlobals.ActiveVessel != null) // Check if in flight + if (HighLogic.LoadedSceneIsFlight) + { + if (FlightGlobals.fetch != null) + { + if (FlightGlobals.ActiveVessel != null) + { + if (FlightGlobals.ActiveVessel.isEVA) // EVA kerbal + { + return GameState.EVA; + } + } + } + return GameState.FLIGHT; + } + if (EditorLogic.fetch != null) // Check if in editor + { + return GameState.EDITOR; + } + return GameState.OTHER; + } + + #region GeometryandSpace + //Geometry and space + + public static double DistanceFromHomeWorld(Vessel vessel) + { + Vector3d vslPos = vessel.GetWorldPos3D(); + CelestialBody HmePlanet = Planetarium.fetch.Home; + Log_Debug("Home = " + HmePlanet.name + " Pos = " + HmePlanet.position); + Log_Debug("Vessel Pos = " + vslPos); + Vector3d hmeplntPos = HmePlanet.position; + double DstFrmHome = Math.Sqrt(Math.Pow(vslPos.x - hmeplntPos.x, 2) + Math.Pow(vslPos.y - hmeplntPos.y, 2) + Math.Pow(vslPos.z - hmeplntPos.z, 2)); + Log_Debug("Distance from Home Planet = " + DstFrmHome); + return DstFrmHome; + } + + public static double DistanceFromHomeWorld(string bodyname) + { + CelestialBody body = FlightGlobals.Bodies.FirstOrDefault(a => a.name == bodyname); + if (body == null) body = Planetarium.fetch.Home; + Vector3d bodyPos = body.getPositionAtUT(0); + CelestialBody HmePlanet = Planetarium.fetch.Home; + Log_Debug("Home = " + HmePlanet.name + " Pos = " + HmePlanet.getPositionAtUT(0)); + Log_Debug("Body Pos = " + bodyPos); + Vector3d hmeplntPos = HmePlanet.getPositionAtUT(0); + double DstFrmHome = Math.Sqrt(Math.Pow(bodyPos.x - hmeplntPos.x, 2) + Math.Pow(bodyPos.y - hmeplntPos.y, 2) + Math.Pow(bodyPos.z - hmeplntPos.z, 2)); + Log_Debug("Distance from Home Planet = " + DstFrmHome); + return DstFrmHome; + } + + public static bool CelestialBodyDistancetoSun(CelestialBody cb, out Vector3d sun_dir, out double sun_dist) + { + // bodies traced against + CelestialBody sun = FlightGlobals.Bodies[0]; + if (cb == sun) //If we have passed in the sun as the cb we default to a distance of 700000Km + { + sun_dir = Vector3d.forward; + sun_dist = sun.Radius + 700000000; + sun_dir /= sun_dist; + return true; + } + sun_dir = sun.position - cb.position; + sun_dist = sun_dir.magnitude; + sun_dir /= sun_dist; + sun_dist -= sun.Radius; + return true; + } + + // return sun luminosity + public static double SolarLuminosity + { + get + { + // note: it is 0 before loading first vessel in a game session, we compute it in that case + if (PhysicsGlobals.SolarLuminosity <= double.Epsilon) + { + double A = FlightGlobals.GetHomeBody().orbit.semiMajorAxis; + return A * A * 12.566370614359172 * PhysicsGlobals.SolarLuminosityAtHome; + } + return PhysicsGlobals.SolarLuminosity; + } + } + + #endregion GeometryandSpace + + #region ObjectsandTransforms + public static void PrintTransform(Transform t, string title = "") + { + Log_Debug("------" + title + "------"); + Log_Debug("Position: " + t.localPosition); + Log_Debug("Rotation: " + t.localRotation); + Log_Debug("Scale: " + t.localScale); + Log_Debug("------------------"); + } + + public static void DumpObjectProperties(object o, string title = "---------") + { + // Iterate through all of the properties + Log_Debug("--------- " + title + " ------------"); + foreach (PropertyInfo property in o.GetType().GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public)) + { + if (property.CanRead) + Log_Debug(property.Name + " = " + property.GetValue(o, null)); + } + Log_Debug("--------------------------------------"); + } + + // Dump an object by reflection + internal static void DumpObjectFields(object o, string title = "---------") + { + // Dump (by reflection) + Debug.Log("---------" + title + "------------"); + foreach (FieldInfo field in o.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public)) + { + if (!field.IsStatic) + { + Debug.Log(field.Name + " = " + field.GetValue(o)); + } + } + Debug.Log("--------------------------------------"); + } + + // Use Reflection to get a field from an object + internal static object GetObjectField(object o, string fieldName) + { + object outputObj = new object(); + bool foundObj = false; + foreach (FieldInfo field in o.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public)) + { + if (!field.IsStatic) + { + if (field.Name == fieldName) + { + foundObj = true; + outputObj = field.GetValue(o); + break; + } + } + } + if (foundObj) + { + return outputObj; + } + return null; + } + + /** + * Recursively searches for a named transform in the Transform heirarchy. The requirement of + * such a function is sad. This should really be in the Unity3D API. Transform.Find() only + * searches in the immediate children. + * + * @param transform Transform in which is search for named child + * @param name Name of child to find + * + * @return Desired transform or null if it could not be found + */ + + internal static Transform FindInChildren(Transform transform, string name) + { + // Is this null? + if (transform == null) + { + return null; + } + + // Are the names equivalent + if (transform.name == name) + { + return transform; + } + + // If we did not find a transform, search through the children + return (from Transform child in transform select FindInChildren(child, name)).FirstOrDefault(t => t != null); + + // Return the transform (will be null if it was not found) + } + + public static Transform FindChildRecursive(Transform parent, string name) + { + return parent.gameObject.GetComponentsInChildren().FirstOrDefault(t => t.name == name); + } + + public static Animation FindAnimChildRecursive(Transform parent, string name) + { + return parent.gameObject.GetComponentsInChildren().FirstOrDefault(t => t.name == name); + } + + internal static void dmpKerbalRefs(Kerbal kerbal, Kerbal seatkerbalref, Kerbal protocrewkerbalref) + { + if (kerbal != null) + { + Log_Debug("kerbal " + kerbal.name + " " + kerbal.GetInstanceID()); + Log_Debug(kerbal.GetComponent("TRIvaModule") != null + ? "kerbal has TRIvaModule attached" + : "kerbal DOES NOT have TRIvaModule attached"); + } + + if (seatkerbalref != null) + { + Log_Debug("seatkerbalref " + seatkerbalref.name + " " + seatkerbalref.GetInstanceID()); + Log_Debug(seatkerbalref.GetComponent("TRIvaModule") != null + ? "seatkerbalref has TRIvaModule attached" + : "seatkerbalref DOES NOT have TRIvaModule attached"); + } + if (protocrewkerbalref != null) + { + Log_Debug("protocrewkerbalref " + protocrewkerbalref.name + " " + protocrewkerbalref.GetInstanceID()); + Log_Debug(protocrewkerbalref.GetComponent("TRIvaModule") != null + ? "protocrewkerbalref has TRIvaModule attached" + : "protocrewkerbalref DOES NOT have TRIvaModule attached"); + } + } + + internal static void dmpAllKerbals() + { + foreach (Kerbal kerbal in Resources.FindObjectsOfTypeAll()) + { + Log_Debug("Kerbal " + kerbal.name + " " + kerbal.crewMemberName + " instance " + kerbal.GetInstanceID() + " rosterstatus " + kerbal.rosterStatus); + Log_Debug(kerbal.protoCrewMember == null ? "ProtoCrewmember is null " : "ProtoCrewmember exists " + kerbal.protoCrewMember.name); + } + } + + internal static void dmpAnimationNames(Animation anim) + { + List states = new List(anim.Cast()); + Log_Debug("Animation " + anim.name); + foreach (AnimationState state in states) + { + Log_Debug("Animation clip " + state.name); + } + } + + // The following method is modified from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + internal static void setTransparentTransforms(this Part thisPart, string transparentTransforms) + { + string transparentShaderName = "Transparent/Specular"; + var transparentShader = Shader.Find(transparentShaderName); + foreach (string transformName in transparentTransforms.Split('|')) + { + Log_Debug("setTransparentTransforms " + transformName); + try + { + Transform tr = thisPart.FindModelTransform(transformName.Trim()); + if (tr != null) + { + // We both change the shader and backup the original shader so we can undo it later. + Shader backupShader = tr.GetComponent().material.shader; + tr.GetComponent().material.shader = transparentShader; + } + } + catch (Exception e) + { + Debug.Log("Unable to set transparent shader transform " + transformName); + Debug.LogException(e); + } + } + } + + #endregion ObjectsandTransforms + + #region Cameras + + internal static Camera FindCamera(string name) + { + return Camera.allCameras.FirstOrDefault(c => c.name == name); + } + + // Dump all Unity Cameras + internal static void DumpCameras() + { + // Dump (by reflection) + Debug.Log("--------- Dump Unity Cameras ------------"); + foreach (Camera c in Camera.allCameras) + { + Debug.Log("Camera " + c.name + " cullingmask " + c.cullingMask + " depth " + c.depth + " farClipPlane " + c.farClipPlane + " nearClipPlane " + c.nearClipPlane); + } + + Debug.Log("--------------------------------------"); + } + + public static Camera findCameraByName(string camera) + { + return Camera.allCameras.FirstOrDefault(cam => cam.name == camera); + } + + private static Camera StockOverlayCamera; + /// + /// Returns True if the Stock Overlay Camera Mode is on, otherwise will return false. + /// + public static bool StockOverlayCamIsOn + { + get + { + StockOverlayCamera = findCameraByName("InternalSpaceOverlay Host"); + if (StockOverlayCamera != null) return true; + return false; + } + } + + private static Shader DepthMaskShader; + private static string DepthMaskShaderName = "DepthMask"; + /// + /// Will search for and change the Mesh (and all it's children) supplied in MeshName Field on the part supplied to Enabled or NotEnabled based on the SetVisible parm. + /// + /// The part to look for the mesh on + /// True will Enable the mesh, False will disable the mesh + /// String containing the Mesh name to look for on the part + internal static void SetInternalDepthMask(Part part, bool SetVisible, string MeshName = "") + { + if (DepthMaskShader == null) DepthMaskShader = Shader.Find(DepthMaskShaderName); + if (part.internalModel != null) + { + if (MeshName != "") + { + Transform parentTransform = FindInChildren(part.internalModel.transform, MeshName); + if (parentTransform != null) + { + parentTransform.gameObject.SetActive(SetVisible); + } + } + } + } + + #endregion Cameras + + #region Animations + public static IEnumerator WaitForAnimation(Animation animation, string name) + { + do + { + yield return null; + } while (animation.IsPlaying(name)); + } + + public static IEnumerator WaitForAnimationNoClip(Animation animation) + { + do + { + yield return null; + } while (animation.isPlaying); + } + + #endregion Animations + + #region Kerbals + + // The following method is derived from TextureReplacer mod. Which is licensed as: + //Copyright © 2013-2015 Davorin Učakar, Ryan Bray + //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + //The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + private static double atmSuitPressure = 50.0; + + internal static bool isAtmBreathable() + { + bool value = !HighLogic.LoadedSceneIsFlight + || (FlightGlobals.getStaticPressure() >= atmSuitPressure); + Log_Debug("isATMBreathable Inflight? " + value + " InFlight " + HighLogic.LoadedSceneIsFlight + " StaticPressure " + FlightGlobals.getStaticPressure()); + return value; + } + + // The following method is derived from TextureReplacer mod. Which is licensed as: + //Copyright © 2013-2015 Davorin Učakar, Ryan Bray + //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + //The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + private static Mesh[] helmetMesh = { null, null }; + + private static Mesh[] visorMesh = { null, null }; + private static bool helmetMeshstored; + + internal static void storeHelmetMesh() + { + Log_Debug("StoreHelmetMesh"); + foreach (Kerbal kerbal in Resources.FindObjectsOfTypeAll()) + { + int gender = kerbal.transform.name == "kerbalFemale" ? 1 : 0; + // Save pointer to helmet & visor meshes so helmet removal can restore them. + foreach (SkinnedMeshRenderer smr in kerbal.GetComponentsInChildren(true)) + { + if (smr.name.EndsWith("helmet", StringComparison.Ordinal)) + helmetMesh[gender] = smr.sharedMesh; + else if (smr.name.EndsWith("visor", StringComparison.Ordinal)) + visorMesh[gender] = smr.sharedMesh; + } + } + helmetMeshstored = true; + } + + // The following method is derived from TextureReplacer mod.Which is licensed as: + //Copyright © 2013-2015 Davorin Učakar, Ryan Bray + //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + //The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + internal static void setHelmetshaders(Kerbal thatKerbal, bool helmetOn) + { + if (!helmetMeshstored) + storeHelmetMesh(); + + //This will check if Atmospher is breathable then we always remove our hetmets regardless. + if (helmetOn && isAtmBreathable()) + { + helmetOn = false; + Log_Debug("setHelmetShaders to put on helmet but in breathable atmosphere"); + } + + try + { + foreach (SkinnedMeshRenderer smr in thatKerbal.helmetTransform.GetComponentsInChildren()) + { + if (smr.name.EndsWith("helmet", StringComparison.Ordinal)) + smr.sharedMesh = helmetOn ? helmetMesh[(int)thatKerbal.protoCrewMember.gender] : null; + else if (smr.name.EndsWith("visor", StringComparison.Ordinal)) + smr.sharedMesh = helmetOn ? visorMesh[(int)thatKerbal.protoCrewMember.gender] : null; + } + } + catch (Exception ex) + { + Log("Error attempting to setHelmetshaders for " + thatKerbal.name + " to " + helmetOn); + Log(ex.Message); + } + } + + // The following method is derived from TextureReplacer mod. Which is licensed as: + //Copyright © 2013-2015 Davorin Učakar, Ryan Bray + //Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + //The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + internal static void setHelmets(this Part thisPart, bool helmetOn) + { + if (thisPart.internalModel == null) + { + Log_Debug("setHelmets but no internalModel"); + return; + } + + if (!helmetMeshstored) + storeHelmetMesh(); + + Log_Debug("setHelmets helmetOn=" + helmetOn); + //Kerbal thatKerbal = null; + foreach (InternalSeat thatSeat in thisPart.internalModel.seats) + { + if (thatSeat.crew != null) + { + Kerbal thatKerbal = thatSeat.kerbalRef; + if (thatKerbal != null) + { + thatSeat.allowCrewHelmet = helmetOn; + Log_Debug("Setting helmet=" + helmetOn + " for kerbal " + thatSeat.crew.name); + // `Kerbal.ShowHelmet(false)` irreversibly removes a helmet while + // `Kerbal.ShowHelmet(true)` has no effect at all. We need the following workaround. + // I think this can be done using a coroutine to despawn and spawn the internalseat crewmember kerbalref. + // But I found this workaround in TextureReplacer so easier to use that. + //if (thatKerbal.showHelmet) + //{ + setHelmetshaders(thatKerbal, helmetOn); + //} + //else + // Log_Debug("Showhelmet is OFF so the helmettransform does not exist"); + } + else + Log_Debug("kerbalref = null?"); + } + } + } + + // Sets the kerbal layers to make them visible (Thawed) or not (Frozen), setVisible = true sets layers to visible, false turns them off. + internal static void setFrznKerbalLayer(Part part, ProtoCrewMember kerbal, bool setVisible) + { + if (!setVisible) + { + kerbal.KerbalRef.SetVisibleInPortrait(setVisible); + kerbal.KerbalRef.InPart = null; + } + + kerbal.KerbalRef.gameObject.SetActive(setVisible); + if (setVisible) + { + kerbal.KerbalRef.SetVisibleInPortrait(setVisible); + kerbal.KerbalRef.InPart = part; + } + + } + + private static RuntimeAnimatorController kerbalIVAController, myController; + private static AnimatorOverrideController myOverrideController; + + internal static void subdueIVAKerbalAnimations(Kerbal kerbal) + { + try + { + foreach (Animator anim in kerbal.gameObject.GetComponentsInChildren()) + { + if (anim.name == kerbal.name) + { + kerbalIVAController = anim.runtimeAnimatorController; + myController = anim.runtimeAnimatorController; + myOverrideController = new AnimatorOverrideController(); + myOverrideController.runtimeAnimatorController = myController; + myOverrideController["idle_animA_upWord"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animB"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animC"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animD_dance"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animE_drummingHelmet"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animI_drummingControls"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animJ_yo"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animJ_IdleLoopShort"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["idle_animK_footStretch"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["head_rotation_staringUp"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["head_rotation_longLookUp"] = myOverrideController["idle_animH_notDoingAnything"]; + myOverrideController["head_faceExp_fun_ohAh"] = myOverrideController["idle_animH_notDoingAnything"]; + // Put this line at the end because when you assign a controller on an Animator, unity rebinds all the animated properties + anim.runtimeAnimatorController = myOverrideController; + Log_Debug("Animator " + anim.name + " for " + kerbal.name + " subdued"); + } + } + } + catch (Exception ex) + { + Log(" failed to subdue IVA animations for " + kerbal.name); + Debug.LogException(ex); + } + } + + internal static void reinvigerateIVAKerbalAnimations(Kerbal kerbal) + { + foreach (Animator anim in kerbal.gameObject.GetComponentsInChildren()) + { + if (anim.name == kerbal.name) + { + myController = kerbalIVAController; + myOverrideController = new AnimatorOverrideController(); + myOverrideController.runtimeAnimatorController = myController; + // Put this line at the end because when you assign a controller on an Animator, unity rebinds all the animated properties + anim.runtimeAnimatorController = myOverrideController; + Log_Debug("Animator " + anim.name + " for " + kerbal.name + " reinvigerated"); + } + } + } + + #endregion Kerbals + + #region Vessels + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + /// + /// Returns True if thatVessel is the activevessel and the camera is in IVA mode, otherwise returns false. + /// + /// + /// + internal static bool VesselIsInIVA(Vessel thatVessel) + { + // Inactive IVAs are renderer.enabled = false, this can and should be used... + // ... but now it can't because we're doing transparent pods, so we need a more complicated way to find which pod the player is in. + return HighLogic.LoadedSceneIsFlight && IsActiveVessel(thatVessel) && IsInIVA; + } + + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + /// + /// Returns True if thatVessel is the ActiveVessel, otherwise returns false. + /// + /// + /// + internal static bool IsActiveVessel(Vessel thatVessel) + { + return HighLogic.LoadedSceneIsFlight && thatVessel != null && thatVessel.isActiveVessel; + } + + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + public static bool UserIsInPod(Part thisPart) + { + + // Just in case, check for whether we're not in flight. + if (!HighLogic.LoadedSceneIsFlight) + return false; + + // If we're not in IVA, or the part does not have an instantiated IVA, the user can't be in it. + if (!VesselIsInIVA(thisPart.vessel) || thisPart.internalModel == null) + return false; + + // Now that we got that out of the way, we know that the user is in SOME pod on our ship. We just don't know which. + // Let's see if he's controlling a kerbal in our pod. + if (ActiveKerbalIsLocal(thisPart)) + return true; + + // There still remains an option of InternalCamera which we will now sort out. + if (CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.Internal) + { + // So we're watching through an InternalCamera. Which doesn't record which pod we're in anywhere, like with kerbals. + // But we know that if the camera's transform parent is somewhere in our pod, it's us. + // InternalCamera.Instance.transform.parent is the transform the camera is attached to that is on either a prop or the internal itself. + // The problem is figuring out if it's in our pod, or in an identical other pod. + // Unfortunately I don't have anything smarter right now than get a list of all transforms in the internal and cycle through it. + // This is a more annoying computation than looking through every kerbal in a pod (there's only a few of those, + // but potentially hundreds of transforms) and might not even be working as I expect. It needs testing. + return thisPart.internalModel.GetComponentsInChildren().Any(thisTransform => thisTransform == InternalCamera.Instance.transform.parent); + } + + return false; + } + + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + public static bool ActiveKerbalIsLocal(this Part thisPart) + { + return FindCurrentKerbal(thisPart) != null; + } + + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + public static Kerbal FindCurrentKerbal(this Part thisPart) + { + if (thisPart.internalModel == null || !VesselIsInIVA(thisPart.vessel)) + return null; + // InternalCamera instance does not contain a reference to the kerbal it's looking from. + // So we have to search through all of them... + return (from thatSeat in thisPart.internalModel.seats + where thatSeat.kerbalRef != null + where thatSeat.kerbalRef.eyeTransform == InternalCamera.Instance.transform.parent + select thatSeat.kerbalRef).FirstOrDefault(); + } + + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + /// + /// True if Camera is in IVA mode, otherwise false. + /// + internal static bool IsInIVA + { + get { return CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.IVA; } + } + /// + /// True if Camera is in Internal mode, otherwise false. + /// + internal static bool IsInInternal + { + get { return CameraManager.Instance.currentCameraMode == CameraManager.CameraMode.Internal; } + } + + internal static bool ValidVslType(Vessel v) + { + switch (v.vesselType) + { + case VesselType.Base: + case VesselType.Lander: + case VesselType.Probe: + case VesselType.Rover: + case VesselType.Ship: + case VesselType.Station: + return true; + + default: + return false; + } + } + + // The following method is taken from Kerbal Alarm Clock as-is. Which is covered by MIT license. + internal static int getVesselIdx(Vessel vtarget) + { + for (int i = 0; i < FlightGlobals.Vessels.Count; i++) + { + if (FlightGlobals.Vessels[i].id == vtarget.id) + { + Log_Debug("Found Target idx=" + i + " (" + vtarget.id + ")"); + return i; + } + } + return -1; + } + + /// + /// Will Spawn the Internal Model for a part, we do this for DeepFreeze Mod because it doesn't work if the crew capacity is zero, which may be + /// the case sometimes for DeepFreeze parts. + /// + /// The Part to spawn the internal model for + /// True if successful or False if not + internal static bool spawnInternal(Part part) + { + try + { + if (part.internalModel != null) return true; + part.CreateInternalModel(); + if (part.internalModel != null) + { + part.internalModel.Initialize(part); + part.internalModel.SpawnCrew(); + } + else + { + return false; + } + + return true; + } + catch (Exception) + { + return false; + } + } + + + public static void PartHighlight(Part part, bool on) + { + if (on) + { + if (part.highlighter == null) + { + var color = XKCDColors.Yellow; + var model = part.FindModelTransform("model"); + part.highlighter = model.gameObject.AddComponent(); + part.highlighter.ConstantOn(color); + part.SetHighlightColor(color); + part.SetHighlight(true, false); + } + } + else + { + if (part.highlighter != null) + { + part.SetHighlightDefault(); + part.highlighter.gameObject.DestroyGameObjectImmediate(); + part.highlighter = null; + } + } + } + + #endregion Vessels + + #region Temperature + //Temperature + internal static float KelvintoCelsius(float kelvin) + { + return kelvin - 273.15f; + } + + internal static float CelsiustoKelvin(float celsius) + { + return celsius + 273.15f; + } + + #endregion Temperature + + #region Resources + + private static List resources; + //Resources + public static double GetAvailableResource(Part part, String resourceName) + { + resources = new List(); + part.GetConnectedResources(PartResourceLibrary.Instance.GetDefinition(resourceName).id, ResourceFlowMode.ALL_VESSEL, resources); + return resources.Sum(pr => pr.amount); + } + + public const int MAX_TRANSFER_ATTEMPTS = 4; + + private static double totalReceived; + private static double requestAmount; + private static double received; + public static double RequestResource(Part cvp, String name, double amount) + { + if (amount <= 0.0) + return 0.0; + totalReceived = 0.0; + requestAmount = amount; + for (int attempts = 0; (attempts < MAX_TRANSFER_ATTEMPTS) && (amount > 0.000000000001); attempts++) + { + received = cvp.RequestResource(name, requestAmount, ResourceFlowMode.ALL_VESSEL); + //Log_Debug("requestResource attempt " + attempts); + //Log_Debug("requested power = " + requestAmount.ToString("0.0000000000000000000000")); + //Log_Debug("received power = " + received.ToString("0.0000000000000000000000")); + totalReceived += received; + amount -= received; + //Log_Debug("amount = " + amount.ToString("0.0000000000000000000000")); + if (received <= 0.0) + requestAmount = amount * 0.5; + else + requestAmount = amount; + } + return totalReceived; + } + + #endregion Resources + + #region GUI&Window + // GUI & Window Methods + + public static int scaledScreenHeight = 1; + public static int scaledScreenWidth = 1; + private static bool scaledScreenset; + + internal static void setScaledScreen() + { + scaledScreenHeight = Mathf.RoundToInt(Screen.height / 1); + scaledScreenWidth = Mathf.RoundToInt(Screen.width / 1); + scaledScreenset = true; + } + + internal static RectOffset SetRectOffset(RectOffset tmpRectOffset, int intValue) + { + return SetRectOffset(tmpRectOffset, intValue, intValue, intValue, intValue); + } + + internal static RectOffset SetRectOffset(RectOffset tmpRectOffset, int Left, int Right, int Top, int Bottom) + { + tmpRectOffset.left = Left; + tmpRectOffset.top = Top; + tmpRectOffset.right = Right; + tmpRectOffset.bottom = Bottom; + return tmpRectOffset; + } + + //Tooltip variables + //Store the tooltip text from throughout the code + internal static String strToolTipText = ""; + internal static String strLastTooltipText = ""; + //is it displayed and where + internal static Boolean blnToolTipDisplayed; + internal static Rect rectToolTipPosition; + internal static Int32 intTooltipVertOffset = 12; + internal static Int32 intTooltipMaxWidth = 250; + //timer so it only displays for a period of time + internal static float fltTooltipTime; + internal static float fltMaxToolTipTime = 15f; + internal static GUIStyle _TooltipStyle; + + // The following two methods are derived from Kerbal Alarm Clock mod. Which is licensed under: + //The MIT License(MIT) Copyright(c) 2014, David Tregoning + // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the "Software"), to deal + // in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + internal static void DrawToolTip() + { + if (strToolTipText != "" && (fltTooltipTime < fltMaxToolTipTime)) + { + GUIContent contTooltip = new GUIContent(strToolTipText); + if (!blnToolTipDisplayed || (strToolTipText != strLastTooltipText)) + { + //reset display time if text changed + fltTooltipTime = 0f; + //Calc the size of the Tooltip + rectToolTipPosition = new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y + intTooltipVertOffset, 0, 0); + float minwidth, maxwidth; + if (_TooltipStyle == null) + { + Log("Missing _TooltipStyle definition, cannot draw tooltips"); + return; + } + _TooltipStyle.CalcMinMaxWidth(contTooltip, out minwidth, out maxwidth); // figure out how wide one line would be + rectToolTipPosition.width = Math.Min(intTooltipMaxWidth - _TooltipStyle.padding.horizontal, maxwidth); //then work out the height with a max width + rectToolTipPosition.height = _TooltipStyle.CalcHeight(contTooltip, rectToolTipPosition.width); // heers the result + //Make sure its not off the right of the screen + if (rectToolTipPosition.x + rectToolTipPosition.width > Screen.width) rectToolTipPosition.x = Screen.width - rectToolTipPosition.width; + } + //Draw the Tooltip + GUI.Label(rectToolTipPosition, contTooltip, _TooltipStyle); + //On top of everything + GUI.depth = 0; + + //update how long the tip has been on the screen and reset the flags + fltTooltipTime += Time.deltaTime; + blnToolTipDisplayed = true; + } + else + { + //clear the flags + blnToolTipDisplayed = false; + } + if (strToolTipText != strLastTooltipText) fltTooltipTime = 0f; + strLastTooltipText = strToolTipText; + } + + internal static void SetTooltipText() + { + if (Event.current.type == EventType.Repaint) + { + strToolTipText = GUI.tooltip; + } + } + + // The following method is taken from RasterPropMonitor as-is. Which is covered by GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + public static string WordWrap(string text, int maxLineLength) + { + var sb = new StringBuilder(); + char[] prc = { ' ', ',', '.', '?', '!', ':', ';', '-' }; + char[] ws = { ' ' }; + + foreach (string line in text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + { + int currentIndex; + int lastWrap = 0; + do + { + currentIndex = lastWrap + maxLineLength > line.Length ? line.Length : line.LastIndexOfAny(prc, Math.Min(line.Length - 1, lastWrap + maxLineLength)) + 1; + if (currentIndex <= lastWrap) + currentIndex = Math.Min(lastWrap + maxLineLength, line.Length); + sb.AppendLine(line.Substring(lastWrap, currentIndex - lastWrap).Trim(ws)); + lastWrap = currentIndex; + } while (currentIndex < line.Length); + } + return sb.ToString(); + } + + /// + /// Displays a horizontal list of toggles and returns the index of the selected item. + /// all you have to do is check items[selected] to see what is selected. + /// + public static int ToggleList(int selected, GUIContent[] items, GUIStyle[] styles, float width) + { + // Keep the selected index within the bounds of the items array + selected = selected < 0 ? 0 : selected >= items.Length ? items.Length - 1 : selected; + + GUILayout.BeginHorizontal(); + for (int i = 0; i < items.Length; i++) + { + // Display toggle. Get if toggle changed. + bool change = GUILayout.Toggle(selected == i, items[i], styles[i], GUILayout.Width(width)); + // If changed, set selected to current index. + if (change) + selected = i; + } + GUILayout.EndHorizontal(); + + // Return the currently selected item's index + return selected; + } + + //Returns True if the PauseMenu is open. Because the GameEvent callbacks don't work on the mainmenu. + internal static bool isPauseMenuOpen + { + get + { + try + { + + return PauseMenu.isOpen; + } + catch(Exception) + { + return false; + } + } + } + /// + ///Will delete Screen Messages. If you pass in messagetext it will only delete messages that contain that text string. + ///If you pass in a messagearea it will only delete messages in that area. Values are: UC,UL,UR,LC,ALL + /// + /// Specify a string that is part of a message that you want to remove, or pass in empty string to delete all messages + /// Specify a string representing the message area of the screen that you want messages removed from, + /// or pass in "ALL" string to delete from all message areas. + /// messagearea accepts the values of "UC" - UpperCenter, "UL" - UpperLeft, "UR" - UpperRight, "LC" - LowerCenter, "ALL" - All Message Areas + internal static void DeleteScreenMessages(string messagetext, string messagearea) + { + //Get the ScreenMessages Instance + var messages = ScreenMessages.Instance; + List messagetexts = new List(); + //Get the message Area messages based on the value of messagearea parameter. + switch (messagearea) + { + case "UC": + messagetexts = messages.upperCenter.gameObject.GetComponentsInChildren().ToList(); + break; + case "UL": + messagetexts = messages.upperLeft.gameObject.GetComponentsInChildren().ToList(); + break; + case "UR": + messagetexts = messages.upperRight.gameObject.GetComponentsInChildren().ToList(); + break; + case "LC": + messagetexts = messages.lowerCenter.gameObject.GetComponentsInChildren().ToList(); + break; + case "ALL": + messagetexts = messages.gameObject.GetComponentsInChildren().ToList(); + break; + } + //Loop through all the mesages we found. + List activemessagelist = ScreenMessages.Instance.ActiveMessages; + foreach (var msgtext in messagetexts) + { + //If the user specified text to search for only delete messages that contain that text. + if (messagetext != "") + { + if (msgtext != null && msgtext.text.text.Contains(messagetext)) + { + Object.Destroy(msgtext.gameObject); + } + } + else //If the user did not specific a message text to search for we DELETE ALL messages!! + { + Object.Destroy(msgtext.gameObject); + } + } + } + + #endregion GUI&Window + + #region ConfigNodes + // Get Config Node Values out of a config node Methods + + internal static Guid GetNodeValue(ConfigNode confignode, string fieldname) + { + if (confignode.HasValue(fieldname)) + { + try + { + Guid id = new Guid(confignode.GetValue(fieldname)); + return id; + } + catch (Exception ex) + { + Debug.Log("Unable to getNodeValue " + fieldname + " from " + confignode); + Debug.Log("Err: " + ex); + return Guid.Empty; + } + } + return Guid.Empty; + } + + internal static T GetNodeValue(ConfigNode confignode, string fieldname, T defaultValue) where T : IComparable, IFormattable, IConvertible + { + if (confignode.HasValue(fieldname)) + { + string stringValue = confignode.GetValue(fieldname); + if (Enum.IsDefined(typeof(T), stringValue)) + { + return (T)Enum.Parse(typeof(T), stringValue); + } + } + return defaultValue; + } + #endregion ConfigNodes + + #region Time + //Formatting time functions + + private static int y, d, h, m; + private static List parts = new List(); + //Format a Time double variable into format "xxxx:year xxxx:days xxxx:hours xxxx:mins x:xx:secs" + //Future expansion required to format to different formats. + public static String formatTime(double seconds) + { + y = (int)(seconds / (6.0 * 60.0 * 60.0 * 426.08)); + seconds = seconds % (6.0 * 60.0 * 60.0 * 426.08); + d = (int)(seconds / (6.0 * 60.0 * 60.0)); + seconds = seconds % (6.0 * 60.0 * 60.0); + h = (int)(seconds / (60.0 * 60.0)); + seconds = seconds % (60.0 * 60.0); + m = (int)(seconds / 60.0); + seconds = seconds % 60.0; + + //List parts = new List(); + parts.Clear(); + + if (y > 0) + { + parts.Add(String.Format("{0}:year ", y)); + } + + if (d > 0) + { + parts.Add(String.Format("{0}:days ", d)); + } + + if (h > 0) + { + parts.Add(String.Format("{0}:hours ", h)); + } + + if (m > 0) + { + parts.Add(String.Format("{0}:mins ", m)); + } + + if (seconds > 0) + { + parts.Add(String.Format("{0:00}:secs ", seconds)); + } + + if (parts.Count > 0) + { + return String.Join(" ", parts.ToArray()); + } + return "0s"; + } + + private static string outputstring; + private static int[] datestructure = new int[5]; + //Format a Time double variable into format "YxxxxDxxxhh:mm:ss" + //Future expansion required to format to different formats. + internal static string FormatDateString(double time) + { + outputstring = String.Empty; + //int[] datestructure = new int[5]; + if (GameSettings.KERBIN_TIME) + { + datestructure[0] = (int)time / 60 / 60 / 6 / 426; // Years + datestructure[1] = (int)time / 60 / 60 / 6 % 426; // Days + datestructure[2] = (int)time / 60 / 60 % 6; // Hours + datestructure[3] = (int)time / 60 % 60; // Minutes + datestructure[4] = (int)time % 60; //seconds + } + else + { + datestructure[0] = (int)time / 60 / 60 / 24 / 365; // Years + datestructure[1] = (int)time / 60 / 60 / 24 % 365; // Days + datestructure[2] = (int)time / 60 / 60 % 24; // Hours + datestructure[3] = (int)time / 60 % 60; // Minutes + datestructure[4] = (int)time % 60; //seconds + } + if (datestructure[0] > 0) + outputstring += "Y" + datestructure[0].ToString("####") + ":"; + if (datestructure[1] > 0) + outputstring += "D" + datestructure[1].ToString("###") + ":"; + outputstring += datestructure[2].ToString("00:"); + outputstring += datestructure[3].ToString("00:"); + outputstring += datestructure[4].ToString("00"); + return outputstring; + } + + // Electricity and temperature functions are only valid if timewarp factor is < 5. + internal static bool timewarpIsValid(int max) + { + return TimeWarp.CurrentRateIndex < max; + } + + internal static void stopWarp() + { + TimeWarp.SetRate(0, false); + } + + #endregion Time + + #region Strings + /// + /// Removes a String A from String B. + /// + internal static string RemoveSubStr(string B, string A) + { + StringBuilder b = new StringBuilder(B); + b.Replace(A, String.Empty); + return b.ToString(); + } + + public enum ISRUStatus + { + Inactive, + Active, + MissingResource, + OutputFull + } + + private static ISRUStatus returnStatus; + /// + /// Returns a Status Indicating the Status of a ISRU ModuleResourceConverter, given that it's actual status can be active, but not actually doing anything. + /// + internal static ISRUStatus GetModResConverterStatus(ModuleResourceConverter tmpRegRc) + { + returnStatus = ISRUStatus.Inactive; + if (!tmpRegRc.IsActivated) return ISRUStatus.Inactive; //If it's not Activated, it must be inactive. + // Otherwise it's Activated, but is it really working and using EC? Get it's real status. + if (tmpRegRc.status.ToLower().Contains("inactive")) returnStatus = ISRUStatus.Inactive; //Status is inactive, it's inactive.. Not sure how but sometimes this remains on load even when it's inactive? Hence the test above. + if (tmpRegRc.status.ToLower().Contains("missing")) returnStatus = ISRUStatus.MissingResource; //Missing an Input resource makes this appear in the status. + if (tmpRegRc.status.ToLower().Contains("full")) returnStatus = ISRUStatus.OutputFull; //If the vessel has nowhere to store the output, full appears in the status. + if (tmpRegRc.status.ToLower().Contains("load")) returnStatus = ISRUStatus.Active; //a Percentage Load indicates it is active and actually processing... except when it gets stuck on this. + return returnStatus; + } + + #endregion Strings + + #region ModsInstalled + + private static Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + internal static bool IsRTInstalled + { + get + { + return IsModInstalled("RemoteTech"); + } + } + + internal static bool IsKopInstalled + { + get + { + return IsModInstalled("Kopernicus"); + } + } + + internal static bool IsRSSInstalled + { + get + { + return IsModInstalled("RealSolarSystem"); + } + } + + internal static bool IsResearchBodiesInstalled + { + get + { + return IsModInstalled("ResearchBodies"); + } + } + + internal static bool IsTSTInstalled + { + get + { + return IsModInstalled("TarsierSpaceTech"); + } + } + + internal static bool IsEVEInstalled + { + get + { + return IsModInstalled("EVEManager"); + + } + } + + internal static bool IsOPMInstalled + { + get + { + CelestialBody sarnus = FlightGlobals.Bodies.FirstOrDefault(a => a.name == "Sarnus"); + if (sarnus != null) + { + return true; + } + return false; + } + } + + internal static bool IsNHInstalled + { + get + { + CelestialBody sonnah = FlightGlobals.Bodies.FirstOrDefault(a => a.name == "Sonnah"); + if (sonnah != null) + { + return true; + } + return false; + } + } + + internal static bool IsModInstalled(string assemblyName) + { + Assembly assembly = (from a in assemblies + where a.FullName.Contains(assemblyName) + select a).FirstOrDefault(); + return assembly != null; + } + + #endregion ModsInstalled + + #region Logging + // Logging Functions + // Name of the Assembly that is running this MonoBehaviour + internal static String _AssemblyName + { get { return Assembly.GetExecutingAssembly().GetName().Name; } } + + internal static bool debuggingOn = false; + + /// + /// Logging to the debug file + /// + /// Text to be printed - can be formatted as per String.format + /// Objects to feed into a String.format + + internal static void Log_Debug(String Message, params object[] strParams) + { + if (debuggingOn) + { + Log("DEBUG: " + Message, strParams); + } + } + + /// + /// Logging to the log file + /// + /// Text to be printed - can be formatted as per String.format + /// Objects to feed into a String.format + + internal static void Log(String Message, params object[] strParams) + { + Message = String.Format(Message, strParams); // This fills the params into the message + String strMessageLine = String.Format("{0},{2},{1}", + DateTime.Now, Message, + _AssemblyName); // This adds our standardised wrapper to each line + Debug.Log(strMessageLine); // And this puts it in the log + } + #endregion Logging + } +} \ No newline at end of file diff --git a/RectExtentions.cs b/RectExtentions.cs new file mode 100644 index 0000000..d4fa8ea --- /dev/null +++ b/RectExtentions.cs @@ -0,0 +1,58 @@ +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +using UnityEngine; + +namespace RSTUtils.Extensions +{ + public static class RectExtensions + { + /// + /// Clamps the rectangle inside the screen region. + /// + public static Rect ClampInsideScreen(this Rect value) + { + value.x = Mathf.Clamp(value.x, 0, Screen.width - value.width); + value.y = Mathf.Clamp(value.y, 0, Screen.height - value.height); + + return value; + } + + /// + /// Clamps the rectangle into the screen region by the specified margin. + /// + public static Rect ClampToScreen(this Rect value, float margin = 25.0f) + { + value.x = Mathf.Clamp(value.x, -(value.width - margin), Screen.width - margin); + value.y = Mathf.Clamp(value.y, -(value.height - margin), Screen.height - margin); + + return value; + } + + /// + /// Returns whether the mouse is within the coordinates of this rectangle. + /// + public static bool MouseIsOver(this Rect value) + { + return value.Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)); + } + + public static Rect Translate(this Rect value, Rect rectangle) + { + value.x += rectangle.x; + value.y += rectangle.y; + + return value; + } + } +} \ No newline at end of file diff --git a/ToolBarManager.cs b/ToolBarManager.cs new file mode 100644 index 0000000..5ef2846 --- /dev/null +++ b/ToolBarManager.cs @@ -0,0 +1,876 @@ +/* +Copyright (c) 2013-2014, Maik Schreiber +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; + +namespace RSTUtils +{ + /**********************************************************\ + * --- DO NOT EDIT BELOW THIS COMMENT --- * + * * + * This file contains classes and interfaces to use the * + * Toolbar Plugin without creating a hard dependency on it. * + * * + * There is nothing in this file that needs to be edited * + * by hand. * + * * + * --- DO NOT EDIT BELOW THIS COMMENT --- * + \**********************************************************/ + + /// + /// The global tool bar manager. + /// + public partial class ToolbarManager : IToolbarManager + { + /// + /// Whether the Toolbar Plugin is available. + /// + public static bool ToolbarAvailable + { + get + { + if (toolbarAvailable == null) + { + toolbarAvailable = Instance != null; + } + return (bool)toolbarAvailable; + } + } + + /// + /// The global tool bar manager instance. + /// + public static IToolbarManager Instance + { + get + { + if ((toolbarAvailable != false) && (instance_ == null)) + { + Type type = ToolbarTypes.getType("Toolbar.ToolbarManager"); + if (type != null) + { + object realToolbarManager = ToolbarTypes.getStaticProperty(type, "Instance").GetValue(null, null); + instance_ = new ToolbarManager(realToolbarManager); + } + } + return instance_; + } + } + } + + #region interfaces + + /// + /// A toolbar manager. + /// + public interface IToolbarManager + { + /// + /// Adds a new button. + /// + /// + /// To replace an existing button, just add a new button using the old button's namespace and ID. + /// Note that the new button will inherit the screen position of the old button. + /// + /// The new button's namespace. This is usually the plugin's name. Must not include special characters like '.' + /// The new button's ID. This ID must be unique across all buttons in the namespace. Must not include special characters like '.' + /// The button created. + IButton add(string ns, string id); + } + + /// + /// Represents a clickable button. + /// + public interface IButton + { + /// + /// The text displayed on the button. Set to null to hide text. + /// + /// + /// The text can be changed at any time to modify the button's appearance. Note that since this will also + /// modify the button's size, this feature should be used sparingly, if at all. + /// + /// + string Text + { + set; + get; + } + + /// + /// The color the button text is displayed with. Defaults to Color.white. + /// + /// + /// The text color can be changed at any time to modify the button's appearance. + /// + Color TextColor + { + set; + get; + } + + /// + /// The path of a texture file to display an icon on the button. Set to null to hide icon. + /// + /// + /// + /// A texture path on a button will have precedence over text. That is, if both text and texture path + /// have been set on a button, the button will show the texture, not the text. + /// + /// + /// The texture size must not exceed 24x24 pixels. + /// + /// + /// The texture path must be relative to the "GameData" directory, and must not specify a file name suffix. + /// Valid example: MyAddon/Textures/icon_mybutton + /// + /// + /// The texture path can be changed at any time to modify the button's appearance. + /// + /// + /// + string TexturePath + { + set; + get; + } + + /// + /// The button's tool tip text. Set to null if no tool tip is desired. + /// + /// + /// Tool Tip Text Should Always Use Headline Style Like This. + /// + string ToolTip + { + set; + get; + } + + /// + /// Whether this button is currently visible or not. Can be used in addition to or as a replacement for . + /// + /// + /// Setting this property to true does not affect the player's ability to hide the button using the configuration. + /// Conversely, setting this property to false does not enable the player to show the button using the configuration. + /// + bool Visible + { + set; + get; + } + + /// + /// Determines this button's visibility. Can be used in addition to or as a replacement for . + /// + /// + /// The return value from IVisibility.Visible is subject to the same rules as outlined for + /// . + /// + IVisibility Visibility + { + set; + get; + } + + /// + /// Whether this button is currently effectively visible or not. This is a combination of + /// and . + /// + /// + /// Note that the toolbar is not visible in certain game scenes, for example the loading screens. This property + /// does not reflect button invisibility in those scenes. In addition, this property does not reflect the + /// player's configuration of the button's visibility. + /// + bool EffectivelyVisible + { + get; + } + + /// + /// Whether this button is currently enabled (clickable) or not. This does not affect the player's ability to + /// position the button on their toolbar. + /// + bool Enabled + { + set; + get; + } + + /// + /// Whether this button is currently "important." Set to false to return to normal button behaviour. + /// + /// + /// + /// This can be used to temporarily force the button to be shown on screen regardless of the toolbar being + /// currently in auto-hidden mode. For example, a button that signals the arrival of a private message in + /// a chat room could mark itself as "important" as long as the message has not been read. + /// + /// + /// Setting this property does not change the appearance of the button. Use to + /// change the button's icon. + /// + /// + /// Setting this property to true does not affect the player's ability to hide the button using the + /// configuration. + /// + /// + /// This feature should be used only sparingly, if at all, since it forces the button to be displayed on + /// screen even when it normally wouldn't. + /// + /// + bool Important + { + set; + get; + } + + /// + /// A drawable that is tied to the current button. This can be anything from a popup menu to + /// an informational window. Set to null to hide the drawable. + /// + IDrawable Drawable + { + set; + get; + } + + /// + /// Event handler that can be registered with to receive "on click" events. + /// + /// + /// + /// IButton button = ... + /// button.OnClick += (e) => { + /// Debug.Log("button clicked, mouseButton: " + e.MouseButton); + /// }; + /// + /// + event ClickHandler OnClick; + + /// + /// Event handler that can be registered with to receive "on mouse enter" events. + /// + /// + /// + /// IButton button = ... + /// button.OnMouseEnter += (e) => { + /// Debug.Log("mouse entered button"); + /// }; + /// + /// + event MouseEnterHandler OnMouseEnter; + + /// + /// Event handler that can be registered with to receive "on mouse leave" events. + /// + /// + /// + /// IButton button = ... + /// button.OnMouseLeave += (e) => { + /// Debug.Log("mouse left button"); + /// }; + /// + /// + event MouseLeaveHandler OnMouseLeave; + + /// + /// Permanently destroys this button so that it is no longer displayed. + /// Should be used when a plugin is stopped to remove leftover buttons. + /// + void Destroy(); + } + + /// + /// A drawable that is tied to a particular button. This can be anything from a popup menu + /// to an informational window. + /// + public interface IDrawable + { + /// + /// Update any information. This is called once per frame. + /// + void Update(); + + /// + /// Draws GUI widgets for this drawable. This is the equivalent to the OnGUI() message in + /// . + /// + /// + /// The drawable will be positioned near its parent toolbar according to the drawable's current + /// width/height. + /// + /// The left/top position of where to draw this drawable. + /// The current width/height of this drawable. + Vector2 Draw(Vector2 position); + } + + #endregion interfaces + + #region events + + /// + /// Event describing a click on a button. + /// + public partial class ClickEvent : EventArgs + { + /// + /// The button that has been clicked. + /// + public readonly IButton Button; + + /// + /// The mouse button which the button was clicked with. + /// + /// + /// Is 0 for left mouse button, 1 for right mouse button, and 2 for middle mouse button. + /// + public readonly int MouseButton; + } + + /// + /// An event handler that is invoked whenever a button has been clicked. + /// + /// An event describing the button click. + public delegate void ClickHandler(ClickEvent e); + + /// + /// Event describing the mouse pointer moving about a button. + /// + public abstract partial class MouseMoveEvent + { + /// + /// The button in question. + /// + public readonly IButton button; + } + + /// + /// Event describing the mouse pointer entering a button's area. + /// + public partial class MouseEnterEvent : MouseMoveEvent + { + } + + /// + /// Event describing the mouse pointer leaving a button's area. + /// + public partial class MouseLeaveEvent : MouseMoveEvent + { + } + + /// + /// An event handler that is invoked whenever the mouse pointer enters a button's area. + /// + /// An event describing the mouse pointer entering. + public delegate void MouseEnterHandler(MouseEnterEvent e); + + /// + /// An event handler that is invoked whenever the mouse pointer leaves a button's area. + /// + /// An event describing the mouse pointer leaving. + public delegate void MouseLeaveHandler(MouseLeaveEvent e); + + #endregion events + + #region visibility + + /// + /// Determines visibility of a button. + /// + /// + public interface IVisibility + { + /// + /// Whether a button is currently visible or not. + /// + /// + bool Visible + { + get; + } + } + + /// + /// Determines visibility of a button in relation to the currently running game scene. + /// + /// + /// + /// IButton button = ... + /// button.Visibility = new GameScenesVisibility(GameScenes.EDITOR, GameScenes.SPH); + /// + /// + /// + public class GameScenesVisibility : IVisibility + { + public bool Visible + { + get + { + return (bool)visibleProperty.GetValue(realGameScenesVisibility, null); + } + } + + private object realGameScenesVisibility; + private PropertyInfo visibleProperty; + + public GameScenesVisibility(params GameScenes[] gameScenes) + { + Type gameScenesVisibilityType = ToolbarTypes.getType("Toolbar.GameScenesVisibility"); + realGameScenesVisibility = Activator.CreateInstance(gameScenesVisibilityType, gameScenes); + visibleProperty = ToolbarTypes.getProperty(gameScenesVisibilityType, "Visible"); + } + } + + #endregion visibility + + #region drawable + + /// + /// A drawable that draws a popup menu. + /// + public class PopupMenuDrawable : IDrawable + { + /// + /// Event handler that can be registered with to receive "any menu option clicked" events. + /// + public event Action OnAnyOptionClicked + { + add + { + onAnyOptionClickedEvent.AddEventHandler(realPopupMenuDrawable, value); + } + remove + { + onAnyOptionClickedEvent.RemoveEventHandler(realPopupMenuDrawable, value); + } + } + + private object realPopupMenuDrawable; + private MethodInfo updateMethod; + private MethodInfo drawMethod; + private MethodInfo addOptionMethod; + private MethodInfo addSeparatorMethod; + private MethodInfo destroyMethod; + private EventInfo onAnyOptionClickedEvent; + + public PopupMenuDrawable() + { + Type popupMenuDrawableType = ToolbarTypes.getType("Toolbar.PopupMenuDrawable"); + realPopupMenuDrawable = Activator.CreateInstance(popupMenuDrawableType, null); + updateMethod = ToolbarTypes.getMethod(popupMenuDrawableType, "Update"); + drawMethod = ToolbarTypes.getMethod(popupMenuDrawableType, "Draw"); + addOptionMethod = ToolbarTypes.getMethod(popupMenuDrawableType, "AddOption"); + addSeparatorMethod = ToolbarTypes.getMethod(popupMenuDrawableType, "AddSeparator"); + destroyMethod = ToolbarTypes.getMethod(popupMenuDrawableType, "Destroy"); + onAnyOptionClickedEvent = ToolbarTypes.getEvent(popupMenuDrawableType, "OnAnyOptionClicked"); + } + + public void Update() + { + updateMethod.Invoke(realPopupMenuDrawable, null); + } + + public Vector2 Draw(Vector2 position) + { + return (Vector2)drawMethod.Invoke(realPopupMenuDrawable, new object[] { position }); + } + + /// + /// Adds a new option to the popup menu. + /// + /// The text of the option. + /// A button that can be used to register clicks on the menu option. + public IButton AddOption(string text) + { + object realButton = addOptionMethod.Invoke(realPopupMenuDrawable, new object[] { text }); + return new Button(realButton, new ToolbarTypes()); + } + + /// + /// Adds a separator to the popup menu. + /// + public void AddSeparator() + { + addSeparatorMethod.Invoke(realPopupMenuDrawable, null); + } + + /// + /// Destroys this drawable. This must always be called before disposing of this drawable. + /// + public void Destroy() + { + destroyMethod.Invoke(realPopupMenuDrawable, null); + } + } + + #endregion drawable + + #region private implementations + + public partial class ToolbarManager : IToolbarManager + { + private static bool? toolbarAvailable; + private static IToolbarManager instance_; + private object realToolbarManager; + private MethodInfo addMethod; + private Dictionary buttons = new Dictionary(); + private ToolbarTypes types = new ToolbarTypes(); + + private ToolbarManager(object realToolbarManager) + { + this.realToolbarManager = realToolbarManager; + addMethod = ToolbarTypes.getMethod(types.iToolbarManagerType, "add"); + } + + public IButton add(string ns, string id) + { + object realButton = addMethod.Invoke(realToolbarManager, new object[] { ns, id }); + IButton button = new Button(realButton, types); + buttons.Add(realButton, button); + return button; + } + } + + internal class Button : IButton + { + private object realButton; + private ToolbarTypes types; + private Delegate realClickHandler; + private Delegate realMouseEnterHandler; + private Delegate realMouseLeaveHandler; + + internal Button(object realButton, ToolbarTypes types) + { + this.realButton = realButton; + this.types = types; + realClickHandler = attachEventHandler(types.button.onClickEvent, "clicked", realButton); + realMouseEnterHandler = attachEventHandler(types.button.onMouseEnterEvent, "mouseEntered", realButton); + realMouseLeaveHandler = attachEventHandler(types.button.onMouseLeaveEvent, "mouseLeft", realButton); + } + + private Delegate attachEventHandler(EventInfo @event, string methodName, object realButton) + { + MethodInfo method = GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); + Delegate d = Delegate.CreateDelegate(@event.EventHandlerType, this, method); + @event.AddEventHandler(realButton, d); + return d; + } + + public string Text + { + set + { + types.button.textProperty.SetValue(realButton, value, null); + } + get + { + return (string)types.button.textProperty.GetValue(realButton, null); + } + } + + public Color TextColor + { + set + { + types.button.textColorProperty.SetValue(realButton, value, null); + } + get + { + return (Color)types.button.textColorProperty.GetValue(realButton, null); + } + } + + public string TexturePath + { + set + { + types.button.texturePathProperty.SetValue(realButton, value, null); + } + get + { + return (string)types.button.texturePathProperty.GetValue(realButton, null); + } + } + + public string ToolTip + { + set + { + types.button.toolTipProperty.SetValue(realButton, value, null); + } + get + { + return (string)types.button.toolTipProperty.GetValue(realButton, null); + } + } + + public bool Visible + { + set + { + types.button.visibleProperty.SetValue(realButton, value, null); + } + get + { + return (bool)types.button.visibleProperty.GetValue(realButton, null); + } + } + + public IVisibility Visibility + { + set + { + object functionVisibility = null; + if (value != null) + { + functionVisibility = Activator.CreateInstance(types.functionVisibilityType, new Func(() => value.Visible)); + } + types.button.visibilityProperty.SetValue(realButton, functionVisibility, null); + visibility_ = value; + } + get + { + return visibility_; + } + } + + private IVisibility visibility_; + + public bool EffectivelyVisible + { + get + { + return (bool)types.button.effectivelyVisibleProperty.GetValue(realButton, null); + } + } + + public bool Enabled + { + set + { + types.button.enabledProperty.SetValue(realButton, value, null); + } + get + { + return (bool)types.button.enabledProperty.GetValue(realButton, null); + } + } + + public bool Important + { + set + { + types.button.importantProperty.SetValue(realButton, value, null); + } + get + { + return (bool)types.button.importantProperty.GetValue(realButton, null); + } + } + + public IDrawable Drawable + { + set + { + object functionDrawable = null; + if (value != null) + { + functionDrawable = Activator.CreateInstance(types.functionDrawableType, new Action(value.Update), new Func(value.Draw)); + } + types.button.drawableProperty.SetValue(realButton, functionDrawable, null); + drawable_ = value; + } + get + { + return drawable_; + } + } + + private IDrawable drawable_; + + public event ClickHandler OnClick; + + private void clicked(object realEvent) + { + if (OnClick != null) + { + OnClick(new ClickEvent(realEvent, this)); + } + } + + public event MouseEnterHandler OnMouseEnter; + + private void mouseEntered(object realEvent) + { + if (OnMouseEnter != null) + { + OnMouseEnter(new MouseEnterEvent(this)); + } + } + + public event MouseLeaveHandler OnMouseLeave; + + private void mouseLeft(object realEvent) + { + if (OnMouseLeave != null) + { + OnMouseLeave(new MouseLeaveEvent(this)); + } + } + + public void Destroy() + { + detachEventHandler(types.button.onClickEvent, realClickHandler, realButton); + detachEventHandler(types.button.onMouseEnterEvent, realMouseEnterHandler, realButton); + detachEventHandler(types.button.onMouseLeaveEvent, realMouseLeaveHandler, realButton); + types.button.destroyMethod.Invoke(realButton, null); + } + + private void detachEventHandler(EventInfo @event, Delegate d, object realButton) + { + @event.RemoveEventHandler(realButton, d); + } + } + + public partial class ClickEvent : EventArgs + { + internal ClickEvent(object realEvent, IButton button) + { + Type type = realEvent.GetType(); + Button = button; + MouseButton = (int)type.GetField("MouseButton", BindingFlags.Public | BindingFlags.Instance).GetValue(realEvent); + } + } + + public abstract partial class MouseMoveEvent : EventArgs + { + internal MouseMoveEvent(IButton button) + { + this.button = button; + } + } + + public partial class MouseEnterEvent : MouseMoveEvent + { + internal MouseEnterEvent(IButton button) + : base(button) + { + } + } + + public partial class MouseLeaveEvent : MouseMoveEvent + { + internal MouseLeaveEvent(IButton button) + : base(button) + { + } + } + + internal class ToolbarTypes + { + internal readonly Type iToolbarManagerType; + internal readonly Type functionVisibilityType; + internal readonly Type functionDrawableType; + internal readonly ButtonTypes button; + + internal ToolbarTypes() + { + iToolbarManagerType = getType("Toolbar.IToolbarManager"); + functionVisibilityType = getType("Toolbar.FunctionVisibility"); + functionDrawableType = getType("Toolbar.FunctionDrawable"); + Type iButtonType = getType("Toolbar.IButton"); + button = new ButtonTypes(iButtonType); + } + + internal static Type getType(string name) + { + return AssemblyLoader.loadedAssemblies + .SelectMany(a => a.assembly.GetExportedTypes()) + .SingleOrDefault(t => t.FullName == name); + } + + internal static PropertyInfo getProperty(Type type, string name) + { + return type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance); + } + + internal static PropertyInfo getStaticProperty(Type type, string name) + { + return type.GetProperty(name, BindingFlags.Public | BindingFlags.Static); + } + + internal static EventInfo getEvent(Type type, string name) + { + return type.GetEvent(name, BindingFlags.Public | BindingFlags.Instance); + } + + internal static MethodInfo getMethod(Type type, string name) + { + return type.GetMethod(name, BindingFlags.Public | BindingFlags.Instance); + } + } + + internal class ButtonTypes + { + internal readonly Type iButtonType; + internal readonly PropertyInfo textProperty; + internal readonly PropertyInfo textColorProperty; + internal readonly PropertyInfo texturePathProperty; + internal readonly PropertyInfo toolTipProperty; + internal readonly PropertyInfo visibleProperty; + internal readonly PropertyInfo visibilityProperty; + internal readonly PropertyInfo effectivelyVisibleProperty; + internal readonly PropertyInfo enabledProperty; + internal readonly PropertyInfo importantProperty; + internal readonly PropertyInfo drawableProperty; + internal readonly EventInfo onClickEvent; + internal readonly EventInfo onMouseEnterEvent; + internal readonly EventInfo onMouseLeaveEvent; + internal readonly MethodInfo destroyMethod; + + internal ButtonTypes(Type iButtonType) + { + this.iButtonType = iButtonType; + textProperty = ToolbarTypes.getProperty(iButtonType, "Text"); + textColorProperty = ToolbarTypes.getProperty(iButtonType, "TextColor"); + texturePathProperty = ToolbarTypes.getProperty(iButtonType, "TexturePath"); + toolTipProperty = ToolbarTypes.getProperty(iButtonType, "ToolTip"); + visibleProperty = ToolbarTypes.getProperty(iButtonType, "Visible"); + visibilityProperty = ToolbarTypes.getProperty(iButtonType, "Visibility"); + effectivelyVisibleProperty = ToolbarTypes.getProperty(iButtonType, "EffectivelyVisible"); + enabledProperty = ToolbarTypes.getProperty(iButtonType, "Enabled"); + importantProperty = ToolbarTypes.getProperty(iButtonType, "Important"); + drawableProperty = ToolbarTypes.getProperty(iButtonType, "Drawable"); + onClickEvent = ToolbarTypes.getEvent(iButtonType, "OnClick"); + onMouseEnterEvent = ToolbarTypes.getEvent(iButtonType, "OnMouseEnter"); + onMouseLeaveEvent = ToolbarTypes.getEvent(iButtonType, "OnMouseLeave"); + destroyMethod = ToolbarTypes.getMethod(iButtonType, "Destroy"); + } + } + + #endregion private implementations +} \ No newline at end of file From 6b0a4cf5ce3c853619a495b52d9ad703962ca3b6 Mon Sep 17 00:00:00 2001 From: Jamie Leighton Date: Fri, 17 Jun 2016 18:58:07 +1000 Subject: [PATCH 5/6] SourceTree Cleanup --- .gitignore | 1 + .../REPOSoftTech/DeepFreeze/Changelog.txt | 2 +- .../DeepFreeze/DeepFreezeContinued.version | 4 +- Source/DeepFreeze.csproj | 133 +- Source/DeepFreezeGUI.cs | 2 +- Source/DeepFreezerPart.cs | 716 ++------- .../AppLauncherToolBarExtension.cs | 355 ----- Source/REPOSoftTechKSPUtils/RSTutilities.cs | 1391 ----------------- Source/REPOSoftTechKSPUtils/RectExtentions.cs | 58 - Source/REPOSoftTechKSPUtils/ToolBarManager.cs | 876 ----------- 10 files changed, 166 insertions(+), 3372 deletions(-) delete mode 100644 Source/REPOSoftTechKSPUtils/AppLauncherToolBarExtension.cs delete mode 100644 Source/REPOSoftTechKSPUtils/RSTutilities.cs delete mode 100644 Source/REPOSoftTechKSPUtils/RectExtentions.cs delete mode 100644 Source/REPOSoftTechKSPUtils/ToolBarManager.cs diff --git a/.gitignore b/.gitignore index e63d54f..79f52e4 100644 --- a/.gitignore +++ b/.gitignore @@ -212,3 +212,4 @@ pip-log.txt #Mr Developer .mr.developer.cfg +*.zip diff --git a/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt b/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt index fc0a00d..6a61f6c 100644 --- a/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt +++ b/Distribution/GameData/REPOSoftTech/DeepFreeze/Changelog.txt @@ -1,6 +1,6 @@ V0.22.3.0 Fix Bug where KAC Alarms GUI window fails if there is a KAC alarm of type "Crew" - +Adjustments to internal code for 1.1.3 API changes for Crew Transfers. V0.22.2.0 "Bug Fixes" Fixed bug with Icons not loading on Linux. Efficiency clean-up on code modules (speed and memory use). diff --git a/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version b/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version index 10cc879..7e08e52 100644 --- a/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version +++ b/Distribution/GameData/REPOSoftTech/DeepFreeze/DeepFreezeContinued.version @@ -3,7 +3,7 @@ "URL":"http://ksp-avc.cybutek.net/version.php?id=183", "DOWNLOAD":"http://spacedock.info/mod/142/DeepFreeze%20Continued...", "VERSION":{"MAJOR":0,"MINOR":22,"PATCH":3,"BUILD":0}, -"KSP_VERSION":{"MAJOR":1,"MINOR":1,"PATCH":2}, +"KSP_VERSION":{"MAJOR":1,"MINOR":1,"PATCH":3}, "KSP_VERSION_MIN":{"MAJOR":1,"MINOR":1,"PATCH":2}, -"KSP_VERSION_MAX":{"MAJOR":1,"MINOR":1,"PATCH":2} +"KSP_VERSION_MAX":{"MAJOR":1,"MINOR":1,"PATCH":3} } \ No newline at end of file diff --git a/Source/DeepFreeze.csproj b/Source/DeepFreeze.csproj index 6ef209f..a24ac36 100644 --- a/Source/DeepFreeze.csproj +++ b/Source/DeepFreeze.csproj @@ -36,26 +36,14 @@ OnOutputUpdated - - False - ..\..\KSPDLLs\Assembly-CSharp.dll + + ..\..\KSPDLLs - 1.1.3\Assembly-CSharp.dll - - False - ..\..\KSPDLLs\Assembly-CSharp-firstpass.dll - - - ..\..\KSPDLLs\Assembly-UnityScript.dll - - - ..\..\KSPDLLs\Assembly-UnityScript-firstpass.dll - - - False - ..\..\KSPDLLs\KSPAssets.dll + + ..\..\KSPDLLs - 1.1.3\Assembly-CSharp-firstpass.dll - ..\..\KSPDLLs\KSPUtil.dll + ..\..\KSPDLLs - 1.1.3\KSPUtil.dll @@ -63,13 +51,11 @@ - - False - ..\..\KSPDLLs\UnityEngine.dll + + ..\..\KSPDLLs - 1.1.3\UnityEngine.dll - - False - ..\..\KSPDLLs\UnityEngine.UI.dll + + ..\..\KSPDLLs - 1.1.3\UnityEngine.UI.dll @@ -106,62 +92,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + DeepFreezeContinued.version + @@ -171,31 +104,9 @@ - - - - - - - - - - - - - - Always + + Changelog.txt - - - - - - - - - - @@ -222,13 +133,13 @@ set /p MONO_EXE_DIR=<"$(ProjectDir)LocalDev\mono_exe.txt" set /p PDB2MDB_EXE_DIR=<"$(ProjectDir)LocalDev\pdb2mdb_exe.txt" set /p ZA_DIR=<"$(ProjectDir)LocalDev\7za_dir.txt" -xcopy "$(TargetPath)" "$(SolutionDir)Distribution\GameData\REPOSoftTech\$(ProjectName)\Plugins\" /Y +xcopy "$(TargetPath)" "$(SolutionDir)..\Distribution\GameData\REPOSoftTech\$(ProjectName)\Plugins\" /Y -xcopy "$(TargetDir)$(ProjectName).pdb" "$(SolutionDir)Distribution\GameData\REPOSoftTech\$(ProjectName)\Plugins\" /Y +xcopy "$(TargetDir)$(ProjectName).pdb" "$(SolutionDir)..\Distribution\GameData\REPOSoftTech\$(ProjectName)\Plugins\" /Y cd "$(TargetDir)" "%25MONO_EXE_DIR%25" "%25PDB2MDB_EXE_DIR%25" "$(TargetFileName)" -xcopy "$(TargetDir)$(ProjectName).dll.mdb" "$(SolutionDir)Distribution\GameData\REPOSoftTech\$(ProjectName)\Plugins\" /Y +xcopy "$(TargetDir)$(ProjectName).dll.mdb" "$(SolutionDir)..\Distribution\GameData\REPOSoftTech\$(ProjectName)\Plugins\" /Y where /q %25ZA_DIR%25:7za.exe if %25ERRORLEVEL%25 == 1 ( @@ -236,11 +147,13 @@ if %25ERRORLEVEL%25 == 1 ( ) else ( echo Packaging build ) -if exist "$(SolutionDir)Distribution\$(ProjectName)_V@(VersionNumber).zip" del "$(SolutionDir)Distribution\$(ProjectName)_V@(VersionNumber).zip" +if exist "$(SolutionDir)..\Distribution\$(ProjectName)_V@(VersionNumber).zip" del "$(SolutionDir)..\Distribution\$(ProjectName)_V@(VersionNumber).zip" -%25ZA_DIR%257za.exe a -tzip -r "$(SolutionDir)Distribution\$(ProjectName)_V@(VersionNumber).zip" "$(SolutionDir)Distribution\GameData" -c:\7za\7za d -r -x!REPOSoftTech.png -x!Glykerol.png -x!DFtoolbar.png -x!DeepFreezeOff.png -x!DeepFreezeOn.png -x!DeepFreezeEditor.png -x!alphaonly.png -x!DFbtnResize.png -x!DFbtnRedCross.png -x!DFToolTipBox.png "$(SolutionDir)Distribution\$(ProjectName)_V@(VersionNumber).zip" "*.ddsified" "*.pdb" "*.mdb" "*.png" -xcopy /E /Y "$(SolutionDir)Distribution\GameData" "%25KSP_DIR%25\GameData" +%25ZA_DIR%257za.exe a -tzip -r "$(SolutionDir)..\Distribution\$(ProjectName)_V@(VersionNumber).zip" "$(SolutionDir)..\Distribution\GameData" +cd "$(SolutionDir)..\Distribution\GameData\RepoSoftTech\$(ProjectName)\Icons" +dir /b > "$(SolutionDir)IconsExclude.txt" +c:\7za\7za d -r -x@"$(SolutionDir)IconsExclude.txt" -x!REPOSoftTech.png "$(SolutionDir)..\Distribution\$(ProjectName)_V@(VersionNumber).zip" "*.ddsified" "*.pdb" "*.mdb" "*.png" +xcopy /E /Y "$(SolutionDir)..\Distribution\GameData" "%25KSP_DIR%25\GameData"