using System; using System.Collections.Generic; using System.Xml.Linq; using CodeWriter.ExpressionParser; using UnityEngine; public class MinEventActionCVarExpression : MinEventActionTargetedBase { private enum VariableType { None, CVar, RandomInt, RandomFloat, TierList } private class VariableInfo { public VariableType varType; public string cvarName; public float[] valueList; public float randomMin; public float randomMax; } public string cvarName; public MinEventActionModifyCVar.OperationTypes operation; private bool isValid = false; private ExpressionContext context; private Expression compiledExpr; private VariableInfo[] variableInfos; private MinEventParams minEventContext; private EntityAlive target; public override bool CanExecute(MinEventTypes _eventType, MinEventParams _params) { if (cvarName != null && cvarName.StartsWith("_")) { Log.Out("CVar '{0}' is readonly", new object[] { cvarName }); return false; } if (!isValid) { Log.Out("Invalid expression!"); return false; } return base.CanExecute(_eventType, _params); } public override void Execute(MinEventParams _params) { if (_params.Self.isEntityRemote && !_params.IsLocal) { return; } minEventContext = _params; if (compiledExpr == null) { return; } for (int i = 0; i < targets.Count; i++) { target = targets[i]; float cvar = target.Buffs.GetCustomVar(cvarName); float value = compiledExpr.Invoke(); switch (operation) { case MinEventActionModifyCVar.OperationTypes.set: case MinEventActionModifyCVar.OperationTypes.setvalue: cvar = value; break; case MinEventActionModifyCVar.OperationTypes.add: cvar += value; break; case MinEventActionModifyCVar.OperationTypes.subtract: cvar -= value; break; case MinEventActionModifyCVar.OperationTypes.multiply: cvar *= value; break; case MinEventActionModifyCVar.OperationTypes.divide: cvar /= ((value == 0f) ? 0.0001f : value); break; case MinEventActionModifyCVar.OperationTypes.percentadd: cvar += cvar * value; break; case MinEventActionModifyCVar.OperationTypes.percentsubtract: cvar -= cvar * value; break; } target.Buffs.SetCustomVar(cvarName, cvar); } minEventContext = null; target = null; } public override bool ParseXmlAttribute(XAttribute _attribute) { bool flag = base.ParseXmlAttribute(_attribute); if (!flag) { switch(_attribute.Name.LocalName) { case "cvar": cvarName = _attribute.Value; flag = true; break; case "expression": isValid = true; string expr = _attribute.Value; context = new ExpressionContext(); List variableInfos = new List(); Dictionary varStrs = new Dictionary(); while (true) { int nextVarStart = expr.IndexOf('['); if (nextVarStart < 0) { break; } int nextVarEnd = expr.IndexOf(']', nextVarStart); if (nextVarEnd < 0) { isValid = false; break; } string varStr = expr.Substring(nextVarStart + 1, nextVarEnd - nextVarStart - 1); VariableInfo variableInfo = null; if (varStr.StartsWith("@")) { if (!varStrs.ContainsKey(varStr)) { variableInfo = new VariableInfo(); variableInfo.varType = VariableType.CVar; variableInfo.cvarName = varStr.Substring(1); varStrs.Add(varStr, variableInfos.Count); } } else if (varStr.StartsWith("randomInt", StringComparison.OrdinalIgnoreCase)) { variableInfo = new VariableInfo(); variableInfo.varType = VariableType.RandomInt; Vector2 vector = StringParsers.ParseVector2(varStr.Substring(varStr.IndexOf('(') + 1, varStr.IndexOf(')') - (varStr.IndexOf('(') + 1))); variableInfo.randomMin = (int)vector.x; variableInfo.randomMax = (int)vector.y; } else if (varStr.StartsWith("randomFloat", StringComparison.OrdinalIgnoreCase)) { variableInfo = new VariableInfo(); variableInfo.varType = VariableType.RandomFloat; Vector2 vector = StringParsers.ParseVector2(varStr.Substring(varStr.IndexOf('(') + 1, varStr.IndexOf(')') - (varStr.IndexOf('(') + 1))); variableInfo.randomMin = vector.x; variableInfo.randomMax = vector.y; } else if (varStr.Contains(',')) { if (!varStrs.ContainsKey(varStr)) { variableInfo = new VariableInfo(); variableInfo.varType = VariableType.TierList; string[] array = varStr.Split(',', StringSplitOptions.None); variableInfo.valueList = new float[array.Length]; for (int i = 0; i < array.Length; i++) { variableInfo.valueList[i] = float.Parse(array[i]); } varStrs.Add(varStr, variableInfos.Count); } } else if (float.TryParse(varStr, out _)) { expr = expr.Remove(nextVarEnd).Remove(nextVarStart); } else { isValid = false; break; } int curIndex = varStrs.TryGetValue(varStr, out var index) ? index : variableInfos.Count; string varName = "x" + curIndex; expr = expr.Remove(nextVarStart, nextVarEnd - nextVarStart + 1).Insert(nextVarStart, varName); //Log.Out($"cur index {curIndex} var name {varStr} is new var {curIndex == variableInfos.Count}"); if (curIndex == variableInfos.Count) { context.RegisterVariable(varName, () => { return EvaluateVar(curIndex); }); } if (variableInfo != null) { variableInfos.Add(variableInfo); } } if (!isValid) { Log.Out("Invalid expression: {0}", new object[] { expr }); return false; } Log.Out($"Compiling expr {expr}..."); compiledExpr = FloatExpressionParser.Instance.Compile(expr, context, true); this.variableInfos = variableInfos.ToArray(); flag = true; break; case "operation": this.operation = EnumUtils.Parse(_attribute.Value, true); flag = true; break; } } return flag; } private float EvaluateVar(int index) { var variableInfo = variableInfos[index]; switch (variableInfo.varType) { case VariableType.CVar: return target.Buffs.GetCustomVar(variableInfo.cvarName); case VariableType.RandomInt: return Mathf.Clamp(minEventContext.Self.rand.RandomRange((int)variableInfo.randomMin, (int)variableInfo.randomMax + 1), variableInfo.randomMin, variableInfo.randomMax); case VariableType.RandomFloat: return Mathf.Clamp(minEventContext.Self.rand.RandomRange(variableInfo.randomMin, variableInfo.randomMax + 1), variableInfo.randomMin, variableInfo.randomMax); case VariableType.TierList: if (minEventContext.ParentType == MinEffectController.SourceParentType.ItemClass || minEventContext.ParentType == MinEffectController.SourceParentType.ItemModifierClass) { if (!minEventContext.ItemValue.IsEmpty()) { int tier = (int)(minEventContext.ItemValue.Quality - 1); if (tier >= 0) { return variableInfo.valueList[tier]; } } } else if (minEventContext.ParentType == MinEffectController.SourceParentType.ProgressionClass && minEventContext.ProgressionValue != null) { int level = minEventContext.ProgressionValue.CalculatedLevel(minEventContext.Self); if (level >= 0) { return variableInfo.valueList[level]; } } return 0f; default: return 0f; } } }