Skip to content

Commit

Permalink
Merge pull request #151 from MOARdV/master
Browse files Browse the repository at this point in the history
Add support for user-defined custom variables
  • Loading branch information
MOARdV committed Feb 6, 2015
2 parents 957d9e9 + e54f6d7 commit e506f64
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 3 deletions.
6 changes: 3 additions & 3 deletions GameData/JSI/RasterPropMonitor/RasterPropMonitor.version
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
"DOWNLOAD": "https://github.com/Mihara/RasterPropMonitor/releases",
"VERSION": {
"MAJOR": 0,
"MINOR": 18,
"PATCH": 3
"MINOR": 19,
"PATCH": 0
},
"KSP_VERSION": {
"MAJOR": 0,
"MINOR": 25,
"MINOR": 90,
"PATCH": 0
}
}
161 changes: 161 additions & 0 deletions RasterPropMonitor/Core/CustomVariable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JSI
{
// SourceVariable defines a single RPM variable plus a range of digits
// that are used for compound tests for custom variables.
class SourceVariable
{
public readonly string name;
public readonly double minValue, maxValue;
public readonly bool reverse;

public SourceVariable(ConfigNode node)
{
name = node.GetValue("name");

if (node.HasValue("range")) {
string[] tokens = { };
tokens = node.GetValue("range").Split(',');
if(tokens.Length != 2) {
throw new ArgumentException("Found an unparseable value reading custom SOURCE_VARIABLE range");
}
if (!double.TryParse(tokens[0], out minValue)) {
throw new ArgumentException("Found an unparseable value reading custom SOURCE_VARIABLE range");
}
if (!double.TryParse(tokens[1], out maxValue)) {
throw new ArgumentException("Found an unparseable value reading custom SOURCE_VARIABLE range");
}

if(minValue > maxValue) {
double mv = minValue;
minValue = maxValue;
maxValue = mv;
}
} else {
minValue = double.MaxValue;
maxValue = double.MinValue;
}

if (node.HasValue("reverse")) {
if (!bool.TryParse(node.GetValue("reverse"), out reverse)) {
throw new ArgumentException("So is 'reverse' true or false?");
}
} else {
reverse = false;
}
}

public object Evaluate(RasterPropMonitorComputer comp)
{
double result = comp.ProcessVariable(name).MassageToDouble();
bool returnResult = (result >= minValue && result <= maxValue);
//JUtil.LogMessage(this, "Evaluate {0} ({3}, {4}) = {1}, returning {2}", name, result, returnResult, minValue, maxValue);

return (reverse) ? !returnResult : returnResult;
}
}

// A CustomVariable defines a user-defined variable that consists of one or
// more RPM variables. The CustomVariable applies a single logical operator
// across all the variables.
class CustomVariable
{
enum Operator
{
// No evaluation - returns the results of the first variable, but
// evaluates the others.
NONE,
// Evaluate each source variable as a boolean, return the logical AND of the result
AND,
// Evaluate each source variable as a boolean, return the logical OR of the result
OR,
// Evaluate each source variable as a boolean, return the logical XOR of the result
XOR,
// Evaluate each source variable as a boolean, return the logical NAND of the result
NAND,
// Evaluate each source variable as a boolean, return the logical NOR of the result
NOR,
};

public readonly string name;

private Operator op;
private List<SourceVariable> sourceVariables = new List<SourceVariable>();

public CustomVariable(ConfigNode node)
{
name = node.GetValue("name");

foreach (ConfigNode sourceVarNode in node.GetNodes("SOURCE_VARIABLE")) {
SourceVariable sourceVar = new SourceVariable(sourceVarNode);

sourceVariables.Add(sourceVar);
}

if(sourceVariables.Count == 0) {
throw new ArgumentException("Did not find any SOURCE_VARIABLE nodes in RPM_CUSTOM_VARIABLE", name);
}

string oper = node.GetValue("operator");
if (oper == Operator.NONE.ToString()) {
op = Operator.NONE;
} else if (oper == Operator.AND.ToString()) {
op = Operator.AND;
} else if (oper == Operator.OR.ToString()) {
op = Operator.OR;
} else if (oper == Operator.XOR.ToString()) {
op = Operator.XOR;
} else {
throw new ArgumentException("Found an invalid operator type in RPM_CUSTOM_VARIABLE", oper);
}
}

public object Evaluate(RasterPropMonitorComputer comp)
{
// MOARdV TODO: Reevaluate (SWIDT?) this method if math expressions are added
object evaluation = sourceVariables[0].Evaluate(comp);

for (int i = 1; i < sourceVariables.Count; ++i) {
object nextValue = sourceVariables[i].Evaluate(comp);
if(!(nextValue is bool)) {
throw new ArgumentException("CustomVariable.Evaluate - source variable did not return a bool?");
}

switch(op)
{
case Operator.AND:
case Operator.NAND:
if (nextValue is bool) {
evaluation = ((bool)evaluation) && ((bool)nextValue);
}
break;
case Operator.OR:
case Operator.NOR:
if (nextValue is bool) {
evaluation = ((bool)evaluation) || ((bool)nextValue);
}
break;
case Operator.XOR:
if (nextValue is bool) {
evaluation = ((bool)evaluation) ^ ((bool)nextValue);
}
break;
default:
throw new ArgumentException("CustomVariable.Evaluate was called with an invalid operator?");
case Operator.NONE:
break;
}
}

if (op == Operator.NAND || op == Operator.NOR) {
evaluation = !((bool)evaluation);
}

return evaluation.GetHashCode();
}
}
}
30 changes: 30 additions & 0 deletions RasterPropMonitor/Core/PropMonitorComputer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ public class RasterPropMonitorComputer: PartModule

private string[] storedStringsArray;

private Dictionary<string, CustomVariable> customVariables = new Dictionary<string, CustomVariable>();

// Processing cache!
private readonly DefaultableDictionary<string,object> resultCache = new DefaultableDictionary<string,object>(null);
// Public functions:
Expand Down Expand Up @@ -1294,6 +1296,34 @@ private object VariableToObject(string input, out bool cacheable)
resources.ListElement(resourcesAlphabetic[resourceID], tokens[2], false);
}

// Custom variables - if the first token is CUSTOM, we'll evaluate it here
if (tokens.Length > 1 && tokens[0] == "CUSTOM") {
if (customVariables.ContainsKey(input)) {
return customVariables[input].Evaluate(this);
} else {
string customName = input.Substring(7);
CustomVariable customVar = null;

// We haven't encountered this custom variable yet.
foreach (ConfigNode node in GameDatabase.Instance.GetConfigNodes("RPM_CUSTOM_VARIABLE")) {
if (node.GetValue("name") == customName) {
customVar = new CustomVariable(node);
break;
}
}

if (customVar == null) {
// We failed to find and evaluate a custom variable with this name.
// Return the unrecognized token like we do with any other unrecognized variable.
return input;
} else {
customVariables.Add(input, customVar);

return customVar.Evaluate(this);
}
}
}

// We do similar things for crew rosters.
// The syntax is therefore CREW_<index>_<FIRST|LAST|FULL>
// Part-local crew list is identical but CREWLOCAL_.
Expand Down
1 change: 1 addition & 0 deletions RasterPropMonitor/RasterPropMonitor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Core\CustomVariable.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Core\ButtonHandler.cs" />
<Compile Include="Core\FlyingCamera.cs" />
Expand Down

0 comments on commit e506f64

Please sign in to comment.