diff --git a/ModelMultiParticlePersistFX.cs b/ModelMultiParticlePersistFX.cs index 759660a..d4142a1 100644 --- a/ModelMultiParticlePersistFX.cs +++ b/ModelMultiParticlePersistFX.cs @@ -10,6 +10,8 @@ using System.Collections.Generic; using System.Text; +using SmokeScreen; + using UnityEngine; [EffectDefinition("MODEL_MULTI_PARTICLE_PERSIST")] @@ -85,24 +87,18 @@ public class ModelMultiParticlePersistFX : EffectBehaviour #endregion Persistent fields - public MultiInputCurve emission = new MultiInputCurve("emission"); - - public MultiInputCurve energy = new MultiInputCurve("energy"); - - public MultiInputCurve speed = new MultiInputCurve("speed"); - - public MultiInputCurve grow = new MultiInputCurve("grow", true); - - public MultiInputCurve scale = new MultiInputCurve("scale"); - - public MultiInputCurve size = new MultiInputCurve("size"); - - public MultiInputCurve offset = new MultiInputCurve("offset", true); + public MultiInputCurve emission; + public MultiInputCurve energy; + public MultiInputCurve speed; + public MultiInputCurve grow; + public MultiInputCurve scale; + public MultiInputCurve size; + public MultiInputCurve offset; // Logarithmic growth applied to to the particle. // The size at time t after emission will be approximately // (Log(logarithmicGrowth * t + 1) + 1) * initialSize, assuming grow = 0. - public MultiInputCurve logGrow = new MultiInputCurve("logGrow", true); + public MultiInputCurve logGrow; private float logarithmicGrow; @@ -132,7 +128,7 @@ public class ModelMultiParticlePersistFX : EffectBehaviour public bool showUI = true; - private readonly List list = new List(); + private static readonly List list = new List(); public bool overRideInputs = false; @@ -146,11 +142,11 @@ public List Instances } } - //public ModelMultiParticlePersistFX() - //{ - // list.Add(this); - // winID++; - //} + public ModelMultiParticlePersistFX() + { + list.Add(this); + winID = baseWinID + list.Count; + } private void OnDestroy() { @@ -652,9 +648,20 @@ public void Update() public override void OnInitialize() { + if (node_backup != string.Empty) Restore(); + print("OnInitialize"); - print("OnInitialize" + emission.name); - print("OnInitialize" + emission.curves[0].valueName); + print("OnInitialize " + emission.name); + emission.Test(); + + + if (emission.curves != null && emission.curves[0] == null) + print("OnInitialize curve[0] is null"); + + if (emission.curves != null && emission.curves[0] != null) + print("OnInitialize curve[0] is " + emission.curves[0].valueName); + //print("OnInitialize " + emission.curves[0].valueName); + // The shader loading require proper testing // Unity doc says that "Creating materials this way supports only simple shaders (fixed function ones). @@ -751,9 +758,39 @@ public override void OnInitialize() UnityEngine.Object.Destroy(templateKspParticleEmitter); } + public string node_backup = string.Empty; + + //private bool node_backupisLoaded = false; + + + public void Backup(ConfigNode node) + { + node_backup = SmokeScreenUtil.WriteRootNode(node); + print("Backup node_backup is\n " + node_backup.Replace(Environment.NewLine, Environment.NewLine + "ModelMultiParticlePersistFX ")); + } + + public void Restore() + { + print("Restore node_backup is\n " + node_backup.Replace(Environment.NewLine, Environment.NewLine + "ModelMultiParticlePersistFX ")); + string[] text = node_backup.Split(new string[] { "\n" }, StringSplitOptions.None); + ConfigNode node = SmokeScreenUtil.RecurseFormat(SmokeScreenUtil.PreFormatConfig(text)); + this.OnLoad(node); + } + public override void OnLoad(ConfigNode node) { print("OnLoad"); + Backup(node); + + emission = new MultiInputCurve("emission"); + energy = new MultiInputCurve("energy"); + speed = new MultiInputCurve("speed"); + grow = new MultiInputCurve("grow", true); + scale = new MultiInputCurve("scale"); + size = new MultiInputCurve("size"); + offset = new MultiInputCurve("offset", true); + logGrow = new MultiInputCurve("logGrow", true); + ConfigNode.LoadObjectFromConfig(this, node); print("OnLoad2"); emission.Load(node); @@ -771,6 +808,8 @@ public override void OnLoad(ConfigNode node) print("OnLoad3" + emission.name); print("OnLoad3" + emission.curves[0].valueName); + + //isLoaded = true; } public override void OnSave(ConfigNode node) @@ -796,11 +835,11 @@ private static void print(String s) } - // TODO : move the whole UI stuff to a dedicated class - this is getting to big + // TODO : move the whole UI stuff to a dedicated class - this is getting way to big private Rect winPos = new Rect(300, 100, 400, 100); private const int baseWinID = 512100; - private int winID = baseWinID; + private int winID; private string nodeText = ""; @@ -845,19 +884,19 @@ private void windowGUI(int ID) overRideInputs = GUILayout.Toggle(overRideInputs, "Manual Inputs"); - if (!overRideInputs) - { - GUILayout.Label("Power : " + inputs[(int)MultiInputCurve.Inputs.power].ToString("F2")); - - GUILayout.Label("Atmo Density : " + inputs[(int)MultiInputCurve.Inputs.density].ToString("F2")); - - GUILayout.Label("Mach Speed : " + inputs[(int)MultiInputCurve.Inputs.mach].ToString("F2")); - - GUILayout.Label("Part Temperature : " + inputs[(int)MultiInputCurve.Inputs.parttemp].ToString("F2")); - - GUILayout.Label("External Temperature : " + inputs[(int)MultiInputCurve.Inputs.externaltemp].ToString("F2")); - } - else + //if (!overRideInputs) + //{ + // GUILayout.Label("Power : " + inputs[(int)MultiInputCurve.Inputs.power].ToString("F2")); + // + // GUILayout.Label("Atmo Density : " + inputs[(int)MultiInputCurve.Inputs.density].ToString("F2")); + // + // GUILayout.Label("Mach Speed : " + inputs[(int)MultiInputCurve.Inputs.mach].ToString("F2")); + // + // GUILayout.Label("Part Temperature : " + inputs[(int)MultiInputCurve.Inputs.parttemp].ToString("F2")); + // + // GUILayout.Label("External Temperature : " + inputs[(int)MultiInputCurve.Inputs.externaltemp].ToString("F2")); + //} + //else { GUIInput((int)MultiInputCurve.Inputs.power,"Power"); GUIInput((int)MultiInputCurve.Inputs.density,"Atmo Density"); @@ -877,21 +916,22 @@ private void windowGUI(int ID) { ConfigNode node = new ConfigNode(); this.OnSave(node); - nodeText = WriteRootNode(node); + nodeText = SmokeScreenUtil.WriteRootNode(node); + print("Displaying node \n " + nodeText.Replace("\n", "\n" + "ModelMultiParticlePersistFX ")); } - //if (GUILayout.Button("Apply")) - //{ - // string[] text = nodeText.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); - // ConfigNode node = ConfigNode.RecurseFormat(ConfigNode.PreFormatConfig(text)); - // this.OnLoad(node); - //} + if (GUILayout.Button("Apply")) + { + string[] text = nodeText.Split(new string[] { "\n" }, StringSplitOptions.None); + ConfigNode node = SmokeScreenUtil.RecurseFormat(SmokeScreenUtil.PreFormatConfig(text)); + this.OnLoad(node); + } GUILayout.EndHorizontal(); - scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.ExpandWidth(true), GUILayout.Height(200)); + scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true, GUILayout.MinHeight(300)); - nodeText = GUILayout.TextArea(nodeText, GUILayout.ExpandWidth(true), GUILayout.Height(200)); + nodeText = GUILayout.TextArea(nodeText, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true)); GUILayout.EndScrollView(); } @@ -904,122 +944,58 @@ private void windowGUI(int ID) private void GUIInput(int id, string text) { - GUILayout.Label(text); - GUILayout.BeginHorizontal(); - boxInput[id] = GUILayout.Toggle(boxInput[id],""); + float min = minInput(id); + float max = maxInput(id); - if (boxInput[id]) - { - float.TryParse( - GUILayout.TextField(inputs[id].ToString("F2"), GUILayout.ExpandWidth(true), GUILayout.Width(100)), - out inputs[id]); - } - else + GUILayout.Label(text + " Val=" + inputs[id].ToString("F3") + " Min=" + min.ToString("F2") + " Max=" + max.ToString("F2")); + + + if (overRideInputs) { - inputs[id] = GUILayout.HorizontalSlider(inputs[id], minInput(id), maxInput(id)); - } + GUILayout.BeginHorizontal(); + boxInput[id] = GUILayout.Toggle(boxInput[id], "", GUILayout.ExpandWidth(false)); - GUILayout.EndHorizontal(); + if (boxInput[id]) + { + float.TryParse( + GUILayout.TextField(inputs[id].ToString("F2"), GUILayout.ExpandWidth(true), GUILayout.Width(100)), + out inputs[id]); + } + else + { + inputs[id] = GUILayout.HorizontalSlider(inputs[id], minInput(id), maxInput(id), GUILayout.ExpandWidth(true)); + } + + GUILayout.EndHorizontal(); + } } private float minInput(int id) { - float min = emission.minKey[id]; - min = Mathf.Min(min, energy.minKey[id]); - min = Mathf.Min(min, speed.minKey[id]); - min = Mathf.Min(min, grow.minKey[id]); - min = Mathf.Min(min, scale.minKey[id]); - min = Mathf.Min(min, size.minKey[id]); - min = Mathf.Min(min, offset.minKey[id]); - min = Mathf.Min(min, logGrow.minKey[id]); - + float min = emission.minInput[id]; + min = Mathf.Min(min, energy.minInput[id]); + min = Mathf.Min(min, speed.minInput[id]); + min = Mathf.Min(min, grow.minInput[id]); + min = Mathf.Min(min, scale.minInput[id]); + min = Mathf.Min(min, size.minInput[id]); + min = Mathf.Min(min, offset.minInput[id]); + min = Mathf.Min(min, logGrow.minInput[id]); + return min; } private float maxInput(int id) { - float max = emission.maxKey[id]; - max = Mathf.Min(max, energy.maxKey[id]); - max = Mathf.Min(max, speed.maxKey[id]); - max = Mathf.Min(max, grow.maxKey[id]); - max = Mathf.Min(max, scale.maxKey[id]); - max = Mathf.Min(max, size.maxKey[id]); - max = Mathf.Min(max, offset.maxKey[id]); - max = Mathf.Min(max, logGrow.maxKey[id]); - + float max = emission.maxInput[id]; + max = Mathf.Max(max, energy.maxInput[id]); + max = Mathf.Max(max, speed.maxInput[id]); + max = Mathf.Max(max, grow.maxInput[id]); + max = Mathf.Max(max, scale.maxInput[id]); + max = Mathf.Max(max, size.maxInput[id]); + max = Mathf.Max(max, offset.maxInput[id]); + max = Mathf.Max(max, logGrow.maxInput[id]); + return max; } - - // TODO : move those to an utility class - - - //private static string WriteRootNode(ConfigNode node) - //{ - // string result = ""; - // print("node.values.Count " + node.values.Count + " node.nodes.Count " + node.nodes.Count); - // for (int i = 0; i < node.values.Count; i++) - // { - // ConfigNode.Value item = node.values[i]; - // result += string.Concat(item.name, " = ", item.@value); - // } - // for (int j = 0; j < node.nodes.Count; j++) - // { - // string.Concat(result, WriteNodeString(node.nodes[j], string.Empty)); - // } - // return result; - //} - //private static string WriteNodeString(ConfigNode node, string indent) - //{ - // string result = ""; - // result += string.Concat(indent, node.name); - // result += string.Concat(indent, "{"); - // string str = string.Concat(indent, "\t"); - // for (int i = 0; i < node.values.Count; i++) - // { - // ConfigNode.Value item = node.values[i]; - // result += string.Concat(str, item.name, " = ", item.@value); - // } - // for (int j = 0; j < node.nodes.Count; j++) - // { - // result += WriteNodeString(node, str); - // } - // result += string.Concat(indent, "}"); - // return result; - //} - - private static string WriteRootNode(ConfigNode node) - { - StringBuilder builder = new StringBuilder(); - print("node.values.Count " + node.values.Count + " node.nodes.Count " + node.nodes.Count); - for (int i = 0; i < node.values.Count; i++) - { - ConfigNode.Value item = node.values[i]; - builder.AppendLine(string.Concat(item.name, " = ", item.value)); - } - for (int j = 0; j < node.nodes.Count; j++) - { - WriteNodeString(node.nodes[j], ref builder, string.Empty); - } - return builder.ToString(); - } - private static void WriteNodeString(ConfigNode node, ref StringBuilder builder, string indent) - { - builder.AppendLine(string.Concat(indent, node.name)); - builder.AppendLine(string.Concat(indent, "{")); - string str = string.Concat(indent, "\t"); - for (int i = 0; i < node.values.Count; i++) - { - ConfigNode.Value item = node.values[i]; - builder.AppendLine(string.Concat(str, item.name, " = ", item.value)); - } - for (int j = 0; j < node.nodes.Count; j++) - { - WriteNodeString(node, ref builder, str); - } - builder.AppendLine(string.Concat(indent, "}")); - } - - - } \ No newline at end of file diff --git a/MultiInputCurve.cs b/MultiInputCurve.cs index e35459b..e486b27 100644 --- a/MultiInputCurve.cs +++ b/MultiInputCurve.cs @@ -3,116 +3,82 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; - +using SmokeScreen; using UnityEngine; -[Serializable] -public class FXCurves -{ - [SerializeField] - private FXCurve power; - [SerializeField] - private FXCurve density; - [SerializeField] - private FXCurve mach; - [SerializeField] - private FXCurve parttemp; - [SerializeField] - private FXCurve externaltemp; - - public FXCurve this[int i] - { - get - { - switch (i) - { - case (int)MultiInputCurve.Inputs.power: - return power; - case (int)MultiInputCurve.Inputs.density: - return density; - case (int)MultiInputCurve.Inputs.mach: - return mach; - case (int)MultiInputCurve.Inputs.parttemp: - return parttemp; - case (int)MultiInputCurve.Inputs.externaltemp: - return externaltemp; - default: - return power; - } - } - set - { - switch (i) - { - case (int)MultiInputCurve.Inputs.power: - power = value; - break; - case (int)MultiInputCurve.Inputs.density: - density = value; - break; - case (int)MultiInputCurve.Inputs.mach: - mach = value; - break; - case (int)MultiInputCurve.Inputs.parttemp: - parttemp = value; - break; - case (int)MultiInputCurve.Inputs.externaltemp: - externaltemp = value; - break; - } - } - } -} - - +// This class Serialization DOES NOT WORK in Unity +// Life would be so easier if it did. [Serializable] public class MultiInputCurve { public string name; - public FXCurves curves = new FXCurves(); - public FXCurves logCurves = new FXCurves(); - public float[] minKey = new float[inputsCount]; - public float[] maxKey = new float[inputsCount]; + public FXCurve[] curves = new FXCurve[inputsCount]; + + public FXCurve[] logCurves = new FXCurve[inputsCount]; + + public float[] minInput = new float[inputsCount]; - public float minVal; - public float maxVal; + public float[] maxInput = new float[inputsCount]; - [SerializeField] - bool additive; + public float minOutput; + + public float maxOutput; + + private bool additive; public enum Inputs { power = 0, + density = 1, + mach = 2, + parttemp = 3, + externaltemp = 4 } - //public static readonly int inputsCount = Enum.GetValues(typeof(Inputs)).Length; - public const int inputsCount = 5; + public static readonly int inputsCount = Enum.GetValues(typeof(Inputs)).Length; + + //public const int inputsCount = 5; public MultiInputCurve(string name, bool additive = false) { - print("Constructor"); + //print("Constructor for " + name + " backup_node is null = " + (node_backup == null).ToString()); this.name = name; this.additive = additive; + //if (node_backup != null) + // print("node_backup is\n " + node_backup.Replace(Environment.NewLine, Environment.NewLine + "MultiInputCurve ")); } private void Reset() { - print("Reset"); + //print("Reset"); for (int i = 0; i < inputsCount; i++) { string key = Enum.GetName(typeof(Inputs), i); - print("Resetting " + key); - curves[i] = new FXCurve(key, additive ? 0f : 1f); - minVal = maxVal = additive ? 0f : 1f; + //print("Resetting " + key); + curves[i] = new FXCurve(key, additive ? 0f : 1f) { valueName = key }; + + // FXCurve constructor does not set the value name + + this.minOutput = this.maxOutput = additive ? 0f : 1f; } } + public void Test() + { + //if (!isLoaded) Restore(); + if (curves != null && curves[0] != null) + { + print("Test curve[0] is " + curves[0].valueName); + } + //print("Test for " + name + " backup_node is null = " + (backup_node == null).ToString()); + } + public void Load(ConfigNode node) { Reset(); @@ -129,19 +95,23 @@ public void Load(ConfigNode node) for (int i = 0; i < inputsCount; i++) { string key = Enum.GetName(typeof(Inputs), i); - print("Loading " + key); + //print("Loading " + key); curves[i].Load(key, node.GetNode(name)); - print("Loaded " + curves[i].valueName); + print( + "Loaded " + key + " in " + curves[i].valueName + " " + curves[i].keyFrames.Count() + " should be " + + node.GetNode(name).GetValues(key).Length); string logKey = "log" + key; if (node.GetNode(name).HasValue(logKey)) { - logCurves[i] = new FXCurve(logKey, additive ? 0f : 1f); + logCurves[i] = new FXCurve(logKey, additive ? 0f : 1f) { valueName = logKey }; + // FXCurve constructor does not set the value name logCurves[i].Load(logKey, node.GetNode(name)); } } } UpdateMinMax(); + //isLoaded = true; } private void UpdateMinMax() @@ -151,7 +121,6 @@ private void UpdateMinMax() float minValue = additive ? 0f : 1f; float maxValue = additive ? 0f : 1f; - if (!curves[i].evalSingle) { //print("UpdateMinMax i=" + i + " " + curves[i].fCurve.length); @@ -160,15 +129,15 @@ private void UpdateMinMax() float key = curves[i].fCurve.keys[j].time; float val = curves[i].fCurve.keys[j].value; - minKey[i] = Mathf.Min(minKey[i], key); - maxKey[i] = Mathf.Max(maxKey[i], key); + this.minInput[i] = Mathf.Min(this.minInput[i], key); + this.maxInput[i] = Mathf.Max(this.maxInput[i], key); minValue = Mathf.Min(minValue, val); maxValue = Mathf.Max(maxValue, val); } } - - if ( logCurves[i] != null && !logCurves[i].evalSingle) + + if (logCurves[i] != null && !logCurves[i].evalSingle) { //print("UpdateMinMax i=" + i + " " + logCurves[i].fCurve.length); for (int j = 0; j < logCurves[i].fCurve.length; j++) @@ -176,8 +145,8 @@ private void UpdateMinMax() float key = logCurves[i].fCurve.keys[j].time; float val = logCurves[i].fCurve.keys[j].value; - minKey[i] = Mathf.Min(minKey[i], key); - maxKey[i] = Mathf.Max(maxKey[i], key); + this.minInput[i] = Mathf.Min(this.minInput[i], key); + this.maxInput[i] = Mathf.Max(this.maxInput[i], key); minValue = Mathf.Min(minValue, val); maxValue = Mathf.Max(maxValue, val); @@ -185,16 +154,15 @@ private void UpdateMinMax() } if (additive) { - minVal += minValue; + minOutput += minValue; } else { - maxVal *= maxValue; + maxOutput *= maxValue; } } } - public float Value(float[] inputs) { float result = additive ? 0f : 1f; @@ -207,7 +175,9 @@ public float Value(float[] inputs) if (logCurves[i] != null) { - result = additive ? result + logCurves[i].Value(Mathf.Log(input)) : result * logCurves[i].Value(Mathf.Log(input)); + result = additive + ? result + logCurves[i].Value(Mathf.Log(input)) + : result * logCurves[i].Value(Mathf.Log(input)); } } return result; @@ -218,7 +188,7 @@ public void Save(ConfigNode node) ConfigNode subNode = new ConfigNode(name); for (int i = 0; i < inputsCount; i++) { - print("Saving curve " + curves[i].valueName); + print("Saving curve " + curves[i].valueName + " " + curves[i].keyFrames.Count()); curves[i].Save(subNode); if (logCurves[i] != null) { diff --git a/SmokeScreenConfig.cs b/SmokeScreenConfig.cs new file mode 100644 index 0000000..c651503 --- /dev/null +++ b/SmokeScreenConfig.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SmokeScreen +{ + class SmokeScreenConfig + { + } +} diff --git a/SmokeScreenUI.cs b/SmokeScreenUI.cs new file mode 100644 index 0000000..ed0455e --- /dev/null +++ b/SmokeScreenUI.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SmokeScreen +{ + class SmokeScreenUI + { + } +} diff --git a/SmokeScreenUtil.cs b/SmokeScreenUtil.cs new file mode 100644 index 0000000..4417ca5 --- /dev/null +++ b/SmokeScreenUtil.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace SmokeScreen +{ + using UnityEngine; + + static class SmokeScreenUtil + { + + public static string WriteRootNode(ConfigNode node) + { + StringBuilder builder = new StringBuilder(); + print("node.values.Count " + node.values.Count + " node.nodes.Count " + node.nodes.Count); + for (int i = 0; i < node.values.Count; i++) + { + ConfigNode.Value item = node.values[i]; + builder.AppendLine(string.Concat(item.name, " = ", item.value)); + } + for (int j = 0; j < node.nodes.Count; j++) + { + WriteNodeString(node.nodes[j], ref builder, string.Empty); + } + return builder.ToString(); + } + public static void WriteNodeString(ConfigNode node, ref StringBuilder builder, string indent) + { + builder.AppendLine(string.Concat(indent, node.name)); + builder.AppendLine(string.Concat(indent, "{")); + string str = string.Concat(indent, " "); + for (int i = 0; i < node.values.Count; i++) + { + ConfigNode.Value item = node.values[i]; + builder.AppendLine(string.Concat(str, item.name, " = ", item.value)); + } + for (int j = 0; j < node.nodes.Count; j++) + { + WriteNodeString(node, ref builder, str); + } + builder.AppendLine(string.Concat(indent, "}")); + } + + public static ConfigNode RecurseFormat(List cfg) + { + int num = 0; + ConfigNode configNode = new ConfigNode("root"); + RecurseFormat(cfg, ref num, configNode); + return configNode; + } + + public static void RecurseFormat(List cfg, ref int index, ConfigNode node) + { + while (index < cfg.Count) + { + if ((int)cfg[index].Length == 2) + { + node.values.Add(new ConfigNode.Value(cfg[index][0], cfg[index][1])); + index = index + 1; + } + else if (cfg[index][0] != "{") + { + if (cfg[index][0] == "}") + { + index = index + 1; + return; + } + if (!NextLineIsOpenBrace(cfg, index)) + { + index = index + 1; + } + else + { + ConfigNode configNode = new ConfigNode(cfg[index][0]); + node.nodes.Add(configNode); + index = index + 2; + RecurseFormat(cfg, ref index, configNode); + } + } + else + { + ConfigNode configNode1 = new ConfigNode(string.Empty); + node.nodes.Add(configNode1); + index = index + 1; + RecurseFormat(cfg, ref index, configNode1); + } + } + } + + public static bool NextLineIsOpenBrace(List cfg, int index) + { + int num = index + 1; + if (num < cfg.Count && (int)cfg[num].Length == 1 && cfg[num][0] == "{") + { + return true; + } + return false; + } + + public static List PreFormatConfig(string[] cfgData) + { + if (cfgData == null || (int)cfgData.Length < 1) + { + Debug.LogError("Error: Empty part config file"); + return null; + } + List strs = new List(cfgData); + int count = strs.Count; + while (true) + { + int num = count - 1; + count = num; + if (num < 0) + { + break; + } + strs[count] = strs[count]; + int num1 = strs[count].IndexOf("//"); + int num2 = num1; + if (num1 != -1) + { + if (num2 != 0) + { + strs[count] = strs[count].Remove(num2); + } + else + { + strs.RemoveAt(count); + continue; + } + } + strs[count] = strs[count].Trim(); + if (strs[count].Length != 0) + { + int num3 = strs[count].IndexOf("}", 0); + num2 = num3; + if (num3 == -1 || num2 == 0 && strs[count].Length == 1) + { + int num4 = strs[count].IndexOf("{", 0); + num2 = num4; + if (num4 != -1 && (num2 != 0 || strs[count].Length != 1)) + { + if (num2 > 0) + { + strs.Insert(count, strs[count].Substring(0, num2)); + count++; + strs[count] = strs[count].Substring(num2); + num2 = 0; + } + if (num2 < strs[count].Length - 1) + { + strs.Insert(count + 1, strs[count].Substring(num2 + 1)); + strs[count] = "{"; + count = count + 2; + } + } + } + else + { + if (num2 > 0) + { + strs.Insert(count, strs[count].Substring(0, num2)); + count++; + strs[count] = strs[count].Substring(num2); + num2 = 0; + } + if (num2 < strs[count].Length - 1) + { + strs.Insert(count + 1, strs[count].Substring(num2 + 1)); + strs[count] = "}"; + count = count + 2; + } + } + } + else + { + strs.RemoveAt(count); + } + } + List strArrays = new List(strs.Count); + for (int i = 0; i < strs.Count; i++) + { + string item = strs[i]; + string[] strArrays1 = item.Split(new char[] { '=' }, 2, StringSplitOptions.None); + if (strArrays1 != null && (int)strArrays1.Length != 0) + { + for (int j = 0; j < (int)strArrays1.Length; j++) + { + strArrays1[j] = strArrays1[j].Trim(); + } + strArrays.Add(strArrays1); + } + } + return strArrays; + } + + private static void print(String s) + { + MonoBehaviour.print("[SmokeScreenUtil] " + s); + } + + } +} diff --git a/ToolbarWrapper.cs b/ToolbarWrapper.cs new file mode 100644 index 0000000..f6d7cda --- /dev/null +++ b/ToolbarWrapper.cs @@ -0,0 +1,897 @@ +/* +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 System.Text; +using UnityEngine; + + +// TODO: Change to your plugin's namespace here. +namespace SmokeScreen +{ + + + + /**********************************************************\ + * --- 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 + + #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 + + #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 + { + private GameScenes[] gameScenes; + + 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, new object[] { gameScenes }); + visibleProperty = ToolbarTypes.getProperty(gameScenesVisibilityType, "Visible"); + this.gameScenes = gameScenes; + } + } + + #endregion + + #region drawable + + /// + /// A drawable that draws a popup menu. + /// + public partial 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 + + #region private implementations + + public partial class ToolbarManager : IToolbarManager + { + private static bool? toolbarAvailable = null; + 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 object[] { 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 object[] { + new Action(() => value.Update()), + new Func((pos) => value.Draw(pos)) + }); + } + 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 +} \ No newline at end of file