void AddButtonOnClick() { if (derivedStats == null) { derivedStats = ScriptableObject.CreateInstance <DerivedStatList>(); } List <DerivedStat> newDerivedStats; newDerivedStats = new List <DerivedStat>(derivedStats.stats); newDerivedStats.Add(new DerivedStat()); derivedStats.stats = newDerivedStats.ToArray(); this.Repaint(); }
public bool TryEvaluate(Actor actor, DerivedStatList derivedStats, out int outcome) { Dictionary <string, int> statSubs = new Dictionary <string, int>(); statSubs.Add("MAXHP", actor.maxHitPoints); statSubs.Add("HP", actor.hitPoints); statSubs.Add("DAMAGE", actor.damage); int numTargets = 1; if (actor.actionTarget.ToString().StartsWith("All")) { numTargets = actor.GetAvailableTargets().Count; } statSubs.Add("NUMTARGETS", numTargets); string workbench = expression.ToUpper(); foreach (string k in statSubs.Keys) { workbench = workbench.Replace(k, statSubs[k].ToString()); } // TODO: Watch out for circular definitions if (derivedStats != null) { for (int i = 0; i < derivedStats.Length; i++) { if (derivedStats.list[i].statName != statName) { int derivedValue; if (derivedStats.list[i].TryEvaluate(actor, null, out derivedValue)) { workbench = workbench.Replace(derivedStats.list[i].statName.ToUpper(), derivedValue.ToString()); } } } } if (!UnityEditor.ExpressionEvaluator.Evaluate <int>(workbench, out outcome)) { UnityEngine.MonoBehaviour.print(workbench); return(false); } else { return(true); } }
public bool TryEvaluate(string propertyName, DerivedStatList derivedStats, out int result) { string propertyNameUpper = propertyName.Trim().ToUpper(); switch (propertyNameUpper) { case "MAXHP": result = maxHitPoints; return(true); case "HP": result = hitPoints; return(true); case "INITIATIVE": result = initiative; return(true); case "DAMAGE": result = damage; return(true); case "SOURCE": result = (int)actionEffectSource; return(true); } if (propertyName.StartsWith("IMMUNITIES[") && propertyName.EndsWith("]")) { result = 0; int targetImmunity = int.Parse(propertyName.Replace("IMMUNITIES[", "").Replace("]", "")); if (immunities == null) { return(true); } for (int i = 0; i < immunities.Length; i++) { if ((int)immunities[i] == targetImmunity) { result = 1; } } return(true); } result = 0; return(false); }
public bool HasCircularReference(DerivedStatList derivedStats) { // Assumption: All stats have different names. (Enforceable?) // (Append (1) (2) etc.? ) // TODO: Recursion // Find derivedStat references within this expression // For each, drill down into their derived stats; if we ever find // the statName of this item, return true up the recursion. // // Is it necessary to check for infinite recursion, since // by definition, if there is no circular reference, // eventually we'll hit the bottom of the tree on all branches? throw new System.NotImplementedException(); }
public override void OnInspectorGUI() { #region Actor Stats Actor myActor = target as Actor; #region Actor Name myActor.actorName = EditorGUILayout.TextField("Actor Name: ", myActor.actorName); #endregion #region Action Target myActor.actionTarget = (Actor.ActionTarget)EditorGUILayout.EnumPopup("Action Target: ", myActor.actionTarget); #endregion #region Action Effect myActor.actionEffect = (Actor.ActionEffect)EditorGUILayout.EnumPopup("Action Effect: ", myActor.actionEffect); #endregion #region AES & Immunities Actor.ActionSource[] sourceValues = Enum.GetValues(typeof(Actor.ActionSource)) as Actor.ActionSource[]; string[] sourceNames = Enum.GetNames(typeof(Actor.ActionSource)); SelectionList <Actor.ActionSource> sources = new SelectionList <Actor.ActionSource>(sourceValues, sourceNames); myActor.actionEffectSource = sources.RadioList("Action Source: ", myActor.actionEffectSource, 3); myActor.immunities = sources.CheckboxList("Immunities: ", myActor.immunities, 3); #endregion #region MaxHit Points string mhpLabel = "Max Hit Points: "; int newMaxHitPoints = myActor.GetComponent <Actor>().maxHitPoints; newMaxHitPoints = EditorGUILayout.IntSlider(mhpLabel, newMaxHitPoints, 0, 1500); myActor.GetComponent <Actor>().maxHitPoints = newMaxHitPoints; #endregion #region Hit Points string hpLabel = "Hit Points: "; int newHitPoints = myActor.GetComponent <Actor>().hitPoints; newHitPoints = EditorGUILayout.IntSlider(hpLabel, newHitPoints, 1, 100); myActor.GetComponent <Actor>().hitPoints = newHitPoints; #endregion #region Initiative string iLabel = "Initiative: "; int newInitiative = myActor.GetComponent <Actor>().initiative; newInitiative = EditorGUILayout.IntSlider(iLabel, newInitiative, 10, 100); myActor.GetComponent <Actor>().initiative = newInitiative; #endregion #region Damage string dLabel = "Damage: "; int newDamage = myActor.GetComponent <Actor>().damage; newDamage = EditorGUILayout.IntSlider(dLabel, newDamage, 0, 180); myActor.GetComponent <Actor>().damage = newDamage; #endregion #region Percent Chance to Hit string pchLabel = "Percent Chance To Hit: "; int newPcth = myActor.GetComponent <Actor>().percentChanceToHit; newPcth = EditorGUILayout.IntSlider(pchLabel, newPcth, 0, 100); myActor.GetComponent <Actor>().percentChanceToHit = newPcth; #endregion #region Board Position Actor.Position[] positionValues = Enum.GetValues(typeof(Actor.Position)) as Actor.Position[]; string[] positionNames = Enum.GetNames(typeof(Actor.Position)); SelectionList <Actor.Position> positions = new SelectionList <Actor.Position>(positionValues, positionNames); myActor.boardPosition = positions.PositionGrid("Board Position: ", myActor.boardPosition, 3); #endregion #endregion #region AI Authoring if (errorBoxStyle == null) { InitializeStyles(); } showDerivedProperties = EditorGUILayout.Foldout(showDerivedProperties, new GUIContent("Derived Properties", "Properties based on static unit stats.")); if (showDerivedProperties) { derivedStats = AssetDatabase.LoadAssetAtPath("Assets/DerivedProperties.asset", typeof(DerivedStatList)) as DerivedStatList; if (derivedStats == null) { UnityEngine.MonoBehaviour.print("Nope, not there"); derivedStats = ScriptableObject.CreateInstance <DerivedStatList>(); AssetDatabase.CreateAsset(derivedStats, "Assets/DerivedProperties.asset"); AssetDatabase.SaveAssets(); } int nameFieldWidth = 80; for (int i = 0; i < derivedStats.Length; i++) { int statNameWidth = (int)EditorStyles.textField.CalcSize(new GUIContent(derivedStats.list[i].statName + " ")).x; if (statNameWidth > nameFieldWidth) { nameFieldWidth = statNameWidth; } } bool derivedPropEquationChanged = false; EditorGUILayout.BeginVertical(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Name", GUILayout.Width(nameFieldWidth)); EditorGUILayout.LabelField("Expression"); EditorGUILayout.LabelField("Current Value", GUILayout.MaxWidth(90)); EditorGUILayout.EndHorizontal(); for (int i = 0; i < (derivedStats != null ? derivedStats.Length : 0); i++) { EditorGUILayout.BeginHorizontal(); derivedStats.list[i].statName = EditorGUILayout.TextField(derivedStats.list[i].statName, GUILayout.Width(nameFieldWidth)); int derivedValue = 0; string derivedEquationErrorMessage = string.Empty; if (!derivedStats.list[i].TryEvaluate((target as Actor), derivedStats, out derivedValue)) { derivedEquationErrorMessage = "Invalid expression."; } string newDerivedPropEquation = EditorGUILayout.TextField(derivedStats.list[i].expression, derivedEquationErrorMessage.Length == 0 ? EditorStyles.textField : errorBoxStyle); if (derivedEquationErrorMessage.Length > 0) { GUI.Label(GUILayoutUtility.GetLastRect(), new GUIContent(string.Empty, derivedEquationErrorMessage)); } else { EditorGUILayout.LabelField(derivedValue.ToString(), GUILayout.MaxWidth(90)); } if (newDerivedPropEquation != derivedStats.list[i].expression) { derivedPropEquationChanged = true; } derivedStats.list[i].expression = newDerivedPropEquation; EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); bool added = false; if (GUILayout.Button("Add")) { added = true; if (derivedStats == null) { derivedStats = new DerivedStatList(); } List <DerivedStat> newDerivedStats; if (derivedStats.list != null) { newDerivedStats = new List <DerivedStat>(derivedStats.list); } else { newDerivedStats = new List <DerivedStat>(); } newDerivedStats.Add(new DerivedStat()); derivedStats.list = newDerivedStats.ToArray(); this.Repaint(); } if (GUI.changed || added) { EditorUtility.SetDirty(derivedStats); serializedObject.ApplyModifiedProperties(); AssetDatabase.SaveAssets(); } if (derivedPropEquationChanged) { this.Repaint(); } DrawDefaultInspector(); } #endregion }
public List <Actor> Apply(Actor initiator, List <Actor> availableTargets, DerivedStatList derivedStats) { string operation = null; if (expression.Contains(">=")) { operation = ">="; } else if (expression.Contains("<=")) { operation = "<="; } else if (expression.Contains("!=")) { operation = "!="; } else if (expression.Contains(">")) { operation = ">"; } else if (expression.Contains("<")) { operation = "<"; } else if (expression.Contains("=")) { operation = "="; } if (operation == null) { throw new System.Exception("Invalid operator in SelectionRule expression [" + expression + "]. Only <, >, <=, >=, =, != are allowed."); } string[] components = expression.Split(new string[] { operation }, System.StringSplitOptions.RemoveEmptyEntries); List <Actor> newAvailableTargets = new List <Actor>(); for (int i = 0; i < availableTargets.Count; i++) { Actor candidateTarget = availableTargets[i]; string[] left = components[0].ToLower().Split('.'); string[] right = components[1].ToLower().Split('.'); int leftValue, rightValue; if (left[0] == "my") { initiator.TryEvaluate(left[1], derivedStats, out leftValue); } if (left[0] == "target") { candidateTarget.TryEvaluate(left[1], derivedStats, out leftValue); } if (left[0] == "targets") { int[] allValues = new int[availableTargets.Count]; for (int j = 0; j < allValues.Length; j++) { availableTargets[j].TryEvaluate(left[2], derivedStats, out allValues[j]); } int outcome = allValues[0]; for (int j = 1; j < allValues.Length; j++) { if (left[1] == "max" && allValues[j] > outcome) { outcome = allValues[j]; } if (left[1] == "min" && allValues[i] < outcome) { outcome = allValues[j]; } } leftValue = outcome; } if (right[0] == "my") { initiator.TryEvaluate(right[1], derivedStats, out rightValue); } if (right[0] == "target") { candidateTarget.TryEvaluate(right[1], derivedStats, out rightValue); } if (right[0] == "targets") { int[] allValues = new int[availableTargets.Count]; for (int j = 0; j < allValues.Length; j++) { availableTargets[j].TryEvaluate(right[2], derivedStats, out allValues[j]); } int outcome = allValues[0]; for (int j = 1; j < allValues.Length; j++) { if (right[1] == "max" && allValues[j] > outcome) { outcome = allValues[j]; } if (right[1] == "min" && allValues[i] < outcome) { outcome = allValues[j]; } } rightValue = outcome; } } }
public List <Actor> Apply(Actor initiator, List <Actor> availableTargets, DerivedStatList derivedStats) { // Parse the expression. string operation = null; if (expression.Contains(">=")) { operation = ">="; } else if (expression.Contains("<=")) { operation = "<="; } else if (expression.Contains("!=")) { operation = "!="; } else if (expression.Contains(">")) { operation = ">"; } else if (expression.Contains("<")) { operation = "<"; } else if (expression.Contains("=")) { operation = "="; } if (operation == null) { throw new System.Exception("Invalid operator in SelectionRule expression [" + expression + "]. Only <, >, <=, >=, =, != are allowed."); } string[] components = expression.Split(new string[] { operation }, System.StringSplitOptions.RemoveEmptyEntries); // Require a simple expression; each side is a property of either the initiator or a target..? What about things like targets.max? // Maybe start with the left side? Oh, or, evaluate for each either way. Derp. List <Actor> newAvailableTargets = new List <Actor>(); for (int i = 0; i < availableTargets.Count; i++) { Actor candidateTarget = availableTargets[i]; string[] left = components[0].ToLower().Split('.'); string[] right = components[1].ToLower().Split('.'); int leftValue, rightValue; // Evaluate left if (left[0] == "my") { // Evaluate on initiator initiator.TryEvaluate(left[1], derivedStats, out leftValue); } if (left[0] == "target") { // Evaluate on candidateTarget candidateTarget.TryEvaluate(left[1], derivedStats, out leftValue); } if (left[0] == "targets") { // max or min here int[] allValues = new int[availableTargets.Count]; for (int j = 0; j < allValues.Length; j++) { availableTargets[j].TryEvaluate(left[2], derivedStats, out allValues[j]); } int outcome = allValues[0]; for (int j = 1; j < allValues.Length; j++) { if (left[1] == "max" && allValues[j] > outcome) { outcome = allValues[j]; } if (left[1] == "min" && allValues[i] < outcome) { outcome = allValues[j]; } } leftValue = outcome; } // Evaluate right if (right[0] == "my") { // Evaluate on initiator initiator.TryEvaluate(right[1], derivedStats, out rightValue); } if (right[0] == "target") { // Evaluate on candidateTarget candidateTarget.TryEvaluate(right[1], derivedStats, out rightValue); } if (right[0] == "targets") { // max or min here int[] allValues = new int[availableTargets.Count]; for (int j = 0; j < allValues.Length; j++) { availableTargets[j].TryEvaluate(right[2], derivedStats, out allValues[j]); } int outcome = allValues[0]; for (int j = 1; j < allValues.Length; j++) { if (right[1] == "max" && allValues[j] > outcome) { outcome = allValues[j]; } if (right[1] == "min" && allValues[i] < outcome) { outcome = allValues[j]; } } rightValue = outcome; } } }
public override void OnInspectorGUI() { Actor editActor = target as Actor; Actor.ActionSource[] immTypeVal = Enum.GetValues(typeof(Actor.ActionSource)) as Actor.ActionSource[]; string[] immTypeName = Enum.GetNames(typeof(Actor.ActionSource)); for (int i = 0; i < immTypeName.Length; i++) { immTypeName[i] += '\t'; } Actor.Position[] posVal = Enum.GetValues(typeof(Actor.Position)) as Actor.Position[]; string[] posName = Enum.GetNames(typeof(Actor.Position)); for (int i = 0; i < posName.Length; i++) { posName[i] += '\t'; } immunityList = new SelectionList <Actor.ActionSource>(immTypeVal, immTypeName); positionList = new SelectionList <Actor.Position>(posVal, posName); #region Name, and HP EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Name"); actName = GUILayout.TextField(editActor.actorName); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Max Hit Points"); maxHP = EditorGUILayout.IntSlider(maxHP, 1, 10000); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Current Hit Points"); currentHP = EditorGUILayout.IntSlider(currentHP, 0, maxHP); EditorGUILayout.EndHorizontal(); #endregion #region Position editPosition = EditorGUILayout.Foldout(editPosition, "Position"); if (editPosition) { boardPos = positionList.RadioList("", boardPos, 2); } #endregion #region Damage EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Damage"); dmg = EditorGUILayout.IntSlider(dmg, 0, 180); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Effect"); EditorGUILayout.EnumFlagsField(effect); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Damage Type"); EditorGUILayout.EnumFlagsField(dmgType); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Attack type"); EditorGUILayout.EnumFlagsField(targetType); EditorGUILayout.EndHorizontal(); #endregion #region Immunities editImmunity = EditorGUILayout.Foldout(editImmunity, "Immunities"); if (editImmunity) { immunities = immunityList.CheckboxList("Immunities", immunities, 2); } #endregion #region AI editAI = EditorGUILayout.Foldout(editAI, "AI Options"); if (editAI) { derivedStats = AssetDatabase.LoadAssetAtPath("Assets/DerivedProperties.asset", typeof(DerivedStatList)) as DerivedStatList; if (derivedStats == null) { derivedStats = ScriptableObject.CreateInstance <DerivedStatList>(); AssetDatabase.CreateAsset(derivedStats, "Assets/DerivedProperties.asset"); AssetDatabase.SaveAssets(); } EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Chance to Hit"); chanceToHit = EditorGUILayout.IntSlider(chanceToHit, 0, 100); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Inititive"); init = EditorGUILayout.IntSlider(init, 5, 100); init = init / 5; init *= 5; EditorGUILayout.EndHorizontal(); EditorGUILayout.Separator(); EditorGUILayout.LabelField("AI Targeting Filters"); EditorGUILayout.LabelField("Filter order does matter (Ordered top to bottom)"); EditorGUILayout.Separator(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Name", GUILayout.MaxWidth(80)); EditorGUILayout.LabelField("Expression"); EditorGUILayout.EndHorizontal(); FilterLayout(); EditorGUILayout.Separator(); bool added = false; if (GUILayout.Button("Add Filter")) { added = true; AddButtonOnClick(); } if (GUI.changed || added) { EditorUtility.SetDirty(derivedStats); serializedObject.ApplyModifiedProperties(); AssetDatabase.SaveAssets(); } } #endregion #region Assignment editActor.actorName = actName; editActor.boardPosition = boardPos; editActor.maxHitPoints = maxHP; editActor.damage = dmg; editActor.actionEffect = effect; editActor.actionEffectSource = dmgType; editActor.immunities = immunities; editActor.actionTarget = targetType; editActor.percentChanceToHit = chanceToHit; editActor.initiative = init; editActor.hitPoints = currentHP; #endregion }
private void DisplayDerivedProperties() { showDerivedProperties = EditorGUILayout.Foldout(showDerivedProperties, new GUIContent("Derived Properties", "Properties based on static unit stats.")); if (showDerivedProperties) { derivedStats = AssetDatabase.LoadAssetAtPath("Assets/DerivedProperties.asset", typeof(DerivedStatList)) as DerivedStatList; if (derivedStats == null) { UnityEngine.MonoBehaviour.print("Nope, not there"); derivedStats = ScriptableObject.CreateInstance <DerivedStatList>(); AssetDatabase.CreateAsset(derivedStats, "Assets/DerivedProperties.asset"); AssetDatabase.SaveAssets(); } int nameFieldWidth = 80; for (int i = 0; i < derivedStats.Length; i++) { int statNameWidth = (int)EditorStyles.textField.CalcSize(new GUIContent(derivedStats.list[i].statName + " ")).x; if (statNameWidth > nameFieldWidth) { nameFieldWidth = statNameWidth; } } bool derivedPropEquationChanged = false; EditorGUILayout.BeginVertical(); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Name", GUILayout.Width(nameFieldWidth)); EditorGUILayout.LabelField("Expression"); EditorGUILayout.LabelField("Current Value", GUILayout.MaxWidth(90)); EditorGUILayout.EndHorizontal(); for (int i = 0; i < (derivedStats != null ? derivedStats.Length : 0); i++) { EditorGUILayout.BeginHorizontal(); // TODO: Watch out for repeats derivedStats.list[i].statName = EditorGUILayout.TextField(derivedStats.list[i].statName, GUILayout.Width(nameFieldWidth)); int derivedValue = 0; string derivedEquationErrorMessage = string.Empty; // How to detect expression errors vs. circular definitions? if (!derivedStats.list[i].TryEvaluate((target as Actor), derivedStats, out derivedValue)) { derivedEquationErrorMessage = "Invalid expression."; } string newDerivedPropEquation = EditorGUILayout.TextField(derivedStats.list[i].expression, derivedEquationErrorMessage.Length == 0 ? EditorStyles.textField : errorBoxStyle); if (derivedEquationErrorMessage.Length > 0) { GUI.Label(GUILayoutUtility.GetLastRect(), new GUIContent(string.Empty, derivedEquationErrorMessage)); } else { EditorGUILayout.LabelField(derivedValue.ToString(), GUILayout.MaxWidth(90)); } if (newDerivedPropEquation != derivedStats.list[i].expression) { derivedPropEquationChanged = true; } derivedStats.list[i].expression = newDerivedPropEquation; EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndVertical(); bool added = false; if (GUILayout.Button("Add")) { added = true; if (derivedStats == null) { derivedStats = new DerivedStatList(); } List <DerivedStat> newDerivedStats; if (derivedStats.list != null) { newDerivedStats = new List <DerivedStat>(derivedStats.list); } else { newDerivedStats = new List <DerivedStat>(); } newDerivedStats.Add(new DerivedStat()); derivedStats.list = newDerivedStats.ToArray(); this.Repaint(); } if (GUI.changed || added) { EditorUtility.SetDirty(derivedStats); serializedObject.ApplyModifiedProperties(); AssetDatabase.SaveAssets(); } if (derivedPropEquationChanged) { this.Repaint(); } } }