From 5c5a5cc66227439be7a7b61da5d1ff68cf187ac3 Mon Sep 17 00:00:00 2001
From: hch <305670599@qq.com>
Date: 星期一, 18 八月 2025 22:47:05 +0800
Subject: [PATCH] 117 【武将】武将系统 - 战力计算

---
 Main/Common/Jace/CalculationEngine.cs.meta                                            |    2 
 Main/System/Hero/HeroInfo.Fetter.cs                                                   |   51 
 Main/Common/Jace/Util/MemoryCache.cs                                                  |  165 +
 Main/Common/Jace/Util/EngineUtil.cs.meta                                              |    2 
 Main/Common/Jace/Operations/Modulo.cs                                                 |   20 
 Main/Common/Jace/Tokenizer/TokenReader.cs                                             |  255 ++
 Main/System/Hero/HeroInfo.InheritPer.cs.meta                                          |    2 
 Main/Common/Jace/JaceOptions.cs                                                       |   87 
 Main/Common/Jace/Operations/Variable.cs.meta                                          |    2 
 Main/System/Tip/PowerAddWin.cs                                                        |    6 
 Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta                                |    2 
 Main/Common/Jace/Operations/And.cs                                                    |   19 
 Main/Common/Jace/Operations/Subtraction.cs                                            |   20 
 Main/System/Hero/HeroInfo.Lineup.cs                                                   |    3 
 Main/Config/Configs/FightPowerRatioConfig.cs                                          |  107 +
 Main/Common/Jace/Operations/Multiplication.cs                                         |   20 
 Main/Common/Jace/Execution/IConstantRegistry.cs.meta                                  |    2 
 Main/Common/Jace/Execution/ParameterInfo.cs                                           |   14 
 Main/Common/Jace/Operations/NotEqual.cs                                               |   20 
 Main/Common/Jace/Util/TypeExtensions.cs                                               |   43 
 Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB122_tagSCHeroInfo.cs         |    4 
 Main/Common/Jace/Operations/LessOrEqualThan.cs                                        |   20 
 Main/Common/Jace/Execution/IFunctionRegistry.cs                                       |   13 
 Main/Common/Jace/AstBuilder.cs.meta                                                   |    2 
 Main/Config/PartialConfigs/HeroTalentConfig.cs                                        |    4 
 Main/Common/Jace/Execution/ConstantRegistry.cs.meta                                   |    2 
 Main/Common/Jace/Execution/DynamicCompiler.cs.meta                                    |    2 
 Main/Utility/JaceCalculator.cs                                                        |   36 
 Main/System/Hero/HeroInfo.InheritPer.cs                                               |   24 
 Main/Common/Jace/Operations/Division.cs.meta                                          |    2 
 Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0403_tagPlayerLoginLoadOK.cs |    6 
 Main/Common/Jace/Operations/Exponentiation.cs                                         |   20 
 Main/Common/Jace/Execution/FunctionInfo.cs.meta                                       |    2 
 Main/Common/Jace/Operations/LessThan.cs.meta                                          |    2 
 Main/System/OpenServerActivity/OperationTimeHepler.cs                                 |   72 
 Main/Common/Jace/Operations/Multiplication.cs.meta                                    |    2 
 Main/Common/Jace/Operations/NotEqual.cs.meta                                          |    2 
 Main/System/Equip/EquipModel.cs                                                       |   18 
 Main/Common/Jace/Tokenizer/TokenType.cs.meta                                          |    2 
 Main/Common/Jace/Operations.meta                                                      |    2 
 Main/Common/Jace/Execution/FunctionInfo.cs                                            |   32 
 Main/System/Equip/EquipExchangeCell.cs                                                |    5 
 Main/Common/Jace/Operations/Division.cs                                               |   20 
 Main/Common/Jace/Operations/Operation.cs.meta                                         |    2 
 Main/Common/Jace/Tokenizer/TokenType.cs                                               |   18 
 Main/System/Hero/HeroManager.cs                                                       |    4 
 Main/Common/Jace/Execution/ExecutionMode.cs.meta                                      |    2 
 Main/Common/Jace/Execution/DynamicCompiler.cs                                         |  327 +++
 Main/Common/Jace/VariableNotDefinedException.cs.meta                                  |    2 
 Main/Common/Jace/ParseException.cs                                                    |   19 
 Main/Common/Jace/Util/TypeExtensions.cs.meta                                          |    2 
 Main/System/Equip/EquipExchangeWin.cs                                                 |    2 
 Main/Common/Jace/Execution/FormulaBuilder.cs                                          |  131 +
 Main/System/HeroUI/HeroTrainWin.cs                                                    |    6 
 Main/Common/Jace/FormulaContext.cs.meta                                               |    2 
 Main/Common/Jace/Tokenizer/Token.cs.meta                                              |    2 
 Main/Common/Jace/Operations/Exponentiation.cs.meta                                    |    2 
 Main/Common/Jace/Operations/GreaterThan.cs.meta                                       |    2 
 Main/Common/Jace/Execution/ConstantRegistry.cs                                        |   79 
 Main/Common/Jace/Execution/FunctionRegistry.cs                                        |  119 +
 Main/System/HeroUI/HeroUIManager.Reborn.cs                                            |   83 
 Main/Common/Jace/Tokenizer.meta                                                       |    2 
 Main/Common/Jace/Util/MemoryCache.cs.meta                                             |    2 
 Main/Common/Jace/Util/MathExtended.cs.meta                                            |    2 
 Main/System/HeroUI/HeroPosWin.cs                                                      |   30 
 Main/Common/Jace/Operations/Equal.cs                                                  |   20 
 Main/Common/Jace/Execution/FunctionRegistry.cs.meta                                   |    2 
 Main/System/Hero/HeroAttrType.cs                                                      |    8 
 Main/System/Team/TeamHero.cs                                                          |    2 
 Main/Common/Jace/Execution/IConstantRegistry.cs                                       |   15 
 Main/Common/Jace/Util/MathExtended.cs                                                 |   81 
 Main/System/HeroUI/HeroUIManager.cs                                                   |  295 --
 Main/Common/Jace/Execution/IExecutor.cs                                               |   16 
 Main/Common/Jace/Operations/Constant.cs.meta                                          |    2 
 Main/Common/Jace/CalculationEngine.cs                                                 |  483 ++++
 Main/Common/Jace/Operations/GreaterOrEqualThan.cs                                     |   20 
 Main/Common/Jace/VariableNotDefinedException.cs                                       |   23 
 Main/Common/Jace/Operations/Modulo.cs.meta                                            |    2 
 Main/Common/Jace/Operations/And.cs.meta                                               |    2 
 Main/Common/Jace/Operations/GreaterThan.cs                                            |   20 
 Main/Common/Jace/Util.meta                                                            |    2 
 Main/System/Hero/HeroInfo.Talent.cs                                                   |   71 
 Main/Common/Jace/Optimizer.cs                                                         |   76 
 Main/Common/Jace/Execution/ConstantInfo.cs                                            |   23 
 Main/System/Hero/HeroInfo.Properties.cs                                               |  141 
 Main/Common/Jace/Operations/Equal.cs.meta                                             |    2 
 Main/Common/Jace/DataType.cs                                                          |   13 
 Main/Config/PartialConfigs/PlayerPropertyConfig.cs                                    |   52 
 Main/Common/Jace/Operations/Function.cs                                               |   33 
 Main/Config/Configs/HeroConfig.cs                                                     |    6 
 Main/System/Store/StoreModel.cs                                                       |    2 
 Main/Common/Jace/Optimizer.cs.meta                                                    |    2 
 Main/Common/Jace/Execution/ConstantInfo.cs.meta                                       |    2 
 Main/System/Main/FightPowerManager.cs                                                 |  627 ++++-
 Main/Common/Jace/Execution/Interpreter.cs.meta                                        |    2 
 Main/Common/Jace/Util/FuncAdapter.cs                                                  |  126 +
 Main/Common/Jace/Operations/LessOrEqualThan.cs.meta                                   |    2 
 Main/Config/Configs/PlayerLVConfig.cs                                                 |   11 
 Main/System/Hero/HeroInfo.cs                                                          |   96 
 Main/System/UIBase/UIBase.cs                                                          |   22 
 Main/Common/Jace/Tokenizer/TokenReader.cs.meta                                        |    2 
 Main/Common/Jace/Util/MathUtil.cs.meta                                                |    2 
 Main/Common/Jace/Execution/ParameterInfo.cs.meta                                      |    2 
 Main/Common/Jace/Execution/Interpreter.cs                                             |  252 ++
 Main/Common/Jace/Util/MathUtil.cs                                                     |   30 
 Main/Common/Jace/Util/EngineUtil.cs                                                   |   46 
 Main/Common/Jace/Operations/Addition.cs.meta                                          |    2 
 Main/Common/Jace/Operations/Operation.cs                                              |   23 
 Main/Common/Jace/Execution/ExecutionMode.cs                                           |   13 
 Main/System/HeroUI/HeroUIManager.OnTeam.cs                                            |  311 +++
 Main/Common/Jace/Operations/UnaryMinus.cs.meta                                        |    2 
 Main/Config/ConfigManager.cs                                                          |    3 
 Main/System/HeroUI/HeroUIManager.Reborn.cs.meta                                       |    2 
 Main/Component/UI/Effect/UIEffectPlayer.cs                                            |    4 
 Main/System/CustomizedGift/CustomizedGiftModel.cs                                     |    8 
 Main/System/KnapSack/Logic/ItemLogicUtility.cs                                        |   19 
 Main/Common/Jace/Operations/Subtraction.cs.meta                                       |    2 
 Main/System/HeroUI/HeroUIManager.Collect.cs                                           |   46 
 Main/Common/Jace.meta                                                                 |    2 
 Main/Common/Jace/JaceOptions.cs.meta                                                  |    2 
 Main/Common/Jace/Execution/IExecutor.cs.meta                                          |    2 
 Main/System/HeroUI/HeroFormationCell.cs                                               |    2 
 Main/System/Team/TeamBase.cs                                                          |   24 
 Main/Common/Jace/Operations/Function.cs.meta                                          |    2 
 Main/Common/Jace/Tokenizer/Token.cs                                                   |   33 
 Main/Common/Jace/Execution/FormulaBuilder.cs.meta                                     |    2 
 Main/Common/Jace/AstBuilder.cs                                                        |  372 +++
 Main/Common/Jace/Operations/Variable.cs                                               |   37 
 Main/Config/Configs/FightPowerRatioConfig.cs.meta                                     |    2 
 Main/Common/Jace/Execution/IFunctionRegistry.cs.meta                                  |    2 
 Main/System/Hero/HeroInfo.Awake.cs                                                    |   83 
 Main/Common/Jace/FormulaContext.cs                                                    |   25 
 Main/Common/Jace/Execution.meta                                                       |    2 
 Main/Common/Jace/DataType.cs.meta                                                     |    2 
 Main/System/HeroUI/HeroFormationWin.cs                                                |    2 
 Main/Common/Jace/ParseException.cs.meta                                               |    2 
 Main/System/HeroUI/HeroLVBreakWin.cs                                                  |    2 
 /dev/null                                                                             |   11 
 Main/Common/Jace/Util/FuncAdapter.cs.meta                                             |    2 
 Main/System/Hero/HeroInfo.Break.cs                                                    |   81 
 Main/Common/Jace/Operations/Or.cs                                                     |   19 
 Main/Common/Jace/Operations/Addition.cs                                               |   20 
 Main/Common/Jace/Operations/Constant.cs                                               |   48 
 Main/Common/Jace/Operations/LessThan.cs                                               |   20 
 Main/Utility/JaceCalculator.cs.meta                                                   |    2 
 Main/Common/Jace/Operations/Or.cs.meta                                                |    2 
 Main/Common/Jace/Operations/UnaryMinus.cs                                             |   18 
 Main/System/HeroUI/HeroUIManager.Collect.cs.meta                                      |    2 
 Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta                                       |    2 
 149 files changed, 4,982 insertions(+), 892 deletions(-)

diff --git a/Main/Core/GameEngine/Common.meta b/Main/Common/Jace.meta
similarity index 76%
copy from Main/Core/GameEngine/Common.meta
copy to Main/Common/Jace.meta
index 29b34bc..df34b7a 100644
--- a/Main/Core/GameEngine/Common.meta
+++ b/Main/Common/Jace.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
+guid: ca353b9337f7a0749a543c104493ccc5
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}
diff --git a/Main/Common/Jace/AstBuilder.cs b/Main/Common/Jace/AstBuilder.cs
new file mode 100644
index 0000000..713eb25
--- /dev/null
+++ b/Main/Common/Jace/AstBuilder.cs
@@ -0,0 +1,372 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Jace.Execution;
+using Jace.Operations;
+using Jace.Tokenizer;
+using Jace.Util;
+
+namespace Jace
+{
+    public class AstBuilder
+    {
+        private readonly IFunctionRegistry functionRegistry;
+        private readonly IConstantRegistry localConstantRegistry;
+        private readonly bool caseSensitive;
+        private Dictionary<char, int> operationPrecedence = new Dictionary<char, int>();
+        private Stack<Operation> resultStack = new Stack<Operation>();
+        private Stack<Token> operatorStack = new Stack<Token>();
+        private Stack<int> parameterCount = new Stack<int>();
+
+        public AstBuilder(IFunctionRegistry functionRegistry, bool caseSensitive, IConstantRegistry compiledConstants = null)
+        {
+            if (functionRegistry == null)
+                throw new ArgumentNullException("functionRegistry");
+
+            this.functionRegistry = functionRegistry;
+            this.localConstantRegistry = compiledConstants ?? new ConstantRegistry(caseSensitive);
+            this.caseSensitive = caseSensitive;
+
+            operationPrecedence.Add('(', 0);
+            operationPrecedence.Add('&', 1);
+            operationPrecedence.Add('|', 1);
+            operationPrecedence.Add('<', 2);
+            operationPrecedence.Add('>', 2);
+            operationPrecedence.Add('鈮�', 2);
+            operationPrecedence.Add('鈮�', 2);
+            operationPrecedence.Add('鈮�', 2);
+            operationPrecedence.Add('=', 2);
+            operationPrecedence.Add('+', 3);
+            operationPrecedence.Add('-', 3);
+            operationPrecedence.Add('*', 4);
+            operationPrecedence.Add('/', 4);
+            operationPrecedence.Add('%', 4);
+            operationPrecedence.Add('_', 6);
+            operationPrecedence.Add('^', 5);
+        }
+
+        public Operation Build(IList<Token> tokens)
+        {
+            resultStack.Clear();
+            operatorStack.Clear();
+
+            parameterCount.Clear();
+
+            foreach (Token token in tokens)
+            {
+                object value = token.Value;
+
+                switch (token.TokenType)
+                {
+                    case TokenType.Integer:
+                        resultStack.Push(new IntegerConstant((int)token.Value));
+                        break;
+                    case TokenType.FloatingPoint:
+                        resultStack.Push(new FloatingPointConstant((double)token.Value));
+                        break;
+                    case TokenType.Text:
+                        if (functionRegistry.IsFunctionName((string)token.Value))
+                        {
+                            operatorStack.Push(token);
+                            parameterCount.Push(1);
+                        }
+                        else
+                        {
+                            string tokenValue = (string)token.Value;
+                            if (localConstantRegistry.IsConstantName(tokenValue))
+                            {
+                                resultStack.Push(new FloatingPointConstant(localConstantRegistry.GetConstantInfo(tokenValue).Value));
+                            }
+                            else
+                            {
+                                if (!caseSensitive)
+                                {
+                                    tokenValue = tokenValue.ToLowerFast();
+                                }
+                                resultStack.Push(new Variable(tokenValue));
+                            }
+                        }
+                        break;
+                    case TokenType.LeftBracket:
+                        operatorStack.Push(token);
+                        break;
+                    case TokenType.RightBracket:
+                        PopOperations(true, token);
+                        //parameterCount.Pop();
+                        break;
+                    case TokenType.ArgumentSeparator:
+                        PopOperations(false, token);
+                        parameterCount.Push(parameterCount.Pop() + 1);
+                        break;
+                    case TokenType.Operation:
+                        Token operation1Token = token;
+                        char operation1 = (char)operation1Token.Value;
+
+                        while (operatorStack.Count > 0 && (operatorStack.Peek().TokenType == TokenType.Operation ||
+                            operatorStack.Peek().TokenType == TokenType.Text))
+                        {
+                            Token operation2Token = operatorStack.Peek();
+                            bool isFunctionOnTopOfStack = operation2Token.TokenType == TokenType.Text;
+
+                            if (!isFunctionOnTopOfStack)
+                            {
+                                char operation2 = (char)operation2Token.Value;
+
+                                if ((IsLeftAssociativeOperation(operation1) &&
+                                        operationPrecedence[operation1] <= operationPrecedence[operation2]) ||
+                                    (operationPrecedence[operation1] < operationPrecedence[operation2]))
+                                {
+                                    operatorStack.Pop();
+                                    resultStack.Push(ConvertOperation(operation2Token));
+                                }
+                                else
+                                {
+                                    break;
+                                }
+                            }
+                            else
+                            {
+                                operatorStack.Pop();
+                                resultStack.Push(ConvertFunction(operation2Token));
+                            }
+                        }
+
+                        operatorStack.Push(operation1Token);
+                        break;
+                }
+            }
+
+            PopOperations(false, null);
+
+            VerifyResultStack();
+
+            return resultStack.First();
+        }
+
+        private void PopOperations(bool untillLeftBracket, Token? currentToken)
+        {
+            if (untillLeftBracket && !currentToken.HasValue)
+                throw new ArgumentNullException("currentToken", "If the parameter \"untillLeftBracket\" is set to true, " +
+                    "the parameter \"currentToken\" cannot be null.");
+
+            while (operatorStack.Count > 0 && operatorStack.Peek().TokenType != TokenType.LeftBracket)
+            {
+                Token token = operatorStack.Pop();
+
+                switch (token.TokenType)
+                {
+                    case TokenType.Operation:
+                        resultStack.Push(ConvertOperation(token));
+                        break;
+                    case TokenType.Text:
+                        resultStack.Push(ConvertFunction(token));
+                        break;
+                }
+            }
+
+            if (untillLeftBracket)
+            {
+                if (operatorStack.Count > 0 && operatorStack.Peek().TokenType == TokenType.LeftBracket)
+                    operatorStack.Pop();
+                else
+                    throw new ParseException(string.Format("No matching left bracket found for the right " +
+                        "bracket at position {0}.", currentToken.Value.StartPosition));
+            }
+            else
+            {
+                if (operatorStack.Count > 0 && operatorStack.Peek().TokenType == TokenType.LeftBracket 
+                    && !(currentToken.HasValue && currentToken.Value.TokenType == TokenType.ArgumentSeparator))
+                    throw new ParseException(string.Format("No matching right bracket found for the left " +
+                        "bracket at position {0}.", operatorStack.Peek().StartPosition));
+            }
+        }
+
+        private Operation ConvertOperation(Token operationToken)
+        {
+            try
+            {
+                DataType dataType;
+                Operation argument1;
+                Operation argument2;
+                Operation divisor;
+                Operation divident;
+
+                switch ((char)operationToken.Value)
+                {
+                    case '+':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new Addition(dataType, argument1, argument2);
+                    case '-':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new Subtraction(dataType, argument1, argument2);
+                    case '*':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new Multiplication(dataType, argument1, argument2);
+                    case '/':
+                        divisor = resultStack.Pop();
+                        divident = resultStack.Pop();
+
+                        return new Division(DataType.FloatingPoint, divident, divisor);
+                    case '%':
+                        divisor = resultStack.Pop();
+                        divident = resultStack.Pop();
+
+                        return new Modulo(DataType.FloatingPoint, divident, divisor);
+                    case '_':
+                        argument1 = resultStack.Pop();
+
+                        return new UnaryMinus(argument1.DataType, argument1);
+                    case '^':
+                        Operation exponent = resultStack.Pop();
+                        Operation @base = resultStack.Pop();
+
+                        return new Exponentiation(DataType.FloatingPoint, @base, exponent);
+                    case '&':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new And(dataType, argument1, argument2);
+                    case '|':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new Or(dataType, argument1, argument2);
+                    case '<':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new LessThan(dataType, argument1, argument2);
+                    case '鈮�':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new LessOrEqualThan(dataType, argument1, argument2);
+                    case '>':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new GreaterThan(dataType, argument1, argument2);
+                    case '鈮�':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new GreaterOrEqualThan(dataType, argument1, argument2);
+                    case '=':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new Equal(dataType, argument1, argument2);
+                    case '鈮�':
+                        argument2 = resultStack.Pop();
+                        argument1 = resultStack.Pop();
+                        dataType = RequiredDataType(argument1, argument2);
+
+                        return new NotEqual(dataType, argument1, argument2);
+                    default:
+                        throw new ArgumentException(string.Format("Unknown operation \"{0}\".", operationToken), "operation");
+                }
+            }
+            catch (InvalidOperationException)
+            {
+                // If we encounter a Stack empty issue this means there is a syntax issue in 
+                // the mathematical formula
+                throw new ParseException(string.Format("There is a syntax issue for the operation \"{0}\" at position {1}. " +
+                    "The number of arguments does not match with what is expected.", operationToken.Value, operationToken.StartPosition));
+            }
+        }
+
+        private Operation ConvertFunction(Token functionToken)
+        {
+            try
+            {
+                string functionName = ((string)functionToken.Value).ToLowerInvariant();
+
+                if (functionRegistry.IsFunctionName(functionName))
+                {
+                    FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(functionName);
+
+                    int numberOfParameters;
+
+                    if (functionInfo.IsDynamicFunc) {
+                        numberOfParameters = parameterCount.Pop();
+                    }
+                    else {
+                        parameterCount.Pop();
+                        numberOfParameters = functionInfo.NumberOfParameters;
+                    }
+                    
+                    List<Operation> operations = new List<Operation>();
+                    for (int i = 0; i < numberOfParameters; i++)
+                        operations.Add(resultStack.Pop());
+                    operations.Reverse();
+
+                    return new Function(DataType.FloatingPoint, functionName, operations, functionInfo.IsIdempotent);
+                }
+                else
+                {
+                    throw new ArgumentException(string.Format("Unknown function \"{0}\".", functionToken.Value), "function");
+                }
+            }
+            catch (InvalidOperationException)
+            {
+                // If we encounter a Stack empty issue this means there is a syntax issue in 
+                // the mathematical formula
+                throw new ParseException(string.Format("There is a syntax issue for the function \"{0}\" at position {1}. " +
+                    "The number of arguments does not match with what is expected.", functionToken.Value, functionToken.StartPosition));
+            }
+        }
+
+        private void VerifyResultStack()
+        {
+            if(resultStack.Count > 1)
+            {
+                Operation[] operations = resultStack.ToArray();
+
+                for (int i = 1; i < operations.Length; i++)
+                {
+                    Operation operation = operations[i];
+
+                    if (operation.GetType() == typeof(IntegerConstant))
+                    {
+                        IntegerConstant constant = (IntegerConstant)operation;
+                        throw new ParseException(string.Format("Unexpected integer constant \"{0}\" found.", constant.Value));
+                    }
+                    else if (operation.GetType() == typeof(FloatingPointConstant))
+                    {
+                        FloatingPointConstant constant = (FloatingPointConstant)operation;
+                        throw new ParseException(string.Format("Unexpected floating point constant \"{0}\" found.", constant.Value)); 
+                    }
+                }
+
+                throw new ParseException("The syntax of the provided formula is not valid.");
+            }
+        }
+
+        private bool IsLeftAssociativeOperation(char character)
+        {
+            return character == '*' || character == '+' || character == '-' || character == '/';
+        }
+
+        private DataType RequiredDataType(Operation argument1, Operation argument2)
+        {
+            return (argument1.DataType == DataType.FloatingPoint || argument2.DataType == DataType.FloatingPoint) ? DataType.FloatingPoint : DataType.Integer;
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/AstBuilder.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/AstBuilder.cs.meta
index e37f87e..2b31c6d 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/AstBuilder.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: de824ec81c42eae4888376c8a458a540
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/CalculationEngine.cs b/Main/Common/Jace/CalculationEngine.cs
new file mode 100644
index 0000000..2502c90
--- /dev/null
+++ b/Main/Common/Jace/CalculationEngine.cs
@@ -0,0 +1,483 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using Jace.Execution;
+using Jace.Operations;
+using Jace.Tokenizer;
+using Jace.Util;
+
+namespace Jace
+{
+    public delegate TResult DynamicFunc<T, TResult>(params T[] values);
+
+    /// <summary>
+    /// The CalculationEngine class is the main class of Jace.NET to convert strings containing
+    /// mathematical formulas into .NET Delegates and to calculate the result.
+    /// It can be configured to run in a number of modes based on the constructor parameters choosen.
+    /// </summary>
+    public class CalculationEngine
+    {
+        private readonly IExecutor executor;
+        private readonly Optimizer optimizer;
+        private readonly CultureInfo cultureInfo;
+        private readonly MemoryCache<string, Func<IDictionary<string, double>, double>> executionFormulaCache;
+        private readonly bool cacheEnabled;
+        private readonly bool optimizerEnabled;
+        private readonly bool caseSensitive;
+
+        private readonly Random random;
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="CalculationEngine"/> class with
+        /// default parameters.
+        /// </summary>
+        public CalculationEngine()
+            : this(new JaceOptions())
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="CalculationEngine"/> class. The dynamic compiler
+        /// is used for formula execution and the optimizer and cache are enabled.
+        /// </summary>
+        /// <param name="cultureInfo">
+        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers.
+        /// </param>
+        public CalculationEngine(CultureInfo cultureInfo)
+            : this(new JaceOptions() { CultureInfo = cultureInfo })
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="CalculationEngine"/> class. The optimizer and 
+        /// cache are enabled.
+        /// </summary>
+        /// <param name="cultureInfo">
+        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers.
+        /// </param>
+        /// <param name="executionMode">The execution mode that must be used for formula execution.</param>
+        public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode)
+            : this (new JaceOptions() { CultureInfo = cultureInfo, ExecutionMode = executionMode })
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="CalculationEngine"/> class.
+        /// </summary>
+        /// <param name="cultureInfo">
+        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers.
+        /// </param>
+        /// <param name="executionMode">The execution mode that must be used for formula execution.</param>
+        /// <param name="cacheEnabled">Enable or disable caching of mathematical formulas.</param>
+        /// <param name="optimizerEnabled">Enable or disable optimizing of formulas.</param>
+        /// <param name="adjustVariableCaseEnabled">Enable or disable auto lowercasing of variables.</param>
+        [Obsolete]
+        public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode, bool cacheEnabled, bool optimizerEnabled, bool adjustVariableCaseEnabled)
+            : this(new JaceOptions() { CultureInfo = cultureInfo, ExecutionMode = executionMode, CacheEnabled = cacheEnabled, OptimizerEnabled = optimizerEnabled, CaseSensitive = !adjustVariableCaseEnabled })
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="CalculationEngine"/> class.
+        /// </summary>
+        /// <param name="cultureInfo">
+        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers.
+        /// </param>
+        /// <param name="executionMode">The execution mode that must be used for formula execution.</param>
+        /// <param name="cacheEnabled">Enable or disable caching of mathematical formulas.</param>
+        /// <param name="optimizerEnabled">Enable or disable optimizing of formulas.</param>
+        /// <param name="adjustVariableCaseEnabled">Enable or disable converting to lower case.</param>
+        /// <param name="defaultFunctions">Enable or disable the default functions.</param>
+        /// <param name="defaultConstants">Enable or disable the default constants.</param>
+        /// <param name="cacheMaximumSize">Configure the maximum cache size for mathematical formulas.</param>
+        /// <param name="cacheReductionSize">Configure the cache reduction size for mathematical formulas.</param>
+        [Obsolete]
+        public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode, bool cacheEnabled,
+            bool optimizerEnabled, bool adjustVariableCaseEnabled, bool defaultFunctions, bool defaultConstants, int cacheMaximumSize, int cacheReductionSize)
+            : this(new JaceOptions() { CultureInfo = cultureInfo, ExecutionMode = executionMode, CacheEnabled = cacheEnabled, OptimizerEnabled = optimizerEnabled,
+                CaseSensitive = !adjustVariableCaseEnabled, DefaultFunctions = defaultFunctions, DefaultConstants = defaultConstants, 
+                CacheMaximumSize = cacheMaximumSize, CacheReductionSize = cacheReductionSize })
+        {
+        }
+
+        /// <summary>
+        /// Creates a new instance of the <see cref="CalculationEngine"/> class.
+        /// </summary>
+        /// <param name="options">The <see cref="JaceOptions"/> to configure the behaviour of the engine.</param>
+        public CalculationEngine(JaceOptions options)
+        {
+            this.executionFormulaCache = new MemoryCache<string, Func<IDictionary<string, double>, double>>(options.CacheMaximumSize, options.CacheReductionSize);
+            this.FunctionRegistry = new FunctionRegistry(false);
+            this.ConstantRegistry = new ConstantRegistry(false);
+            this.cultureInfo = options.CultureInfo;
+            this.cacheEnabled = options.CacheEnabled;
+            this.optimizerEnabled = options.OptimizerEnabled;
+            this.caseSensitive = options.CaseSensitive;
+
+            this.random = new Random();
+
+            if (options.ExecutionMode == ExecutionMode.Interpreted)
+                executor = new Interpreter(caseSensitive);
+            else if (options.ExecutionMode == ExecutionMode.Compiled)
+                executor = new DynamicCompiler(caseSensitive);
+            else
+                throw new ArgumentException(string.Format("Unsupported execution mode \"{0}\".", options.ExecutionMode),
+                    "executionMode");
+
+            optimizer = new Optimizer(new Interpreter()); // We run the optimizer with the interpreter 
+
+            // Register the default constants of Jace.NET into the constant registry
+            if (options.DefaultConstants)
+                RegisterDefaultConstants();
+
+            // Register the default functions of Jace.NET into the function registry
+            if (options.DefaultFunctions)
+                RegisterDefaultFunctions();
+        }
+
+        internal IFunctionRegistry FunctionRegistry { get; private set; }
+
+        internal IConstantRegistry ConstantRegistry { get; private set; }
+
+        public IEnumerable<FunctionInfo> Functions { get { return FunctionRegistry; } }
+
+        public IEnumerable<ConstantInfo> Constants { get { return ConstantRegistry; } }
+
+        public double Calculate(string formulaText)
+        {
+            return Calculate(formulaText, new Dictionary<string, double>());
+        }
+
+        public double Calculate(string formulaText, IDictionary<string, double> variables)
+        {
+            if (string.IsNullOrEmpty(formulaText))
+                throw new ArgumentNullException("formulaText");
+
+            if (variables == null)
+                throw new ArgumentNullException("variables");
+
+            if (!caseSensitive)
+            {
+                variables = EngineUtil.ConvertVariableNamesToLowerCase(variables);
+            }
+            VerifyVariableNames(variables);
+
+            // Add the reserved variables to the dictionary
+            foreach (ConstantInfo constant in ConstantRegistry)
+                variables.Add(constant.ConstantName, constant.Value);
+
+            if (IsInFormulaCache(formulaText, null, out var function))
+            {
+                return function(variables);
+            }
+            else
+            {
+                Operation operation = BuildAbstractSyntaxTree(formulaText, new ConstantRegistry(caseSensitive));
+                function = BuildFormula(formulaText, null, operation);
+                return function(variables);
+            }
+        }
+
+        public FormulaBuilder Formula(string formulaText)
+        {
+            if (string.IsNullOrEmpty(formulaText))
+                throw new ArgumentNullException("formulaText");
+
+            return new FormulaBuilder(formulaText, caseSensitive, this);
+        }
+
+        /// <summary>
+        /// Build a .NET func for the provided formula.
+        /// </summary>
+        /// <param name="formulaText">The formula that must be converted into a .NET func.</param>
+        /// <returns>A .NET func for the provided formula.</returns>
+        public Func<IDictionary<string, double>, double> Build(string formulaText)
+        {
+            if (string.IsNullOrEmpty(formulaText))
+                throw new ArgumentNullException("formulaText");
+
+            if (IsInFormulaCache(formulaText, null, out var result))
+            {
+                return result;
+            }
+            else
+            {
+                Operation operation = BuildAbstractSyntaxTree(formulaText, new ConstantRegistry(caseSensitive));
+                return BuildFormula(formulaText, null, operation);
+            }
+        }
+
+        /// <summary>
+        /// Build a .NET func for the provided formula.
+        /// </summary>
+        /// <param name="formulaText">The formula that must be converted into a .NET func.</param>
+        /// <param name="constants">Constant values for variables defined into the formula. They variables will be replaced by the constant value at pre-compilation time.</param>
+        /// <returns>A .NET func for the provided formula.</returns>
+        public Func<IDictionary<string, double>, double> Build(string formulaText, IDictionary<string, double> constants)
+        {
+            if (string.IsNullOrEmpty(formulaText))
+                throw new ArgumentNullException("formulaText");
+
+
+            ConstantRegistry compiledConstants = new ConstantRegistry(caseSensitive);
+            if (constants != null)
+            {
+                foreach (var constant in constants)
+                {
+                    compiledConstants.RegisterConstant(constant.Key, constant.Value);
+                }
+            }
+
+            if (IsInFormulaCache(formulaText, compiledConstants, out var result))
+            {
+                return result;
+            }
+            else
+            {
+                Operation operation = BuildAbstractSyntaxTree(formulaText, compiledConstants);
+                return BuildFormula(formulaText, compiledConstants,  operation);
+            }
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true); 
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        /// <summary>
+        /// Add a function to the calculation engine.
+        /// </summary>
+        /// <param name="functionName">The name of the function. This name can be used in mathematical formulas.</param>
+        /// <param name="function">The implemenation of the function.</param>
+        /// <param name="isIdempotent">Does the function provide the same result when it is executed multiple times.</param>
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double> function, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, function, isIdempotent, true);
+        }
+
+        public void AddFunction(string functionName, DynamicFunc<double, double> functionDelegate, bool isIdempotent = true)
+        {
+            FunctionRegistry.RegisterFunction(functionName, functionDelegate, isIdempotent, true);
+        }
+
+        /// <summary>
+        /// Add a constant to the calculation engine.
+        /// </summary>
+        /// <param name="constantName">The name of the constant. This name can be used in mathematical formulas.</param>
+        /// <param name="value">The value of the constant.</param>
+        public void AddConstant(string constantName, double value)
+        {
+            ConstantRegistry.RegisterConstant(constantName, value);
+        }
+
+        private void RegisterDefaultFunctions()
+        {
+            FunctionRegistry.RegisterFunction("sin", (Func<double, double>)Math.Sin, true, false);
+            FunctionRegistry.RegisterFunction("cos", (Func<double, double>)Math.Cos, true, false);
+            FunctionRegistry.RegisterFunction("csc", (Func<double, double>)MathUtil.Csc, true, false);
+            FunctionRegistry.RegisterFunction("sec", (Func<double, double>)MathUtil.Sec, true, false);
+            FunctionRegistry.RegisterFunction("asin", (Func<double, double>)Math.Asin, true, false);
+            FunctionRegistry.RegisterFunction("acos", (Func<double, double>)Math.Acos, true, false);
+            FunctionRegistry.RegisterFunction("tan", (Func<double, double>)Math.Tan, true, false);
+            FunctionRegistry.RegisterFunction("cot", (Func<double, double>)MathUtil.Cot, true, false);
+            FunctionRegistry.RegisterFunction("atan", (Func<double, double>)Math.Atan, true, false);
+            FunctionRegistry.RegisterFunction("acot", (Func<double, double>)MathUtil.Acot, true, false);
+            FunctionRegistry.RegisterFunction("loge", (Func<double, double>)Math.Log, true, false);
+            FunctionRegistry.RegisterFunction("log10", (Func<double, double>)Math.Log10, true, false);
+            FunctionRegistry.RegisterFunction("logn", (Func<double, double, double>)((a, b) => Math.Log(a, b)), true, false);
+            FunctionRegistry.RegisterFunction("sqrt", (Func<double, double>)Math.Sqrt, true, false);
+            FunctionRegistry.RegisterFunction("abs", (Func<double, double>)Math.Abs, true, false);
+            FunctionRegistry.RegisterFunction("if", (Func<double, double, double, double>)((a, b, c) => (a != 0.0 ? b : c)), true, false);
+            FunctionRegistry.RegisterFunction("ifless", (Func<double, double, double, double, double>)((a, b, c, d) => (a < b ? c : d)), true, false);
+            FunctionRegistry.RegisterFunction("ifmore", (Func<double, double, double, double, double>)((a, b, c, d) => (a > b ? c : d)), true, false);
+            FunctionRegistry.RegisterFunction("ifequal", (Func<double, double, double, double, double>)((a, b, c, d) => (a == b ? c : d)), true, false);
+            FunctionRegistry.RegisterFunction("ceiling", (Func<double, double>)Math.Ceiling, true, false);
+            FunctionRegistry.RegisterFunction("floor", (Func<double, double>)Math.Floor, true, false);
+            FunctionRegistry.RegisterFunction("truncate", (Func<double, double>)Math.Truncate, true, false);
+            FunctionRegistry.RegisterFunction("round", (Func<double, double>)Math.Round, true, false);
+
+            // Dynamic based arguments Functions
+            FunctionRegistry.RegisterFunction("max",  (DynamicFunc<double, double>)((a) => a.Max()), true, false);
+            FunctionRegistry.RegisterFunction("min", (DynamicFunc<double, double>)((a) => a.Min()), true, false);
+            FunctionRegistry.RegisterFunction("avg", (DynamicFunc<double, double>)((a) => a.Average()), true, false);
+            FunctionRegistry.RegisterFunction("median", (DynamicFunc<double, double>)((a) => MathExtended.Median(a)), true, false);
+
+            // Non Idempotent Functions
+            FunctionRegistry.RegisterFunction("random", (Func<double>)random.NextDouble, false, false);
+        }
+
+        private void RegisterDefaultConstants()
+        {
+            ConstantRegistry.RegisterConstant("e", Math.E, false);
+            ConstantRegistry.RegisterConstant("pi", Math.PI, false);
+        }
+
+        /// <summary>
+        /// Build the abstract syntax tree for a given formula. The formula string will
+        /// be first tokenized.
+        /// </summary>
+        /// <param name="formulaText">A string containing the mathematical formula that must be converted 
+        /// into an abstract syntax tree.</param>
+        /// <returns>The abstract syntax tree of the formula.</returns>
+        private Operation BuildAbstractSyntaxTree(string formulaText, ConstantRegistry compiledConstants)
+        {
+            TokenReader tokenReader = new TokenReader(cultureInfo);
+            List<Token> tokens = tokenReader.Read(formulaText);
+            
+            AstBuilder astBuilder = new AstBuilder(FunctionRegistry, caseSensitive, compiledConstants);
+            Operation operation = astBuilder.Build(tokens);
+
+            if (optimizerEnabled)
+                return optimizer.Optimize(operation, this.FunctionRegistry, this.ConstantRegistry);
+            else
+                return operation;
+        }
+
+        private Func<IDictionary<string, double>, double> BuildFormula(string formulaText, ConstantRegistry compiledConstants, Operation operation)
+        {
+            return executionFormulaCache.GetOrAdd(GenerateFormulaCacheKey(formulaText, compiledConstants), v => executor.BuildFormula(operation, this.FunctionRegistry, this.ConstantRegistry));
+        }
+
+        private bool IsInFormulaCache(string formulaText, ConstantRegistry compiledConstants, out Func<IDictionary<string, double>, double> function)
+        {
+            function = null;
+            return cacheEnabled && executionFormulaCache.TryGetValue(GenerateFormulaCacheKey(formulaText, compiledConstants), out function);
+        }
+
+        private string GenerateFormulaCacheKey(string formulaText, ConstantRegistry compiledConstants)
+        {
+            return (compiledConstants != null && compiledConstants.Any()) ? $"{formulaText}@{String.Join(",", compiledConstants?.Select(x => $"{x.ConstantName}:{x.Value}"))}" : formulaText;
+        }
+
+        /// <summary>
+        /// Verify a collection of variables to ensure that all the variable names are valid.
+        /// Users are not allowed to overwrite reserved variables or use function names as variables.
+        /// If an invalid variable is detected an exception is thrown.
+        /// </summary>
+        /// <param name="variables">The colletion of variables that must be verified.</param>
+        internal void VerifyVariableNames(IDictionary<string, double> variables)
+        {
+            foreach (string variableName in variables.Keys)
+            {
+                if(ConstantRegistry.IsConstantName(variableName) && !ConstantRegistry.GetConstantInfo(variableName).IsOverWritable)
+                    throw new ArgumentException(string.Format("The name \"{0}\" is a reservered variable name that cannot be overwritten.", variableName), "variables");
+
+                if (FunctionRegistry.IsFunctionName(variableName))
+                    throw new ArgumentException(string.Format("The name \"{0}\" is a function name. Parameters cannot have this name.", variableName), "variables");
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/CalculationEngine.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/CalculationEngine.cs.meta
index e37f87e..0304ee1 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/CalculationEngine.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 7605496412e5ed546a6ab8ccba648580
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/DataType.cs b/Main/Common/Jace/DataType.cs
new file mode 100644
index 0000000..33a7a56
--- /dev/null
+++ b/Main/Common/Jace/DataType.cs
@@ -0,0 +1,13 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace
+{
+    public enum DataType
+    {
+        Integer,
+        FloatingPoint
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/DataType.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/DataType.cs.meta
index e37f87e..0f3254c 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/DataType.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: f30cb87ce5accbe4ebcbc950c4eaae28
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Core/GameEngine/Common.meta b/Main/Common/Jace/Execution.meta
similarity index 76%
rename from Main/Core/GameEngine/Common.meta
rename to Main/Common/Jace/Execution.meta
index 29b34bc..e4ad765 100644
--- a/Main/Core/GameEngine/Common.meta
+++ b/Main/Common/Jace/Execution.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
+guid: 9d45a5ae0f6671f4783425dac0689ce4
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}
diff --git a/Main/Common/Jace/Execution/ConstantInfo.cs b/Main/Common/Jace/Execution/ConstantInfo.cs
new file mode 100644
index 0000000..f9fb700
--- /dev/null
+++ b/Main/Common/Jace/Execution/ConstantInfo.cs
@@ -0,0 +1,23 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Execution
+{
+    public class ConstantInfo
+    {
+        public ConstantInfo(string constantName, double value, bool isOverWritable)
+        {
+            this.ConstantName = constantName;
+            this.Value = value;
+            this.IsOverWritable = isOverWritable;
+        }
+
+        public string ConstantName { get; private set; }
+
+        public double Value { get; private set; }
+
+        public bool IsOverWritable { get; set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/ConstantInfo.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/ConstantInfo.cs.meta
index e37f87e..08c5741 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/ConstantInfo.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 80e7b1aba7b9c2245b2b872f018c07d6
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/ConstantRegistry.cs b/Main/Common/Jace/Execution/ConstantRegistry.cs
new file mode 100644
index 0000000..f934105
--- /dev/null
+++ b/Main/Common/Jace/Execution/ConstantRegistry.cs
@@ -0,0 +1,79 @@
+锘縰sing System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Jace.Util;
+
+namespace Jace.Execution
+{
+    public class ConstantRegistry : IConstantRegistry
+    {
+        private readonly bool caseSensitive;
+        private readonly Dictionary<string, ConstantInfo> constants;
+
+        public ConstantRegistry(bool caseSensitive)
+        {
+            this.caseSensitive = caseSensitive;
+            this.constants = new Dictionary<string, ConstantInfo>();
+        }
+
+        public IEnumerator<ConstantInfo> GetEnumerator()
+        {
+            return constants.Values.GetEnumerator();
+        }
+
+        IEnumerator System.Collections.IEnumerable.GetEnumerator()
+        {
+            return this.GetEnumerator();
+        }
+
+        public ConstantInfo GetConstantInfo(string constantName)
+        {
+            if (string.IsNullOrEmpty(constantName))
+                throw new ArgumentNullException("constantName");
+
+            ConstantInfo constantInfo = null;
+            return constants.TryGetValue(ConvertConstantName(constantName), out constantInfo) ? constantInfo : null;
+        }
+
+        public bool IsConstantName(string constantName)
+        {
+            if (string.IsNullOrEmpty(constantName))
+                throw new ArgumentNullException("constantName");
+
+            return constants.ContainsKey(ConvertConstantName(constantName));
+        }
+
+        public void RegisterConstant(string constantName, double value)
+        {
+            RegisterConstant(constantName, value, true);
+        }
+
+        public void RegisterConstant(string constantName, double value, bool isOverWritable)
+        {
+            if(string.IsNullOrEmpty(constantName))
+                throw new ArgumentNullException("constantName");
+
+            constantName = ConvertConstantName(constantName);
+
+            if (constants.ContainsKey(constantName) && !constants[constantName].IsOverWritable)
+            {
+                string message = string.Format("The constant \"{0}\" cannot be overwriten.", constantName);
+                throw new Exception(message);
+            }
+
+            ConstantInfo constantInfo = new ConstantInfo(constantName, value, isOverWritable);
+
+            if (constants.ContainsKey(constantName))
+                constants[constantName] = constantInfo;
+            else
+                constants.Add(constantName, constantInfo);
+        }
+
+        private string ConvertConstantName(string constantName)
+        {
+            return caseSensitive ? constantName : constantName.ToLowerFast();
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/ConstantRegistry.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/ConstantRegistry.cs.meta
index e37f87e..7ca5785 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/ConstantRegistry.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 4c5b0410c8fc58445a69f737f244707e
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/DynamicCompiler.cs b/Main/Common/Jace/Execution/DynamicCompiler.cs
new file mode 100644
index 0000000..a45480a
--- /dev/null
+++ b/Main/Common/Jace/Execution/DynamicCompiler.cs
@@ -0,0 +1,327 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using Jace.Operations;
+using Jace.Util;
+
+namespace Jace.Execution
+{
+    public class DynamicCompiler : IExecutor
+    {
+        private string FuncAssemblyQualifiedName;
+        private readonly bool caseSensitive;
+
+        public DynamicCompiler(): this(false) { }
+        public DynamicCompiler(bool caseSensitive)
+        {
+            this.caseSensitive = caseSensitive;
+            // The lower func reside in mscorelib, the higher ones in another assembly.
+            // This is  an easy cross platform way to to have this AssemblyQualifiedName.
+            FuncAssemblyQualifiedName =
+                typeof(Func<double, double, double, double, double, double, double, double, double, double>).GetTypeInfo().Assembly.FullName;
+        }
+
+        public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
+        {
+            return Execute(operation, functionRegistry, constantRegistry, new Dictionary<string, double>());
+        }
+
+        public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry, 
+            IDictionary<string, double> variables)
+        {
+            return BuildFormula(operation, functionRegistry, constantRegistry)(variables);
+        }
+
+        public Func<IDictionary<string, double>, double> BuildFormula(Operation operation,
+            IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
+        {
+            Func<FormulaContext, double> func = BuildFormulaInternal(operation, functionRegistry);
+            return caseSensitive
+                ? (Func<IDictionary<string, double>, double>)(variables =>
+                {
+                    return func(new FormulaContext(variables, functionRegistry, constantRegistry));
+                })
+                : (Func<IDictionary<string, double>, double>)(variables =>
+                {
+                    variables = EngineUtil.ConvertVariableNamesToLowerCase(variables);
+                    FormulaContext context = new FormulaContext(variables, functionRegistry, constantRegistry);
+                    return func(context);
+                });
+        }
+
+        private Func<FormulaContext, double> BuildFormulaInternal(Operation operation, 
+            IFunctionRegistry functionRegistry)
+        {
+            ParameterExpression contextParameter = Expression.Parameter(typeof(FormulaContext), "context");
+
+            LabelTarget returnLabel = Expression.Label(typeof(double));
+
+            Expression<Func<FormulaContext, double>> lambda = Expression.Lambda<Func<FormulaContext, double>>(
+                GenerateMethodBody(operation, contextParameter, functionRegistry),
+                contextParameter
+            );
+            return lambda.Compile();
+        }
+
+        
+
+        private Expression GenerateMethodBody(Operation operation, ParameterExpression contextParameter,
+            IFunctionRegistry functionRegistry)
+        {
+            if (operation == null)
+                throw new ArgumentNullException("operation");
+
+            if (operation.GetType() == typeof(IntegerConstant))
+            {
+                IntegerConstant constant = (IntegerConstant)operation;
+
+                double value = constant.Value;
+                return Expression.Constant(value, typeof(double));
+            }
+            else if (operation.GetType() == typeof(FloatingPointConstant))
+            {
+                FloatingPointConstant constant = (FloatingPointConstant)operation;
+
+                return Expression.Constant(constant.Value, typeof(double));
+            }
+            else if (operation.GetType() == typeof(Variable))
+            {
+                Variable variable = (Variable)operation;
+
+                Func<string, FormulaContext, double> getVariableValueOrThrow = PrecompiledMethods.GetVariableValueOrThrow;
+                return Expression.Call(null,
+                    getVariableValueOrThrow.GetMethodInfo(),
+                    Expression.Constant(variable.Name),
+                    contextParameter);
+            }
+            else if (operation.GetType() == typeof(Multiplication))
+            {
+                Multiplication multiplication = (Multiplication)operation;
+                Expression argument1 = GenerateMethodBody(multiplication.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(multiplication.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Multiply(argument1, argument2);
+            }
+            else if (operation.GetType() == typeof(Addition))
+            {
+                Addition addition = (Addition)operation;
+                Expression argument1 = GenerateMethodBody(addition.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(addition.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Add(argument1, argument2);
+            }
+            else if (operation.GetType() == typeof(Subtraction))
+            {
+                Subtraction addition = (Subtraction)operation;
+                Expression argument1 = GenerateMethodBody(addition.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(addition.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Subtract(argument1, argument2);
+            }
+            else if (operation.GetType() == typeof(Division))
+            {
+                Division division = (Division)operation;
+                Expression dividend = GenerateMethodBody(division.Dividend, contextParameter, functionRegistry);
+                Expression divisor = GenerateMethodBody(division.Divisor, contextParameter, functionRegistry);
+
+                return Expression.Divide(dividend, divisor);
+            }
+            else if (operation.GetType() == typeof(Modulo))
+            {
+                Modulo modulo = (Modulo)operation;
+                Expression dividend = GenerateMethodBody(modulo.Dividend, contextParameter, functionRegistry);
+                Expression divisor = GenerateMethodBody(modulo.Divisor, contextParameter, functionRegistry);
+
+                return Expression.Modulo(dividend, divisor);
+            }
+            else if (operation.GetType() == typeof(Exponentiation))
+            {
+                Exponentiation exponentation = (Exponentiation)operation;
+                Expression @base = GenerateMethodBody(exponentation.Base, contextParameter, functionRegistry);
+                Expression exponent = GenerateMethodBody(exponentation.Exponent, contextParameter, functionRegistry);
+
+                return Expression.Call(null, typeof(Math).GetRuntimeMethod("Pow", new Type[] { typeof(double), typeof(double) }), @base, exponent);
+            }
+            else if (operation.GetType() == typeof(UnaryMinus))
+            {
+                UnaryMinus unaryMinus = (UnaryMinus)operation;
+                Expression argument = GenerateMethodBody(unaryMinus.Argument, contextParameter, functionRegistry);
+                return Expression.Negate(argument);
+            }
+            else if (operation.GetType() == typeof(And))
+            {
+                And and = (And)operation;
+                Expression argument1 = Expression.NotEqual(GenerateMethodBody(and.Argument1, contextParameter, functionRegistry), Expression.Constant(0.0));
+                Expression argument2 = Expression.NotEqual(GenerateMethodBody(and.Argument2, contextParameter, functionRegistry), Expression.Constant(0.0));
+
+                return Expression.Condition(Expression.And(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(Or))
+            {
+                Or and = (Or)operation;
+                Expression argument1 = Expression.NotEqual(GenerateMethodBody(and.Argument1, contextParameter, functionRegistry), Expression.Constant(0.0));
+                Expression argument2 = Expression.NotEqual(GenerateMethodBody(and.Argument2, contextParameter, functionRegistry), Expression.Constant(0.0));
+
+                return Expression.Condition(Expression.Or(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(LessThan))
+            {
+                LessThan lessThan = (LessThan)operation;
+                Expression argument1 = GenerateMethodBody(lessThan.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(lessThan.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Condition(Expression.LessThan(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(LessOrEqualThan))
+            {
+                LessOrEqualThan lessOrEqualThan = (LessOrEqualThan)operation;
+                Expression argument1 = GenerateMethodBody(lessOrEqualThan.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(lessOrEqualThan.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Condition(Expression.LessThanOrEqual(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(GreaterThan))
+            {
+                GreaterThan greaterThan = (GreaterThan)operation;
+                Expression argument1 = GenerateMethodBody(greaterThan.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(greaterThan.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Condition(Expression.GreaterThan(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(GreaterOrEqualThan))
+            {
+                GreaterOrEqualThan greaterOrEqualThan = (GreaterOrEqualThan)operation;
+                Expression argument1 = GenerateMethodBody(greaterOrEqualThan.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(greaterOrEqualThan.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Condition(Expression.GreaterThanOrEqual(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(Equal))
+            {
+                Equal equal = (Equal)operation;
+                Expression argument1 = GenerateMethodBody(equal.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(equal.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Condition(Expression.Equal(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(NotEqual))
+            {
+                NotEqual notEqual = (NotEqual)operation;
+                Expression argument1 = GenerateMethodBody(notEqual.Argument1, contextParameter, functionRegistry);
+                Expression argument2 = GenerateMethodBody(notEqual.Argument2, contextParameter, functionRegistry);
+
+                return Expression.Condition(Expression.NotEqual(argument1, argument2),
+                    Expression.Constant(1.0),
+                    Expression.Constant(0.0));
+            }
+            else if (operation.GetType() == typeof(Function))
+            {
+                Function function = (Function)operation;
+
+                FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(function.FunctionName);
+                Type funcType;
+                Type[] parameterTypes;
+                Expression[] arguments;
+
+                if (functionInfo.IsDynamicFunc)
+                {
+                    funcType = typeof(DynamicFunc<double, double>);
+                    parameterTypes = new Type[] { typeof(double[]) };
+
+
+                    Expression[] arrayArguments = new Expression[function.Arguments.Count];
+                    for (int i = 0; i < function.Arguments.Count; i++)
+                        arrayArguments[i] = GenerateMethodBody(function.Arguments[i], contextParameter, functionRegistry);
+
+                    arguments = new Expression[1];
+                    arguments[0] = NewArrayExpression.NewArrayInit(typeof(double), arrayArguments);
+                }
+                else
+                {
+                    funcType = GetFuncType(functionInfo.NumberOfParameters);
+                    parameterTypes = (from i in Enumerable.Range(0, functionInfo.NumberOfParameters)
+                                             select typeof(double)).ToArray();
+
+                    arguments = new Expression[functionInfo.NumberOfParameters];
+                    for (int i = 0; i < functionInfo.NumberOfParameters; i++)
+                        arguments[i] = GenerateMethodBody(function.Arguments[i], contextParameter, functionRegistry);
+                }
+
+                Expression getFunctionRegistry = Expression.Property(contextParameter, "FunctionRegistry");
+
+                ParameterExpression functionInfoVariable = Expression.Variable(typeof(FunctionInfo));
+
+                Expression funcInstance;
+                if (!functionInfo.IsOverWritable)
+                {
+                    funcInstance = Expression.Convert(
+                        Expression.Property(
+                            Expression.Call(
+                                getFunctionRegistry,
+                                typeof(IFunctionRegistry).GetRuntimeMethod("GetFunctionInfo", new Type[] { typeof(string) }),
+                                Expression.Constant(function.FunctionName)),
+                            "Function"),
+                        funcType);
+                }
+                else
+                    funcInstance = Expression.Constant(functionInfo.Function, funcType);
+
+                return Expression.Call(
+                    funcInstance,
+                    funcType.GetRuntimeMethod("Invoke", parameterTypes),
+                    arguments);
+            }
+            else
+            {
+                throw new ArgumentException(string.Format("Unsupported operation \"{0}\".", operation.GetType().FullName), "operation");
+            }
+        }
+
+        private Type GetFuncType(int numberOfParameters)
+        {
+            string funcTypeName;
+            if (numberOfParameters < 9)
+                funcTypeName = string.Format("System.Func`{0}", numberOfParameters + 1);
+            else
+                funcTypeName = string.Format("System.Func`{0}, {1}", numberOfParameters + 1, FuncAssemblyQualifiedName);
+            Type funcType = Type.GetType(funcTypeName);
+
+            Type[] typeArguments = new Type[numberOfParameters + 1];
+            for (int i = 0; i < typeArguments.Length; i++)
+                typeArguments[i] = typeof(double);
+
+            return funcType.MakeGenericType(typeArguments);
+        }
+
+        private static class PrecompiledMethods
+        {
+            public static double GetVariableValueOrThrow(string variableName, FormulaContext context)
+            {
+                if (context.Variables.TryGetValue(variableName, out double result))
+                    return result;
+                else if (context.ConstantRegistry.IsConstantName(variableName))
+                    return context.ConstantRegistry.GetConstantInfo(variableName).Value;
+                else
+                    throw new VariableNotDefinedException($"The variable \"{variableName}\" used is not defined.");
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/DynamicCompiler.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/DynamicCompiler.cs.meta
index e37f87e..3cb36e7 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/DynamicCompiler.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: c058c0ded31899643b05723eafcafc40
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/ExecutionMode.cs b/Main/Common/Jace/Execution/ExecutionMode.cs
new file mode 100644
index 0000000..d498740
--- /dev/null
+++ b/Main/Common/Jace/Execution/ExecutionMode.cs
@@ -0,0 +1,13 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Execution
+{
+    public enum ExecutionMode
+    {
+        Interpreted,
+        Compiled
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/ExecutionMode.cs.meta
similarity index 83%
rename from Main/System/Hero/HeroInfo.Quality.cs.meta
rename to Main/Common/Jace/Execution/ExecutionMode.cs.meta
index e37f87e..52335f6 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/ExecutionMode.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 0d9522fc6bc25a644b4074177b045fc6
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/FormulaBuilder.cs b/Main/Common/Jace/Execution/FormulaBuilder.cs
new file mode 100644
index 0000000..63945d9
--- /dev/null
+++ b/Main/Common/Jace/Execution/FormulaBuilder.cs
@@ -0,0 +1,131 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using Jace.Operations;
+using Jace.Util;
+
+namespace Jace.Execution
+{
+    public class FormulaBuilder
+    {
+        private readonly CalculationEngine engine;
+
+        private string formulaText;
+        private bool caseSensitive;
+        private DataType? resultDataType;
+        private List<ParameterInfo> parameters;
+        private IDictionary<string, double> constants;
+
+        /// <summary>
+        /// Creates a new instance of the FormulaBuilder class.
+        /// </summary>
+        /// <param name="formulaText">
+        /// A calculation engine instance that can be used for interpreting and executing 
+        /// the formula.
+        /// </param>
+        internal FormulaBuilder(string formulaText, bool caseSensitive, CalculationEngine engine)
+        {
+            this.parameters = new List<ParameterInfo>();
+            this.constants = new Dictionary<string, double>();
+            this.formulaText = formulaText;
+            this.engine = engine;
+            this.caseSensitive = caseSensitive;
+        }
+
+        /// <summary>
+        /// Add a new parameter to the formula being constructed. Parameters are
+        /// added in the order of which they are defined.
+        /// </summary>
+        /// <param name="name">The name of the parameter.</param>
+        /// <param name="dataType">The date type of the parameter.</param>
+        /// <returns>The <see cref="FormulaBuilder"/> instance.</returns>
+        public FormulaBuilder Parameter(string name, DataType dataType)
+        {
+            if (string.IsNullOrEmpty(name))
+                throw new ArgumentNullException("name");
+
+            if (engine.FunctionRegistry.IsFunctionName(name))
+                throw new ArgumentException(string.Format("The name \"{0}\" is a function name. Parameters cannot have this name.", name), "name");
+
+            if (parameters.Any(p => p.Name == name))
+                throw new ArgumentException(string.Format("A parameter with the name \"{0}\" was already defined.", name), "name");
+
+            parameters.Add(new ParameterInfo() {Name = name, DataType = dataType});
+            return this;
+        }
+
+        /// <summary>
+        /// Add a new constant to the formula being constructed.
+        /// </summary>
+        /// <param name="name">The name of the constant.</param>
+        /// <param name="constantValue">The value of the constant. Variables for which a constant value is defined will be replaced at pre-compilation time.</param>
+        /// <returns>The <see cref="FormulaBuilder"/> instance.</returns>
+        public FormulaBuilder Constant(string name, int constantValue)
+        {
+            return Constant(name, (double)constantValue);
+        }
+
+        /// <summary>
+        /// Add a new constant to the formula being constructed. The
+        /// </summary>
+        /// <param name="name">The name of the constant.</param>
+        /// <param name="constantValue">The value of the constant.</param>
+        /// <returns>The <see cref="FormulaBuilder"/> instance.</returns>
+        public FormulaBuilder Constant(string name, double constantValue)
+        {
+            if (string.IsNullOrEmpty(name))
+                throw new ArgumentNullException("name");
+
+            if (constants.Any(p => p.Key == name))
+                throw new ArgumentException(string.Format("A constant with the name \"{0}\" was already defined.", name), "name");
+
+            constants[name] = constantValue;
+            return this;
+        }
+
+        /// <summary>
+        /// Define the result data type for the formula.
+        /// </summary>
+        /// <param name="dataType">The result data type for the formula.</param>
+        /// <returns>The <see cref="FormulaBuilder"/> instance.</returns>
+        public FormulaBuilder Result(DataType dataType)
+        {
+            if (resultDataType.HasValue)
+                throw new InvalidOperationException("The result can only be defined once for a given formula.");
+
+            resultDataType = dataType;
+            return this;
+        }
+
+        /// <summary>
+        /// Build the formula defined. This will create a func delegate matching with the parameters
+        /// and the return type specified.
+        /// </summary>
+        /// <returns>The func delegate for the defined formula.</returns>
+        public Delegate Build()
+        {
+            if (!resultDataType.HasValue)
+                throw new Exception("Please define a result data type for the formula.");
+
+            Func<IDictionary<string, double>, double> formula = engine.Build(formulaText, constants);
+
+            FuncAdapter adapter = new FuncAdapter();
+            return adapter.Wrap(parameters, variables => {
+
+                if(!caseSensitive)
+                    variables = EngineUtil.ConvertVariableNamesToLowerCase(variables);
+
+                engine.VerifyVariableNames(variables);
+
+                // Add the reserved variables to the dictionary
+                foreach (ConstantInfo constant in engine.ConstantRegistry)
+                    variables.Add(constant.ConstantName, constant.Value);
+
+                return formula(variables);
+            });
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/FormulaBuilder.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/FormulaBuilder.cs.meta
index e37f87e..0a021cd 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/FormulaBuilder.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: b422f2fc8278c544db048c826ec43f40
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/FunctionInfo.cs b/Main/Common/Jace/Execution/FunctionInfo.cs
new file mode 100644
index 0000000..6451ef6
--- /dev/null
+++ b/Main/Common/Jace/Execution/FunctionInfo.cs
@@ -0,0 +1,32 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Execution
+{
+    public class FunctionInfo
+    {
+        public FunctionInfo(string functionName, int numberOfParameters, bool isIdempotent, bool isOverWritable, bool isDynamicFunc, Delegate function)
+        {
+            this.FunctionName = functionName;
+            this.NumberOfParameters = numberOfParameters;
+            this.IsIdempotent = isIdempotent;
+            this.IsOverWritable = isOverWritable;
+            this.IsDynamicFunc = isDynamicFunc;
+            this.Function = function;
+        }
+
+        public string FunctionName { get; private set; }
+        
+        public int NumberOfParameters { get; private set; }
+
+        public bool IsOverWritable { get; set; }
+
+        public bool IsIdempotent { get; set; }
+
+        public bool IsDynamicFunc { get; private set; }
+
+        public Delegate Function { get; private set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/FunctionInfo.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/FunctionInfo.cs.meta
index e37f87e..3126408 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/FunctionInfo.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 68afcf59c23204e4f83b55a9555fa566
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/FunctionRegistry.cs b/Main/Common/Jace/Execution/FunctionRegistry.cs
new file mode 100644
index 0000000..721bf3b
--- /dev/null
+++ b/Main/Common/Jace/Execution/FunctionRegistry.cs
@@ -0,0 +1,119 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Reflection;
+using System.Collections;
+using Jace.Util;
+
+namespace Jace.Execution
+{
+    public class FunctionRegistry : IFunctionRegistry
+    {
+        private const string DynamicFuncName = "Jace.DynamicFunc";
+
+        private readonly bool caseSensitive;
+        private readonly Dictionary<string, FunctionInfo> functions;
+
+        public FunctionRegistry(bool caseSensitive)
+        {
+            this.caseSensitive = caseSensitive;
+            this.functions = new Dictionary<string, FunctionInfo>();
+        }
+
+        public IEnumerator<FunctionInfo> GetEnumerator()
+        {
+            return functions.Values.GetEnumerator();
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return this.GetEnumerator();
+        }
+
+        public FunctionInfo GetFunctionInfo(string functionName)
+        {
+            if (string.IsNullOrEmpty(functionName))
+                throw new ArgumentNullException("functionName");
+
+            FunctionInfo functionInfo = null;
+            return functions.TryGetValue(ConvertFunctionName(functionName), out functionInfo) ? functionInfo : null;
+        }
+
+        public void RegisterFunction(string functionName, Delegate function)
+        {
+            RegisterFunction(functionName, function, true, true);
+        }
+        
+        public void RegisterFunction(string functionName, Delegate function, bool isIdempotent, bool isOverWritable)
+        {
+            if (string.IsNullOrEmpty(functionName))
+                throw new ArgumentNullException("functionName");
+
+            if (function == null)
+                throw new ArgumentNullException("function");
+
+            Type funcType = function.GetType();
+            bool isDynamicFunc = false;
+            int numberOfParameters = -1;
+            
+            if (funcType.FullName.StartsWith("System.Func"))
+            {
+                foreach (Type genericArgument in funcType.GenericTypeArguments)
+                    if (genericArgument != typeof(double))
+                        throw new ArgumentException("Only doubles are supported as function arguments.", "function");
+
+                numberOfParameters = function
+                    .GetMethodInfo()
+                    .GetParameters()
+                    .Count(p => p.ParameterType == typeof(double));
+            }
+            else if (funcType.FullName.StartsWith(DynamicFuncName))
+            {
+                isDynamicFunc = true;
+            }
+            else
+                throw new ArgumentException("Only System.Func and " + DynamicFuncName + " delegates are permitted.", "function");
+
+            functionName = ConvertFunctionName(functionName);
+
+            if (functions.ContainsKey(functionName) && !functions[functionName].IsOverWritable)
+            {
+                string message = string.Format("The function \"{0}\" cannot be overwriten.", functionName);
+                throw new Exception(message);
+            }
+
+            if (functions.ContainsKey(functionName) && functions[functionName].NumberOfParameters != numberOfParameters)
+            {
+                string message = string.Format("The number of parameters cannot be changed when overwriting a method.");
+                throw new Exception(message);
+            }
+
+            if (functions.ContainsKey(functionName) && functions[functionName].IsDynamicFunc != isDynamicFunc)
+            {
+                string message = string.Format("A Func can only be overwritten by another Func and a DynamicFunc can only be overwritten by another DynamicFunc.");
+                throw new Exception(message);
+            }
+
+            FunctionInfo functionInfo = new FunctionInfo(functionName, numberOfParameters, isIdempotent, isOverWritable, isDynamicFunc, function);
+
+            if (functions.ContainsKey(functionName))
+                functions[functionName] = functionInfo;
+            else
+                functions.Add(functionName, functionInfo);
+        }
+
+            public bool IsFunctionName(string functionName)
+        {
+            if (string.IsNullOrEmpty(functionName))
+                throw new ArgumentNullException("functionName");
+
+            return functions.ContainsKey(ConvertFunctionName(functionName));
+        }
+
+        private string ConvertFunctionName(string functionName)
+        {
+            return caseSensitive ? functionName : functionName.ToLowerFast();
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/FunctionRegistry.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/FunctionRegistry.cs.meta
index e37f87e..94a6a38 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/FunctionRegistry.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: a23e9d8bb99397f4b932d1802e5e1a30
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/IConstantRegistry.cs b/Main/Common/Jace/Execution/IConstantRegistry.cs
new file mode 100644
index 0000000..88823c1
--- /dev/null
+++ b/Main/Common/Jace/Execution/IConstantRegistry.cs
@@ -0,0 +1,15 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Execution
+{
+    public interface IConstantRegistry : IEnumerable<ConstantInfo>
+    {
+        ConstantInfo GetConstantInfo(string constantName);
+        bool IsConstantName(string constantName);
+        void RegisterConstant(string constantName, double value);
+        void RegisterConstant(string constantName, double value, bool isOverWritable);
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/IConstantRegistry.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/IConstantRegistry.cs.meta
index e37f87e..b72ea7d 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/IConstantRegistry.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: ac1d9141e70689a4ea802b6b9d653707
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/IExecutor.cs b/Main/Common/Jace/Execution/IExecutor.cs
new file mode 100644
index 0000000..2d29143
--- /dev/null
+++ b/Main/Common/Jace/Execution/IExecutor.cs
@@ -0,0 +1,16 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Jace.Operations;
+
+namespace Jace.Execution
+{
+    public interface IExecutor
+    {
+        double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry);
+        double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry, IDictionary<string, double> variables);
+
+        Func<IDictionary<string, double>, double> BuildFormula(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry);
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/IExecutor.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/IExecutor.cs.meta
index e37f87e..e2246b5 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/IExecutor.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 5cc438b6f6efc8143b63e3107ce1eb9c
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/IFunctionRegistry.cs b/Main/Common/Jace/Execution/IFunctionRegistry.cs
new file mode 100644
index 0000000..35cc2dd
--- /dev/null
+++ b/Main/Common/Jace/Execution/IFunctionRegistry.cs
@@ -0,0 +1,13 @@
+锘縰sing System;
+using System.Collections.Generic;
+
+namespace Jace.Execution
+{
+    public interface IFunctionRegistry : IEnumerable<FunctionInfo>
+    {
+        FunctionInfo GetFunctionInfo(string functionName);
+        bool IsFunctionName(string functionName);
+        void RegisterFunction(string functionName, Delegate function);
+        void RegisterFunction(string functionName, Delegate function, bool isIdempotent, bool isOverWritable);
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/IFunctionRegistry.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/IFunctionRegistry.cs.meta
index e37f87e..d2c110a 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/IFunctionRegistry.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 5c0e119822c96154a90ac4a9e37de0d9
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/Interpreter.cs b/Main/Common/Jace/Execution/Interpreter.cs
new file mode 100644
index 0000000..1b89e73
--- /dev/null
+++ b/Main/Common/Jace/Execution/Interpreter.cs
@@ -0,0 +1,252 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Jace.Operations;
+using Jace.Util;
+
+namespace Jace.Execution
+{
+    public class Interpreter : IExecutor
+    {
+        private readonly bool caseSensitive;
+
+        public Interpreter(): this(false) { }
+
+        public Interpreter(bool caseSensitive)
+        {
+            this.caseSensitive = caseSensitive;
+        }
+        public Func<IDictionary<string, double>, double> BuildFormula(Operation operation, 
+            IFunctionRegistry functionRegistry,
+            IConstantRegistry constantRegistry)
+        {
+            return caseSensitive
+              ? (Func<IDictionary<string, double>, double>)(variables =>
+              {
+                  return Execute(operation, functionRegistry, constantRegistry, variables);
+              })
+              : (Func<IDictionary<string, double>, double>)(variables =>
+              {
+                  variables = EngineUtil.ConvertVariableNamesToLowerCase(variables);
+                  return Execute(operation, functionRegistry, constantRegistry, variables);
+              });
+        }
+
+        public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
+        {
+            return Execute(operation, functionRegistry, constantRegistry, new Dictionary<string, double>());
+        }
+
+        public double Execute(Operation operation,
+            IFunctionRegistry functionRegistry,
+            IConstantRegistry constantRegistry, 
+            IDictionary<string, double> variables)
+        {
+            if (operation == null)
+                throw new ArgumentNullException("operation");
+
+            if (operation.GetType() == typeof(IntegerConstant))
+            {
+                IntegerConstant constant = (IntegerConstant)operation;
+                return constant.Value;
+            }
+            else if (operation.GetType() == typeof(FloatingPointConstant))
+            {
+                FloatingPointConstant constant = (FloatingPointConstant)operation;
+                return constant.Value;
+            }
+            else if (operation.GetType() == typeof(Variable))
+            {
+                Variable variable = (Variable)operation;
+
+                double value;
+                bool variableFound = variables.TryGetValue(variable.Name, out value);
+
+                if (variableFound)
+                    return value;
+                else
+                    throw new VariableNotDefinedException(string.Format("The variable \"{0}\" used is not defined.", variable.Name));
+            }
+            else if (operation.GetType() == typeof(Multiplication))
+            {
+                Multiplication multiplication = (Multiplication)operation;
+                return Execute(multiplication.Argument1, functionRegistry, constantRegistry,  variables) * Execute(multiplication.Argument2, functionRegistry, constantRegistry,  variables);
+            }
+            else if (operation.GetType() == typeof(Addition))
+            {
+                Addition addition = (Addition)operation;
+                return Execute(addition.Argument1, functionRegistry, constantRegistry,  variables) + Execute(addition.Argument2, functionRegistry, constantRegistry,  variables);
+            }
+            else if (operation.GetType() == typeof(Subtraction))
+            {
+                Subtraction addition = (Subtraction)operation;
+                return Execute(addition.Argument1, functionRegistry, constantRegistry,  variables) - Execute(addition.Argument2, functionRegistry, constantRegistry,  variables);
+            }
+            else if (operation.GetType() == typeof(Division))
+            {
+                Division division = (Division)operation;
+                return Execute(division.Dividend, functionRegistry, constantRegistry,  variables) / Execute(division.Divisor, functionRegistry, constantRegistry,  variables);
+            }
+            else if (operation.GetType() == typeof(Modulo))
+            {
+                Modulo division = (Modulo)operation;
+                return Execute(division.Dividend, functionRegistry, constantRegistry,  variables) % Execute(division.Divisor, functionRegistry, constantRegistry,  variables);
+            }
+            else if (operation.GetType() == typeof(Exponentiation))
+            {
+                Exponentiation exponentiation = (Exponentiation)operation;
+                return Math.Pow(Execute(exponentiation.Base, functionRegistry, constantRegistry,  variables), Execute(exponentiation.Exponent, functionRegistry, constantRegistry,  variables));
+            }
+            else if (operation.GetType() == typeof(UnaryMinus))
+            {
+                UnaryMinus unaryMinus = (UnaryMinus)operation;
+                return -Execute(unaryMinus.Argument, functionRegistry, constantRegistry,  variables);
+            }
+            else if (operation.GetType() == typeof(And))
+            {
+                And and = (And)operation;
+                var operation1 = Execute(and.Argument1, functionRegistry, constantRegistry,  variables) != 0;
+                var operation2 = Execute(and.Argument2, functionRegistry, constantRegistry,  variables) != 0;
+
+                return (operation1 && operation2) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(Or))
+            {
+                Or or = (Or)operation;
+                var operation1 = Execute(or.Argument1, functionRegistry, constantRegistry,  variables) != 0;
+                var operation2 = Execute(or.Argument2, functionRegistry, constantRegistry,  variables) != 0;
+
+                return (operation1 || operation2) ? 1.0 : 0.0;
+            }
+            else if(operation.GetType() == typeof(LessThan))
+            {
+                LessThan lessThan = (LessThan)operation;
+                return (Execute(lessThan.Argument1, functionRegistry, constantRegistry,  variables) < Execute(lessThan.Argument2, functionRegistry, constantRegistry,  variables)) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(LessOrEqualThan))
+            {
+                LessOrEqualThan lessOrEqualThan = (LessOrEqualThan)operation;
+                return (Execute(lessOrEqualThan.Argument1, functionRegistry, constantRegistry,  variables) <= Execute(lessOrEqualThan.Argument2, functionRegistry, constantRegistry,  variables)) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(GreaterThan))
+            {
+                GreaterThan greaterThan = (GreaterThan)operation;
+                return (Execute(greaterThan.Argument1, functionRegistry, constantRegistry,  variables) > Execute(greaterThan.Argument2, functionRegistry, constantRegistry,  variables)) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(GreaterOrEqualThan))
+            {
+                GreaterOrEqualThan greaterOrEqualThan = (GreaterOrEqualThan)operation;
+                return (Execute(greaterOrEqualThan.Argument1, functionRegistry, constantRegistry,  variables) >= Execute(greaterOrEqualThan.Argument2, functionRegistry, constantRegistry,  variables)) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(Equal))
+            {
+                Equal equal = (Equal)operation;
+                return (Execute(equal.Argument1, functionRegistry, constantRegistry,  variables) == Execute(equal.Argument2, functionRegistry, constantRegistry,  variables)) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(NotEqual))
+            {
+                NotEqual notEqual = (NotEqual)operation;
+                return (Execute(notEqual.Argument1, functionRegistry, constantRegistry,  variables) != Execute(notEqual.Argument2, functionRegistry, constantRegistry,  variables)) ? 1.0 : 0.0;
+            }
+            else if (operation.GetType() == typeof(Function))
+            {
+                Function function = (Function)operation;
+
+                FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(function.FunctionName);
+
+                double[] arguments = new double[functionInfo.IsDynamicFunc ? function.Arguments.Count : functionInfo.NumberOfParameters];
+                for (int i = 0; i < arguments.Length; i++)
+                    arguments[i] = Execute(function.Arguments[i], functionRegistry, constantRegistry,  variables);
+
+                return Invoke(functionInfo.Function, arguments);
+            }
+            else
+            {
+                throw new ArgumentException(string.Format("Unsupported operation \"{0}\".", operation.GetType().FullName), "operation");
+            }
+        }
+
+        private double Invoke(Delegate function, double[] arguments)
+        {
+            // DynamicInvoke is slow, so we first try to convert it to a Func
+            if (function is Func<double>)
+            {
+                return ((Func<double>)function).Invoke();
+            }
+            else if (function is Func<double, double>)
+            {
+                return ((Func<double, double>)function).Invoke(arguments[0]);
+            }
+            else if (function is Func<double, double, double>)
+            {
+                return ((Func<double, double, double>)function).Invoke(arguments[0], arguments[1]);
+            }
+            else if (function is Func<double, double, double, double>)
+            {
+                return ((Func<double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2]);
+            }
+            else if (function is Func<double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3]);
+            }
+            else if (function is Func<double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10], arguments[11]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10], arguments[11], arguments[12]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10], arguments[11], arguments[12], arguments[13]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10], arguments[11], arguments[12], arguments[13], arguments[14]);
+            }
+            else if (function is Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double>)
+            {
+                return ((Func<double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double>)function).Invoke(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8], arguments[9], arguments[10], arguments[11], arguments[12], arguments[13], arguments[14], arguments[15]);
+            }
+            else if (function is DynamicFunc<double, double>)
+            {
+                return ((DynamicFunc<double, double>)function).Invoke(arguments);
+            }
+            else
+            {
+                return (double)function.DynamicInvoke((from s in arguments select (object)s).ToArray());
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/Interpreter.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/Interpreter.cs.meta
index e37f87e..3a04dab 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/Interpreter.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 880be476462899f4aa9a1e05d9b1e50f
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Execution/ParameterInfo.cs b/Main/Common/Jace/Execution/ParameterInfo.cs
new file mode 100644
index 0000000..1d4e811
--- /dev/null
+++ b/Main/Common/Jace/Execution/ParameterInfo.cs
@@ -0,0 +1,14 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Jace.Operations;
+
+namespace Jace.Execution
+{
+    public struct ParameterInfo
+    {
+        public string Name;
+        public DataType DataType;
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Execution/ParameterInfo.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Execution/ParameterInfo.cs.meta
index e37f87e..7cc3ee4 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Execution/ParameterInfo.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: f118a37bdba42b8459abfd0f2cc2179b
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/FormulaContext.cs b/Main/Common/Jace/FormulaContext.cs
new file mode 100644
index 0000000..677366e
--- /dev/null
+++ b/Main/Common/Jace/FormulaContext.cs
@@ -0,0 +1,25 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Jace.Execution;
+
+namespace Jace
+{
+    public class FormulaContext
+    {
+        public FormulaContext(IDictionary<string, double> variables,
+            IFunctionRegistry functionRegistry,
+            IConstantRegistry constantRegistry)
+        {
+            this.Variables = variables;
+            this.FunctionRegistry = functionRegistry;
+            this.ConstantRegistry = constantRegistry;
+        }
+
+        public IDictionary<string, double> Variables { get; private set; }
+
+        public IFunctionRegistry FunctionRegistry { get; private set; }
+        public IConstantRegistry ConstantRegistry { get; private set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/FormulaContext.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/FormulaContext.cs.meta
index e37f87e..6845e47 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/FormulaContext.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 766328a00fcb8a545bd00380387bbc50
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/JaceOptions.cs b/Main/Common/Jace/JaceOptions.cs
new file mode 100644
index 0000000..a360e70
--- /dev/null
+++ b/Main/Common/Jace/JaceOptions.cs
@@ -0,0 +1,87 @@
+锘縰sing Jace.Execution;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace Jace
+{
+    public class JaceOptions
+    {
+        internal const int DefaultCacheMaximumSize = 500;
+        internal const int DefaultCacheReductionSize = 50;
+
+        public JaceOptions()
+        {
+            CultureInfo = CultureInfo.CurrentCulture;
+            ExecutionMode = ExecutionMode.Compiled;
+            CacheEnabled = true;
+            OptimizerEnabled = true;
+            CaseSensitive = false;
+            DefaultFunctions = true;
+            DefaultConstants = true;
+            CacheMaximumSize = DefaultCacheMaximumSize;
+            CacheReductionSize = DefaultCacheReductionSize;
+        }
+
+        /// <summary>
+        /// The <see cref="CultureInfo"/> required for correctly reading floating poin numbers.
+        /// </summary>
+        public CultureInfo CultureInfo { get; set; }
+
+        /// <summary>
+        /// The execution mode that must be used for formula execution.
+        /// </summary>
+        public ExecutionMode ExecutionMode { get; set; }
+
+        /// <summary>
+        /// Enable or disable caching of mathematical formulas.
+        /// </summary>
+        public bool CacheEnabled { get; set; }
+
+        /// <summary>
+        /// Configure the maximum cache size for mathematical formulas.
+        /// </summary>
+        public int CacheMaximumSize { get; set; }
+
+        /// <summary>
+        /// Configure the cache reduction size for mathematical formulas.
+        /// </summary>
+        public int CacheReductionSize { get; set; }
+
+        /// <summary>
+        /// Enable or disable optimizing of formulas.
+        /// </summary>
+        public bool OptimizerEnabled { get; set; }
+
+        /// <summary>
+        /// Enable or disable converting to lower case. This parameter is the inverse of <see cref="CaseSensitive"/>.
+        /// </summary>
+        [Obsolete]
+        public bool AdjustVariableCase { 
+            get
+            {
+                return !CaseSensitive;
+            }
+            set
+            {
+                CaseSensitive = !value;
+            }
+        }
+
+        /// <summary>
+        /// Enable case sensitive or case insensitive processing mode.
+        /// </summary>
+        public bool CaseSensitive { get;  set; }
+
+        /// <summary>
+        /// Enable or disable the default functions.
+        /// </summary>
+        public bool DefaultFunctions { get; set; }
+
+        /// <summary>
+        /// Enable or disable the default constants.
+        /// </summary>
+        public bool DefaultConstants { get; set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/JaceOptions.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/JaceOptions.cs.meta
index e37f87e..7899e31 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/JaceOptions.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: da15a14f53646d442988e85bc2545dc8
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Core/GameEngine/Common.meta b/Main/Common/Jace/Operations.meta
similarity index 76%
copy from Main/Core/GameEngine/Common.meta
copy to Main/Common/Jace/Operations.meta
index 29b34bc..420fc54 100644
--- a/Main/Core/GameEngine/Common.meta
+++ b/Main/Common/Jace/Operations.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
+guid: d825ff71751c9124885a7751c284959d
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}
diff --git a/Main/Common/Jace/Operations/Addition.cs b/Main/Common/Jace/Operations/Addition.cs
new file mode 100644
index 0000000..36036d1
--- /dev/null
+++ b/Main/Common/Jace/Operations/Addition.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Addition : Operation
+    {
+        public Addition(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Addition.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Addition.cs.meta
index e37f87e..5529d30 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Addition.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 340301d5fb1dce54ab7dec7873ceea4d
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/And.cs b/Main/Common/Jace/Operations/And.cs
new file mode 100644
index 0000000..83e6d88
--- /dev/null
+++ b/Main/Common/Jace/Operations/And.cs
@@ -0,0 +1,19 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class And : Operation
+    {
+        public And(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/And.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/And.cs.meta
index e37f87e..eb33300 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/And.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: c88e04049f2269a498cfedbd3fc9576c
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Constant.cs b/Main/Common/Jace/Operations/Constant.cs
new file mode 100644
index 0000000..02d7ae2
--- /dev/null
+++ b/Main/Common/Jace/Operations/Constant.cs
@@ -0,0 +1,48 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public abstract class Constant<T> : Operation
+    {
+        public Constant(DataType dataType, T value)
+            : base(dataType, false, true)
+        {
+            this.Value = value;
+        }
+
+        public T Value { get; private set; }
+
+        public override bool Equals(object obj)
+        {
+            Constant<T> other = obj as Constant<T>;
+            if (other != null)
+                return this.Value.Equals(other.Value);
+            else
+                return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return this.Value.GetHashCode();
+        }
+    }
+
+    public class IntegerConstant : Constant<int>
+    {
+        public IntegerConstant(int value)
+            : base(DataType.Integer, value)
+        {
+        }
+    }
+
+    public class FloatingPointConstant : Constant<double>
+    {
+        public FloatingPointConstant(double value)
+            : base(DataType.FloatingPoint, value)
+        {
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Constant.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Constant.cs.meta
index e37f87e..884d3a1 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Constant.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 65575909546ce144e83dd420135e0a01
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Division.cs b/Main/Common/Jace/Operations/Division.cs
new file mode 100644
index 0000000..afe220a
--- /dev/null
+++ b/Main/Common/Jace/Operations/Division.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Division : Operation
+    {
+        public Division(DataType dataType, Operation dividend, Operation divisor)
+            : base(dataType, dividend.DependsOnVariables || divisor.DependsOnVariables, dividend.IsIdempotent && divisor.IsIdempotent)
+        {
+            this.Dividend = dividend;
+            this.Divisor = divisor;
+        }
+
+        public Operation Dividend { get; internal set; }
+        public Operation Divisor { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Division.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Division.cs.meta
index e37f87e..92ceabb 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Division.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 353c124902c7984429815571216959bf
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Equal.cs b/Main/Common/Jace/Operations/Equal.cs
new file mode 100644
index 0000000..8c02a92
--- /dev/null
+++ b/Main/Common/Jace/Operations/Equal.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Equal : Operation
+    {
+        public Equal(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Equal.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Equal.cs.meta
index e37f87e..1040a3e 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Equal.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 87cffdc974ef87a4ab8c92b5321424c6
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Exponentiation.cs b/Main/Common/Jace/Operations/Exponentiation.cs
new file mode 100644
index 0000000..08bf94f
--- /dev/null
+++ b/Main/Common/Jace/Operations/Exponentiation.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Exponentiation : Operation
+    {
+        public Exponentiation(DataType dataType, Operation @base, Operation exponent)
+            : base(dataType, @base.DependsOnVariables || exponent.DependsOnVariables, @base.IsIdempotent && exponent.IsIdempotent)
+        {
+            Base = @base;
+            Exponent = exponent;
+        }
+
+        public Operation Base { get; internal set; }
+        public Operation Exponent { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Exponentiation.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Exponentiation.cs.meta
index e37f87e..d727984 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Exponentiation.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 2491ad50c77e631489551371d36bab46
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Function.cs b/Main/Common/Jace/Operations/Function.cs
new file mode 100644
index 0000000..b5dac27
--- /dev/null
+++ b/Main/Common/Jace/Operations/Function.cs
@@ -0,0 +1,33 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Function : Operation
+    {
+        private IList<Operation> arguments;
+
+        public Function(DataType dataType, string functionName, IList<Operation> arguments, bool isIdempotent)
+            : base(dataType, arguments.FirstOrDefault(o => o.DependsOnVariables) != null, isIdempotent && arguments.All(o => o.IsIdempotent))
+        {
+            this.FunctionName = functionName;
+            this.arguments = arguments;
+        }
+
+        public string FunctionName { get; private set; }
+
+        public IList<Operation> Arguments {
+            get
+            {
+                return arguments;
+            }
+            internal set
+            {
+                this.arguments = value;
+                this.DependsOnVariables = arguments.FirstOrDefault(o => o.DependsOnVariables) != null;
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Function.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Function.cs.meta
index e37f87e..2d85b00 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Function.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 8cea584356c80194eac8e7f5905d7764
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/GreaterOrEqualThan.cs b/Main/Common/Jace/Operations/GreaterOrEqualThan.cs
new file mode 100644
index 0000000..9513c64
--- /dev/null
+++ b/Main/Common/Jace/Operations/GreaterOrEqualThan.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class GreaterOrEqualThan : Operation
+    {
+        public GreaterOrEqualThan(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta
index e37f87e..d74513f 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: a5f7058c1a38e5e4da81a637c5ca0fb8
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/GreaterThan.cs b/Main/Common/Jace/Operations/GreaterThan.cs
new file mode 100644
index 0000000..a720fac
--- /dev/null
+++ b/Main/Common/Jace/Operations/GreaterThan.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class GreaterThan : Operation
+    {
+        public GreaterThan(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/GreaterThan.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/GreaterThan.cs.meta
index e37f87e..451136d 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/GreaterThan.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: aa4d6a9b5c1549743bd305b4e87c56b8
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/LessOrEqualThan.cs b/Main/Common/Jace/Operations/LessOrEqualThan.cs
new file mode 100644
index 0000000..3574dcc
--- /dev/null
+++ b/Main/Common/Jace/Operations/LessOrEqualThan.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class LessOrEqualThan : Operation
+    {
+        public LessOrEqualThan(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/LessOrEqualThan.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/LessOrEqualThan.cs.meta
index e37f87e..da86a83 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/LessOrEqualThan.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 22813b990f9481c41b94640a98d29719
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/LessThan.cs b/Main/Common/Jace/Operations/LessThan.cs
new file mode 100644
index 0000000..132b1f4
--- /dev/null
+++ b/Main/Common/Jace/Operations/LessThan.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class LessThan : Operation
+    {
+        public LessThan(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/LessThan.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/LessThan.cs.meta
index e37f87e..36a5163 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/LessThan.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 259c10599a2e2a74b86a83682dc05cc1
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Modulo.cs b/Main/Common/Jace/Operations/Modulo.cs
new file mode 100644
index 0000000..3271750
--- /dev/null
+++ b/Main/Common/Jace/Operations/Modulo.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Modulo : Operation
+    {
+        public Modulo(DataType dataType, Operation dividend, Operation divisor)
+            : base(dataType, dividend.DependsOnVariables || divisor.DependsOnVariables, dividend.IsIdempotent && divisor.IsIdempotent)
+        {
+            this.Dividend = dividend;
+            this.Divisor = divisor;
+        }
+
+        public Operation Dividend { get; internal set; }
+        public Operation Divisor { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Modulo.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Modulo.cs.meta
index e37f87e..adee485 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Modulo.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 20b05091ee3aab546b21bcbde58b826c
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Multiplication.cs b/Main/Common/Jace/Operations/Multiplication.cs
new file mode 100644
index 0000000..5a3587c
--- /dev/null
+++ b/Main/Common/Jace/Operations/Multiplication.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Multiplication : Operation
+    {
+        public Multiplication(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Multiplication.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Multiplication.cs.meta
index e37f87e..6d3d06e 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Multiplication.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 6156d0f899a0b484383f5a552a76e328
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/NotEqual.cs b/Main/Common/Jace/Operations/NotEqual.cs
new file mode 100644
index 0000000..905bbe4
--- /dev/null
+++ b/Main/Common/Jace/Operations/NotEqual.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class NotEqual : Operation
+    {
+        public NotEqual(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/NotEqual.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/NotEqual.cs.meta
index e37f87e..529d9e7 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/NotEqual.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: b52548570632a8a449c662af67df863b
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Operation.cs b/Main/Common/Jace/Operations/Operation.cs
new file mode 100644
index 0000000..7bfc7b0
--- /dev/null
+++ b/Main/Common/Jace/Operations/Operation.cs
@@ -0,0 +1,23 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public abstract class Operation
+    {
+        public Operation(DataType dataType, bool dependsOnVariables, bool isIdempotent)
+        {
+            this.DataType = dataType;
+            this.DependsOnVariables = dependsOnVariables;
+            this.IsIdempotent = isIdempotent;
+        }
+
+        public DataType DataType { get; private set; }
+
+        public bool DependsOnVariables { get; internal set; }
+
+        public bool IsIdempotent { get; private set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Operation.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Operation.cs.meta
index e37f87e..87a8576 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Operation.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: f31df8e25e70f36488d2dc2f14ba66d7
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Or.cs b/Main/Common/Jace/Operations/Or.cs
new file mode 100644
index 0000000..f6095a2
--- /dev/null
+++ b/Main/Common/Jace/Operations/Or.cs
@@ -0,0 +1,19 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Or : Operation
+    {
+        public Or(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Or.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Or.cs.meta
index e37f87e..42c3277 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Or.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: c39d4b13c7751e34587665bc39443e2a
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Subtraction.cs b/Main/Common/Jace/Operations/Subtraction.cs
new file mode 100644
index 0000000..933433f
--- /dev/null
+++ b/Main/Common/Jace/Operations/Subtraction.cs
@@ -0,0 +1,20 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class Subtraction : Operation
+    {
+        public Subtraction(DataType dataType, Operation argument1, Operation argument2)
+            : base(dataType, argument1.DependsOnVariables || argument2.DependsOnVariables, argument1.IsIdempotent && argument2.IsIdempotent)
+        {
+            this.Argument1 = argument1;
+            this.Argument2 = argument2;
+        }
+
+        public Operation Argument1 { get; internal set; }
+        public Operation Argument2 { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Subtraction.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Subtraction.cs.meta
index e37f87e..1afd0d4 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Subtraction.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: ddba1b68f54ac7841ac6b91dfb55a185
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/UnaryMinus.cs b/Main/Common/Jace/Operations/UnaryMinus.cs
new file mode 100644
index 0000000..c7633d5
--- /dev/null
+++ b/Main/Common/Jace/Operations/UnaryMinus.cs
@@ -0,0 +1,18 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    public class UnaryMinus : Operation
+    {
+        public UnaryMinus(DataType dataType, Operation argument)
+            : base(dataType, argument.DependsOnVariables, argument.IsIdempotent)
+        {
+            this.Argument = argument;
+        }
+
+        public Operation Argument { get; internal set; }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/UnaryMinus.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/UnaryMinus.cs.meta
index e37f87e..74d4e63 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/UnaryMinus.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 350a4f213d50975468899e32708859b1
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Operations/Variable.cs b/Main/Common/Jace/Operations/Variable.cs
new file mode 100644
index 0000000..618be27
--- /dev/null
+++ b/Main/Common/Jace/Operations/Variable.cs
@@ -0,0 +1,37 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Operations
+{
+    /// <summary>
+    /// Represents a variable in a mathematical formula.
+    /// </summary>
+    public class Variable : Operation
+    {
+        public Variable(string name)
+            : base(DataType.FloatingPoint, true, false)
+        {
+            this.Name = name;
+        }
+
+        public string Name { get; private set; }
+
+        public override bool Equals(object obj)
+        {
+            Variable other = obj as Variable;
+            if (other != null)
+            {
+                return this.Name.Equals(other.Name);
+            }
+            else
+                return false;
+        }
+
+        public override int GetHashCode()
+        {
+            return this.Name.GetHashCode();
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Operations/Variable.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Operations/Variable.cs.meta
index e37f87e..346c06d 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Operations/Variable.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 34fb9551f3d90cf418e9557742735a18
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Optimizer.cs b/Main/Common/Jace/Optimizer.cs
new file mode 100644
index 0000000..16fe53f
--- /dev/null
+++ b/Main/Common/Jace/Optimizer.cs
@@ -0,0 +1,76 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Jace.Operations;
+using Jace.Execution;
+
+namespace Jace
+{
+    public class Optimizer
+    {
+        private readonly IExecutor executor;
+
+        public Optimizer(IExecutor executor)
+        {
+            this.executor = executor;
+        }
+
+        public Operation Optimize(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry)
+        {
+            if (!operation.DependsOnVariables && operation.IsIdempotent && operation.GetType() != typeof(IntegerConstant)
+                && operation.GetType() != typeof(FloatingPointConstant))
+            {
+                double result = executor.Execute(operation, functionRegistry, constantRegistry);
+                return new FloatingPointConstant(result);
+            }
+            else
+            {
+                if (operation.GetType() == typeof(Addition))
+                {
+                    Addition addition = (Addition)operation;
+                    addition.Argument1 = Optimize(addition.Argument1, functionRegistry, constantRegistry);
+                    addition.Argument2 = Optimize(addition.Argument2, functionRegistry, constantRegistry);
+                }
+                else if (operation.GetType() == typeof(Subtraction))
+                {
+                    Subtraction substraction = (Subtraction)operation;
+                    substraction.Argument1 = Optimize(substraction.Argument1, functionRegistry, constantRegistry);
+                    substraction.Argument2 = Optimize(substraction.Argument2, functionRegistry, constantRegistry);
+                }
+                else if (operation.GetType() == typeof(Multiplication))
+                {
+                    Multiplication multiplication = (Multiplication)operation;
+                    multiplication.Argument1 = Optimize(multiplication.Argument1, functionRegistry, constantRegistry);
+                    multiplication.Argument2 = Optimize(multiplication.Argument2, functionRegistry, constantRegistry);
+
+                    if ((multiplication.Argument1.GetType() == typeof(FloatingPointConstant) && ((FloatingPointConstant)multiplication.Argument1).Value == 0.0)
+                        || (multiplication.Argument2.GetType() == typeof(FloatingPointConstant) && ((FloatingPointConstant)multiplication.Argument2).Value == 0.0))
+                    {
+                        return new FloatingPointConstant(0.0);
+                    }
+                }
+                else if (operation.GetType() == typeof(Division))
+                {
+                    Division division = (Division)operation;
+                    division.Dividend = Optimize(division.Dividend, functionRegistry, constantRegistry);
+                    division.Divisor = Optimize(division.Divisor, functionRegistry, constantRegistry);
+                }
+                else if (operation.GetType() == typeof(Exponentiation))
+                {
+                    Exponentiation division = (Exponentiation)operation;
+                    division.Base = Optimize(division.Base, functionRegistry, constantRegistry);
+                    division.Exponent = Optimize(division.Exponent, functionRegistry, constantRegistry);
+                }
+                else if(operation.GetType() == typeof(Function))
+                {
+                    Function function = (Function)operation;
+                    IList<Operation> arguments = function.Arguments.Select(a => Optimize(a, functionRegistry, constantRegistry)).ToList();
+                    function.Arguments = arguments;
+                }
+
+                return operation;
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Optimizer.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Optimizer.cs.meta
index e37f87e..4508183 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Optimizer.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 3d95e6f9d0000c54c9abd40e1f81ab4a
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/ParseException.cs b/Main/Common/Jace/ParseException.cs
new file mode 100644
index 0000000..1f3ea3f
--- /dev/null
+++ b/Main/Common/Jace/ParseException.cs
@@ -0,0 +1,19 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace
+{
+    /// <summary>
+    /// The exception that is thrown when there is a syntax error in the formula provided 
+    /// to the calculation engine.
+    /// </summary>
+    public class ParseException : Exception
+    {
+        public ParseException(string message)
+            : base(message)
+        {
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/ParseException.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/ParseException.cs.meta
index e37f87e..7574695 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/ParseException.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: df2bb315758f36a429552a2b28b667e2
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Core/GameEngine/Common.meta b/Main/Common/Jace/Tokenizer.meta
similarity index 76%
copy from Main/Core/GameEngine/Common.meta
copy to Main/Common/Jace/Tokenizer.meta
index 29b34bc..dae8336 100644
--- a/Main/Core/GameEngine/Common.meta
+++ b/Main/Common/Jace/Tokenizer.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
+guid: 2b67a41b280e4f143861b982f7dbc630
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}
diff --git a/Main/Common/Jace/Tokenizer/Token.cs b/Main/Common/Jace/Tokenizer/Token.cs
new file mode 100644
index 0000000..8b2a13f
--- /dev/null
+++ b/Main/Common/Jace/Tokenizer/Token.cs
@@ -0,0 +1,33 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Tokenizer
+{
+    /// <summary>
+    /// Represents an input token
+    /// </summary>
+    public struct Token
+    {
+        /// <summary>
+        /// The start position of the token in the input function text.
+        /// </summary>
+        public int StartPosition;
+        
+        /// <summary>
+        /// The length of token in the input function text.
+        /// </summary>
+        public int Length;
+
+        /// <summary>
+        /// The type of the token.
+        /// </summary>
+        public TokenType TokenType;
+
+        /// <summary>
+        /// The value of the token.
+        /// </summary>
+        public object Value;
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Tokenizer/Token.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Tokenizer/Token.cs.meta
index e37f87e..9d7cb38 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Tokenizer/Token.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 9fa03dbd0043c2b41a256b11a7669fc9
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Tokenizer/TokenReader.cs b/Main/Common/Jace/Tokenizer/TokenReader.cs
new file mode 100644
index 0000000..18400d5
--- /dev/null
+++ b/Main/Common/Jace/Tokenizer/TokenReader.cs
@@ -0,0 +1,255 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Tokenizer
+{
+    /// <summary>
+    /// A token reader that converts the input string in a list of tokens.
+    /// </summary>
+    public class TokenReader
+    {
+        private readonly CultureInfo cultureInfo;
+        private readonly char decimalSeparator;
+        private readonly char argumentSeparator;
+
+        public TokenReader() 
+            : this(CultureInfo.CurrentCulture)
+        {
+        }
+
+        public TokenReader(CultureInfo cultureInfo)
+        {
+            this.cultureInfo = cultureInfo;
+            this.decimalSeparator = cultureInfo.NumberFormat.NumberDecimalSeparator[0];
+            this.argumentSeparator = cultureInfo.TextInfo.ListSeparator[0];
+        }
+
+        /// <summary>
+        /// Read in the provided formula and convert it into a list of takens that can be processed by the
+        /// Abstract Syntax Tree Builder.
+        /// </summary>
+        /// <param name="formula">The formula that must be converted into a list of tokens.</param>
+        /// <returns>The list of tokens for the provided formula.</returns>
+        public List<Token> Read(string formula)
+        {
+            if (string.IsNullOrEmpty(formula))
+                throw new ArgumentNullException("formula");
+
+            List<Token> tokens = new List<Token>();
+
+            char[] characters = formula.ToCharArray();
+
+            bool isFormulaSubPart = true;
+            bool isScientific = false;
+
+            for(int i = 0; i < characters.Length; i++)
+            {
+                if (IsPartOfNumeric(characters[i], true, isFormulaSubPart))
+                {
+                    StringBuilder buffer = new StringBuilder();
+                    buffer.Append(characters[i]);
+                    //string buffer = "" + characters[i];
+                    int startPosition = i;
+                                       
+
+                    while (++i < characters.Length && IsPartOfNumeric(characters[i], false, isFormulaSubPart))
+                    {
+                        if (isScientific && IsScientificNotation(characters[i]))
+                            throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
+
+                        if (IsScientificNotation(characters[i]))
+                        {
+                            isScientific = IsScientificNotation(characters[i]);
+
+                            if (characters[i + 1] == '-')
+                            {
+                                buffer.Append(characters[i++]);
+                            }
+                        }
+
+                        buffer.Append(characters[i]);
+                    }
+
+                    // Verify if we do not have an int
+                    int intValue;
+                    if (int.TryParse(buffer.ToString(), out intValue))
+                    {
+                        tokens.Add(new Token() { TokenType = TokenType.Integer, Value = intValue, StartPosition = startPosition, Length = i - startPosition });
+                        isFormulaSubPart = false;
+                    }
+                    else
+                    {
+                        double doubleValue;
+                        if (double.TryParse(buffer.ToString(), NumberStyles.Float | NumberStyles.AllowThousands,
+                            cultureInfo, out doubleValue))
+                        {
+                            tokens.Add(new Token() { TokenType = TokenType.FloatingPoint, Value = doubleValue, StartPosition = startPosition, Length = i - startPosition });
+                            isScientific = false;
+                            isFormulaSubPart = false;
+                        }
+                        else if (buffer.ToString() == "-")
+                        {
+                            // Verify if we have a unary minus, we use the token '_' for a unary minus in the AST builder
+                            tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '_', StartPosition = startPosition, Length = 1 });
+                        }
+                        // Else we skip
+                    }
+
+                    if (i == characters.Length)
+                    {
+                        // Last character read
+                        continue;
+                    }
+                }
+
+                if (IsPartOfVariable(characters[i], true))
+                {
+                    string buffer = "" + characters[i];
+                    int startPosition = i;
+
+                    while (++i < characters.Length && IsPartOfVariable(characters[i], false))
+                    {
+                        buffer += characters[i];
+                    }
+
+                    tokens.Add(new Token() { TokenType = TokenType.Text, Value = buffer, StartPosition = startPosition, Length = i -startPosition });
+                    isFormulaSubPart = false;
+
+                    if (i == characters.Length)
+                    {
+                        // Last character read
+                        continue;
+                    }
+                }
+                if (characters[i] == this.argumentSeparator)
+                {
+                    tokens.Add(new Token() { TokenType = Tokenizer.TokenType.ArgumentSeparator, Value = characters[i], StartPosition = i, Length = 1 });
+                    isFormulaSubPart = false;
+                }
+                else
+                {
+                    switch (characters[i])
+                    { 
+                        case ' ':
+                            continue;
+                        case '+':
+                        case '-':
+                        case '*':
+                        case '/':
+                        case '^':
+                        case '%':
+                        case '鈮�':
+                        case '鈮�':
+                        case '鈮�':
+                            if (IsUnaryMinus(characters[i], tokens))
+                            {
+                                // We use the token '_' for a unary minus in the AST builder
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '_', StartPosition = i, Length = 1 });
+                            }
+                            else
+                            {
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = characters[i], StartPosition = i, Length = 1 });                            
+                            }
+                            isFormulaSubPart = true;
+                            break;
+                        case '(':
+                            tokens.Add(new Token() { TokenType = TokenType.LeftBracket, Value = characters[i], StartPosition = i, Length = 1 });
+                            isFormulaSubPart = true;
+                            break;
+                        case ')':
+                            tokens.Add(new Token() { TokenType = TokenType.RightBracket, Value = characters[i], StartPosition = i, Length = 1 });
+                            isFormulaSubPart = false;
+                            break;
+                        case '<':
+                            if (i + 1 < characters.Length && characters[i + 1] == '=')
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '鈮�', StartPosition = i++, Length = 2 });
+                            else
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '<', StartPosition = i, Length = 1 });
+                            isFormulaSubPart = false;
+                            break;
+                        case '>':
+                            if (i + 1 < characters.Length && characters[i + 1] == '=')
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '鈮�', StartPosition = i++, Length = 2 });
+                            else
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '>', StartPosition = i, Length = 1 });
+                            isFormulaSubPart = false;
+                            break;
+                        case '!':
+                            if (i + 1 < characters.Length && characters[i + 1] == '=')
+                            {
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '鈮�', StartPosition = i++, Length = 2 });
+                                isFormulaSubPart = false;
+                            }
+                            else
+                                throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
+                            break;
+                        case '&':
+                            if (i + 1 < characters.Length && characters[i + 1] == '&')
+                            {
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '&', StartPosition = i++, Length = 2 });
+                                isFormulaSubPart = false;
+                            }
+                            else
+                                throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
+                            break;
+                        case '|':
+                            if (i + 1 < characters.Length && characters[i + 1] == '|')
+                            {
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '|', StartPosition = i++, Length = 2 });
+                                isFormulaSubPart = false;
+                            }
+                            else
+                                throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
+                            break;
+                        case '=':
+                            if (i + 1 < characters.Length && characters[i + 1] == '=')
+                            {
+                                tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '=', StartPosition = i++, Length = 2 });
+                                isFormulaSubPart = false;
+                            }
+                            else
+                                throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
+                            break;
+                        default:
+                            throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
+                    }
+                }
+            }
+
+            return tokens;
+        }
+
+        private bool IsPartOfNumeric(char character, bool isFirstCharacter, bool isFormulaSubPart)
+        {
+            return character == decimalSeparator || (character >= '0' && character <= '9') || (isFormulaSubPart && isFirstCharacter && character == '-') || (!isFirstCharacter && character == 'e') || (!isFirstCharacter && character == 'E');
+        }
+
+        private bool IsPartOfVariable(char character, bool isFirstCharacter)
+        {
+            return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z') || (!isFirstCharacter && character >= '0' && character <= '9') || (!isFirstCharacter && character == '_');
+        }
+
+        private bool IsUnaryMinus(char currentToken, List<Token> tokens)
+        {
+            if (currentToken == '-')
+            {
+                Token previousToken = tokens[tokens.Count - 1];
+
+                return !(previousToken.TokenType == TokenType.FloatingPoint ||
+                         previousToken.TokenType == TokenType.Integer ||
+                         previousToken.TokenType == TokenType.Text ||
+                         previousToken.TokenType == TokenType.RightBracket);
+            }
+            else
+                return false;
+        }
+
+        private bool IsScientificNotation(char currentToken)
+        {
+            return currentToken == 'e' || currentToken == 'E';
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Tokenizer/TokenReader.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Tokenizer/TokenReader.cs.meta
index e37f87e..8237aed 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Tokenizer/TokenReader.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: e7f38551c1077ee4896e7b1d5aabd1a2
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Tokenizer/TokenType.cs b/Main/Common/Jace/Tokenizer/TokenType.cs
new file mode 100644
index 0000000..f77c3df
--- /dev/null
+++ b/Main/Common/Jace/Tokenizer/TokenType.cs
@@ -0,0 +1,18 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Tokenizer
+{
+    public enum TokenType
+    {
+        Integer,
+        FloatingPoint,
+        Text,
+        Operation,
+        LeftBracket,
+        RightBracket,
+        ArgumentSeparator
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Tokenizer/TokenType.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Tokenizer/TokenType.cs.meta
index e37f87e..2cc8356 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Tokenizer/TokenType.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: ca43eadac5024d040a3f89d8c15e0967
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Core/GameEngine/Common.meta b/Main/Common/Jace/Util.meta
similarity index 76%
copy from Main/Core/GameEngine/Common.meta
copy to Main/Common/Jace/Util.meta
index 29b34bc..6e283a2 100644
--- a/Main/Core/GameEngine/Common.meta
+++ b/Main/Common/Jace/Util.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
+guid: 78f08781a2381ff409b92d3767b449bc
 folderAsset: yes
 DefaultImporter:
   externalObjects: {}
diff --git a/Main/Common/Jace/Util/EngineUtil.cs b/Main/Common/Jace/Util/EngineUtil.cs
new file mode 100644
index 0000000..612b0c2
--- /dev/null
+++ b/Main/Common/Jace/Util/EngineUtil.cs
@@ -0,0 +1,46 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Util
+{
+    /// <summary>
+    /// Utility methods of Jace.NET that can be used throughout the engine.
+    /// </summary>
+    internal  static class EngineUtil
+    {
+        static internal IDictionary<string, double> ConvertVariableNamesToLowerCase(IDictionary<string, double> variables)
+        {
+            Dictionary<string, double> temp = new Dictionary<string, double>();
+            foreach (KeyValuePair<string, double> keyValuePair in variables)
+            {
+                temp.Add(keyValuePair.Key.ToLowerFast(), keyValuePair.Value);
+            }
+
+            return temp;
+        }
+
+        // This is a fast ToLower for strings that are in ASCII
+        static internal string ToLowerFast(this string text)
+        {
+            StringBuilder buffer = new StringBuilder(text.Length);
+
+            for(int i = 0; i < text.Length; i++)
+            {
+                char c = text[i];
+
+                if (c >= 'A' && c <= 'Z')
+                {
+                    buffer.Append((char)(c + 32));
+                }
+                else 
+                {
+                    buffer.Append(c);
+                }
+            }
+
+            return buffer.ToString();
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Util/EngineUtil.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Util/EngineUtil.cs.meta
index e37f87e..be229e1 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Util/EngineUtil.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 3725a3ccea7f46f4bb6980cdd7823745
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Util/FuncAdapter.cs b/Main/Common/Jace/Util/FuncAdapter.cs
new file mode 100644
index 0000000..d5add57
--- /dev/null
+++ b/Main/Common/Jace/Util/FuncAdapter.cs
@@ -0,0 +1,126 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Text;
+using Jace.Operations;
+
+namespace Jace.Util
+{
+    /// <summary>
+    /// An adapter for creating a func wrapper around a func accepting a dictionary. The wrapper
+    /// can create a func that has an argument for every expected key in the dictionary.
+    /// </summary>
+    public class FuncAdapter
+    {
+        /// <summary>
+        /// Wrap the parsed the function into a delegate of the specified type. The delegate must accept 
+        /// the parameters defined in the parameters collection. The order of parameters is respected as defined
+        /// in parameters collection.
+        /// <br/>
+        /// The function must accept a dictionary of strings and doubles as input. The values passed to the 
+        /// wrapping function will be passed to the function using the dictionary. The keys in the dictionary
+        /// are the names of the parameters of the wrapping function.
+        /// </summary>
+        /// <param name="parameters">The required parameters of the wrapping function delegate.</param>
+        /// <param name="function">The function that must be wrapped.</param>
+        /// <returns>A delegate instance of the required type.</returns>
+        public Delegate Wrap(IEnumerable<Jace.Execution.ParameterInfo> parameters, 
+            Func<IDictionary<string, double>, double> function)
+        {
+            Jace.Execution.ParameterInfo[] parameterArray = parameters.ToArray();
+
+            return GenerateDelegate(parameterArray, function);
+        }
+
+        // Uncomment for debugging purposes
+        //public void CreateDynamicModuleBuilder()
+        //{
+        //    AssemblyName assemblyName = new AssemblyName("JaceDynamicAssembly");
+        //    AppDomain domain = AppDomain.CurrentDomain;
+        //    AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName,
+        //        AssemblyBuilderAccess.RunAndSave);
+        //    ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, "test.dll");
+
+        //    TypeBuilder typeBuilder = moduleBuilder.DefineType("MyTestClass");
+
+        //    MethodBuilder method = typeBuilder.DefineMethod("MyTestMethod", MethodAttributes.Static, typeof(double),
+        //       new Type[] { typeof(FuncAdapterArguments), typeof(int), typeof(double) });
+
+        //    ILGenerator generator = method.GetILGenerator();
+        //    GenerateMethodBody(generator, new List<Calculator.Execution.ParameterInfo>() { 
+        //        new Calculator.Execution.ParameterInfo() { Name = "test1", DataType = DataType.Integer },
+        //        new Calculator.Execution.ParameterInfo() { Name = "test2", DataType = DataType.FloatingPoint }},
+        //        (a) => 0.0);
+
+        //    typeBuilder.CreateType();
+
+        //    assemblyBuilder.Save(@"test.dll");
+        //}
+
+        private Delegate GenerateDelegate(Jace.Execution.ParameterInfo[] parameterArray,
+            Func<Dictionary<string, double>, double> function)
+        {
+            Type delegateType = GetDelegateType(parameterArray);
+            Type dictionaryType = typeof(Dictionary<string, double>);
+
+            ParameterExpression dictionaryExpression =
+                Expression.Variable(typeof(Dictionary<string, double>), "dictionary");
+            BinaryExpression dictionaryAssignExpression =
+                Expression.Assign(dictionaryExpression, Expression.New(dictionaryType));
+
+            ParameterExpression[] parameterExpressions = new ParameterExpression[parameterArray.Length];
+
+            List<Expression> methodBody = new List<Expression>();
+            methodBody.Add(dictionaryAssignExpression);
+
+            for (int i = 0; i < parameterArray.Length; i++)
+            {
+                // Create parameter expression for each func parameter
+                Type parameterType = parameterArray[i].DataType == DataType.FloatingPoint ? typeof(double) : typeof(int);
+                parameterExpressions[i] = Expression.Parameter(parameterType, parameterArray[i].Name);
+
+                methodBody.Add(Expression.Call(dictionaryExpression,
+                    dictionaryType.GetRuntimeMethod("Add", new Type[] { typeof(string), typeof(double) }),
+                    Expression.Constant(parameterArray[i].Name),
+                    Expression.Convert(parameterExpressions[i], typeof(double)))
+                    );
+            }
+
+            InvocationExpression invokeExpression = Expression.Invoke(Expression.Constant(function), dictionaryExpression);
+            methodBody.Add(invokeExpression);
+
+            LambdaExpression lambdaExpression = Expression.Lambda(delegateType,
+                Expression.Block(new[] { dictionaryExpression }, methodBody),
+                parameterExpressions);
+
+            return lambdaExpression.Compile();
+        }
+
+        private Type GetDelegateType(Jace.Execution.ParameterInfo[] parameters)
+        {
+            string funcTypeName = string.Format("System.Func`{0}", parameters.Length + 1);
+            Type funcType = Type.GetType(funcTypeName);
+
+            Type[] typeArguments = new Type[parameters.Length + 1];
+            for (int i = 0; i < parameters.Length; i++)
+                typeArguments[i] = (parameters[i].DataType == DataType.FloatingPoint) ? typeof(double) : typeof(int);
+            typeArguments[typeArguments.Length - 1] = typeof(double);
+
+            return funcType.MakeGenericType(typeArguments);
+        }
+
+        private class FuncAdapterArguments
+        {
+            private readonly Func<Dictionary<string, double>, double> function;
+
+            public FuncAdapterArguments(Func<Dictionary<string, double>, double> function)
+            {
+                this.function = function;
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Util/FuncAdapter.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Util/FuncAdapter.cs.meta
index e37f87e..8919198 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Util/FuncAdapter.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 878d5799cfe90cb4dbf7aebc38c3e9ae
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Util/MathExtended.cs b/Main/Common/Jace/Util/MathExtended.cs
new file mode 100644
index 0000000..189295b
--- /dev/null
+++ b/Main/Common/Jace/Util/MathExtended.cs
@@ -0,0 +1,81 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Text;
+using System.Linq;
+
+namespace Jace.Util
+{
+    static class MathExtended
+    {
+        /// <summary>
+        /// Partitions the given list around a pivot element such that all elements on left of pivot are <= pivot
+        /// and the ones at thr right are > pivot. This method can be used for sorting, N-order statistics such as
+        /// as median finding algorithms.
+        /// Pivot is selected ranodmly if random number generator is supplied else its selected as last element in the list.
+        /// Reference: Introduction to Algorithms 3rd Edition, Corman et al, pp 171
+        /// </summary>
+        private static int Partition<T>(this IList<T> list, int start, int end, Random rnd = null) where T : IComparable<T>
+        {
+            if (rnd != null)
+                list.Swap(end, rnd.Next(start, end + 1));
+
+            var pivot = list[end];
+            var lastLow = start - 1;
+            for (var i = start; i < end; i++)
+            {
+                if (list[i].CompareTo(pivot) <= 0)
+                    list.Swap(i, ++lastLow);
+            }
+            list.Swap(end, ++lastLow);
+            return lastLow;
+        }
+
+        /// <summary>
+        /// Returns Nth smallest element from the list. Here n starts from 0 so that n=0 returns minimum, n=1 returns 2nd smallest element etc.
+        /// Note: specified list would be mutated in the process.
+        /// Reference: Introduction to Algorithms 3rd Edition, Corman et al, pp 216
+        /// </summary>
+        public static T NthOrderStatistic<T>(this IList<T> list, int n, Random rnd = null) where T : IComparable<T>
+        {
+            return NthOrderStatistic(list, n, 0, list.Count - 1, rnd);
+        }
+        private static T NthOrderStatistic<T>(this IList<T> list, int n, int start, int end, Random rnd) where T : IComparable<T>
+        {
+            while (true)
+            {
+                var pivotIndex = list.Partition(start, end, rnd);
+                if (pivotIndex == n)
+                    return list[pivotIndex];
+
+                if (n < pivotIndex)
+                    end = pivotIndex - 1;
+                else
+                    start = pivotIndex + 1;
+            }
+        }
+
+        public static void Swap<T>(this IList<T> list, int i, int j)
+        {
+            if (i == j)   //This check is not required but Partition function may make many calls so its for perf reason
+                return;
+            var temp = list[i];
+            list[i] = list[j];
+            list[j] = temp;
+        }
+
+        /// <summary>
+        /// Note: specified list would be mutated in the process.
+        /// </summary>
+        public static T Median<T>(this IList<T> list) where T : IComparable<T>
+        {
+            return list.NthOrderStatistic((list.Count - 1) / 2);
+        }
+
+        public static double Median<T>(this IEnumerable<T> sequence, Func<T, double> getValue)
+        {
+            var list = sequence.Select(getValue).ToList();
+            var mid = (list.Count - 1) / 2;
+            return list.NthOrderStatistic(mid);
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Util/MathExtended.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Util/MathExtended.cs.meta
index e37f87e..f00b53a 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Util/MathExtended.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 9fd567b7004de6e41ae2720dd0b3c1ce
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Util/MathUtil.cs b/Main/Common/Jace/Util/MathUtil.cs
new file mode 100644
index 0000000..c64844a
--- /dev/null
+++ b/Main/Common/Jace/Util/MathUtil.cs
@@ -0,0 +1,30 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace.Util
+{
+    public static class MathUtil
+    {
+        public static double Cot(double a)
+        {
+            return 1 / Math.Tan(a);
+        }
+
+        public static double Acot(double d)
+        {
+            return Math.Atan(1 / d);
+        }
+
+        public static double Csc(double a)
+        {
+            return 1 / Math.Sin(a);
+        }
+
+        public static double Sec(double d)
+        {
+            return 1 / Math.Cos(d);
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Util/MathUtil.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Util/MathUtil.cs.meta
index e37f87e..e790998 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Util/MathUtil.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: b51d1dc4b0eab6e4c9cfb6bdec1d86ea
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Util/MemoryCache.cs b/Main/Common/Jace/Util/MemoryCache.cs
new file mode 100644
index 0000000..c2d728a
--- /dev/null
+++ b/Main/Common/Jace/Util/MemoryCache.cs
@@ -0,0 +1,165 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections.Concurrent;
+using System.Threading;
+
+namespace Jace.Util
+{
+    /// <summary>
+    /// An in-memory based cache to store objects. The implementation is thread safe and supports
+    /// the multiple platforms supported by Jace (.NET, WinRT, WP7 and WP8).
+    /// </summary>
+    /// <typeparam name="TKey">The type of the keys.</typeparam>
+    /// <typeparam name="TValue">The type of the values.</typeparam>
+    public class MemoryCache<TKey, TValue>
+    {
+        private readonly int maximumSize;
+        private readonly int reductionSize;
+
+        private long counter; // We cannot use DateTime.Now, because the precission is not high enough.
+
+        private readonly ConcurrentDictionary<TKey, CacheItem> dictionary;
+
+        /// <summary>
+        /// Create a new instance of the <see cref="MemoryCache"/>.
+        /// </summary>
+        /// <param name="maximumSize">The maximum allowed number of items in the cache.</param>
+        /// <param name="reductionSize">The number of items to be deleted per cleanup of the cache.</param>
+        public MemoryCache(int maximumSize, int reductionSize)
+        {
+            if (maximumSize < 1)
+                throw new ArgumentOutOfRangeException("maximumSize",
+                    "The maximum allowed number of items in the cache must be at least one.");
+
+            if (reductionSize < 1)
+                throw new ArgumentOutOfRangeException("reductionSize",
+                    "The cache reduction size must be at least one.");
+
+            this.maximumSize = maximumSize;
+            this.reductionSize = reductionSize;
+
+            this.dictionary = new ConcurrentDictionary<TKey, CacheItem>();
+        }
+
+        /// <summary>
+        /// Get the value in the cache for the given key.
+        /// </summary>
+        /// <param name="key">The key to lookup in the cache.</param>
+        /// <returns>The value for the given key.</returns>
+        public TValue this[TKey key]
+        {
+            get
+            {
+                CacheItem cacheItem = dictionary[key];
+                cacheItem.Accessed();
+                return cacheItem.Value;
+            }
+        }
+
+        /// <summary>
+        /// Gets the number of items in the cache.
+        /// </summary>
+        public int Count
+        {
+            get
+            {
+                return dictionary.Count;
+            }
+        }
+
+        /// <summary>
+        /// Returns true if an item with the given key is present in the cache.
+        /// </summary>
+        /// <param name="key">The key to lookup in the cache.</param>
+        /// <returns>True if an item is present in the cache for the given key.</returns>
+        public bool ContainsKey(TKey key)
+        {
+            return dictionary.ContainsKey(key);
+        }
+
+        public bool TryGetValue (TKey key, out TValue result)
+        {
+            if (dictionary.TryGetValue(key, out var cachedItem))
+            {
+                cachedItem.Accessed();
+                result = cachedItem.Value;
+                return true;
+            }
+            else
+            {
+                result = default(TValue);
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// If for a given key an item is present in the cache, this method will return
+        /// the value for the given key. If no item is present in the cache for the given
+        /// key, the valueFactory is executed to produce the value. This value is stored in
+        /// the cache and returned to the caller.
+        /// </summary>
+        /// <param name="key">The key to lookup in the cache.</param>
+        /// <param name="valueFactory">The factory to produce the value matching with the key.</param>
+        /// <returns>The value for the given key.</returns>
+        public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
+        {
+            if (valueFactory == null)
+                throw new ArgumentNullException("valueFactory");
+
+            CacheItem cacheItem = dictionary.GetOrAdd(key, k => 
+                {
+                    EnsureCacheStorageAvailable();
+
+                    TValue value = valueFactory(k);
+                    return new CacheItem(this, valueFactory(k));
+                });
+            return cacheItem.Value;
+        }
+
+        /// <summary>
+        /// Ensure that the cache has room for an additional item.
+        /// If there is not enough room anymore, force a removal of oldest
+        /// accessed items in the cache.
+        /// </summary>
+        private void EnsureCacheStorageAvailable()
+        {
+            if (dictionary.Count >= maximumSize) // >= because we want to add an item after this method
+            {
+                IList<TKey> keysToDelete = (from p in dictionary.ToArray()
+                                            where p.Key != null && p.Value != null
+                                            orderby p.Value.LastAccessed ascending
+                                            select p.Key).Take(reductionSize).ToList();
+
+                foreach (TKey key in keysToDelete)
+                {
+                    CacheItem cacheItem;
+                    dictionary.TryRemove(key, out cacheItem);
+                }
+            }
+        }
+
+        private class CacheItem
+        {
+            private MemoryCache<TKey, TValue> cache;
+
+            public CacheItem(MemoryCache<TKey, TValue> cache, TValue value)
+            {
+                this.cache = cache;
+                this.Value = value;
+
+                Accessed();
+            }
+
+            public TValue Value { get; private set; }
+
+            public long LastAccessed { get; private set; }
+
+            public void Accessed()
+            {
+                this.LastAccessed = Interlocked.Increment(ref cache.counter);
+            }
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Util/MemoryCache.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Util/MemoryCache.cs.meta
index e37f87e..aa87558 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Util/MemoryCache.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: da84d8440a462ad44b25f3e1ae8d0c36
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/Util/TypeExtensions.cs b/Main/Common/Jace/Util/TypeExtensions.cs
new file mode 100644
index 0000000..952812f
--- /dev/null
+++ b/Main/Common/Jace/Util/TypeExtensions.cs
@@ -0,0 +1,43 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Jace.Util
+{
+    public static class TypeExtensions
+    {
+        /// <summary>
+        /// Get constructor for a given type matching with the parameter types provided.
+        /// </summary>
+        /// <param name="type">The type for witch a matching constructor must be found.</param>
+        /// <param name="parameters">The types of the parameters of the constructor.</param>
+        /// <returns>The matching constructor.</returns>
+        public static ConstructorInfo GetConstructor(this Type type, Type[] parameters)
+        {
+            IEnumerable<ConstructorInfo> constructors =
+                type.GetTypeInfo().DeclaredConstructors.Where(c => c.GetParameters().Length == parameters.Length);
+
+            foreach (ConstructorInfo constructor in constructors)
+            {
+                bool parametersMatch = true;
+
+                ParameterInfo[] constructorParameters = constructor.GetParameters();
+                for (int i = 0; i < parameters.Length; i++)
+                {
+                    if (parameters[i] != constructorParameters[i].ParameterType)
+                    {
+                        parametersMatch = false;
+                        break;
+                    }
+                }
+
+                if (parametersMatch)
+                    return constructor;
+            }
+
+            throw new Exception("No constructor was found matching with the provided parameters.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/Util/TypeExtensions.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/Util/TypeExtensions.cs.meta
index e37f87e..59e7da8 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/Util/TypeExtensions.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: adedcd02cabd3f94d8be60ce8b3b4a1e
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Common/Jace/VariableNotDefinedException.cs b/Main/Common/Jace/VariableNotDefinedException.cs
new file mode 100644
index 0000000..7aab7d9
--- /dev/null
+++ b/Main/Common/Jace/VariableNotDefinedException.cs
@@ -0,0 +1,23 @@
+锘縰sing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Jace
+{
+    /// <summary>
+    /// An exception thrown when a formula must be executed with a variable that is not defined.
+    /// </summary>
+    public class VariableNotDefinedException : Exception
+    {
+        public VariableNotDefinedException(string message)
+            : base(message)
+        {
+        }
+
+        public VariableNotDefinedException(string message, Exception innerException)
+            : base(message, innerException)
+        {
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Common/Jace/VariableNotDefinedException.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Common/Jace/VariableNotDefinedException.cs.meta
index e37f87e..f1e2fc7 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Common/Jace/VariableNotDefinedException.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: b54823a4c30c0db4992f01e0a5b4bcb6
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Component/UI/Effect/UIEffectPlayer.cs b/Main/Component/UI/Effect/UIEffectPlayer.cs
index c71396f..35a5bfe 100644
--- a/Main/Component/UI/Effect/UIEffectPlayer.cs
+++ b/Main/Component/UI/Effect/UIEffectPlayer.cs
@@ -47,6 +47,7 @@
         if (!isInit)
         {
             InitComponent(showLog);
+            //effeid 涓�0涔熷垵濮嬪寲鎴愬姛锛岄伩鍏嶉噸澶嶅鐞嗭紝鍦ㄥ彉鏇磂ffectid鏃朵細閲嶆柊鍒濆鍖�
             isInit = true;
         }
         else
@@ -56,7 +57,8 @@
             {
                 this.gameObject.SetActive(true);
             }
-            if (effectConfig.isSpine != 0)
+            //闃茶寖effeid 涓�0
+            if (effectConfig != null && effectConfig.isSpine != 0)
             {
                 PlayerTheSpineAnim();
             }
diff --git a/Main/Config/ConfigManager.cs b/Main/Config/ConfigManager.cs
index 8d2b82d..efaf67e 100644
--- a/Main/Config/ConfigManager.cs
+++ b/Main/Config/ConfigManager.cs
@@ -43,6 +43,7 @@
             typeof(DamageNumConfig),
             typeof(DirtyWordConfig),
             typeof(FaceConfig),
+            typeof(FightPowerRatioConfig),
             typeof(HeroLineupHaloConfig),
             typeof(HeroQualityLVConfig),
             typeof(ItemConfig),
@@ -221,6 +222,8 @@
         ClearConfigDictionary<DirtyWordConfig>();
         // 娓呯┖ FaceConfig 瀛楀吀
         ClearConfigDictionary<FaceConfig>();
+        // 娓呯┖ FightPowerRatioConfig 瀛楀吀
+        ClearConfigDictionary<FightPowerRatioConfig>();
         // 娓呯┖ HeroLineupHaloConfig 瀛楀吀
         ClearConfigDictionary<HeroLineupHaloConfig>();
         // 娓呯┖ HeroQualityLVConfig 瀛楀吀
diff --git a/Main/Config/Configs/FightPowerRatioConfig.cs b/Main/Config/Configs/FightPowerRatioConfig.cs
new file mode 100644
index 0000000..4f87731
--- /dev/null
+++ b/Main/Config/Configs/FightPowerRatioConfig.cs
@@ -0,0 +1,107 @@
+锘�//--------------------------------------------------------
+//    [Author]:           YYL
+//    [  Date ]:           2025骞�8鏈�18鏃�
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class FightPowerRatioConfig : ConfigBase<int, FightPowerRatioConfig>
+{
+    static FightPowerRatioConfig()
+    {
+        // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+        visit = true; 
+    }
+
+    public int LV;
+	public float AtkRatio;
+	public float MaxHPRatio;
+	public float DefRatio;
+	public float StunRateRatio;
+	public float SuperHitRateRatio;
+	public float ComboRateRatio;
+	public float MissRateRatio;
+	public float ParryRateRatio;
+	public float SuckHPPerRatio;
+	public float StunRateDefRatio;
+	public float SuperHitRateDefRatio;
+	public float ComboRateDefRatio;
+	public float MissRateDefRatio;
+	public float ParryRateDefRatio;
+	public float SuckHPPerDefRatio;
+	public float NormalSkillPerRatio;
+	public float NormalSkillPerDefRatio;
+	public float AngerSkillPerRatio;
+	public float AngerSkillPerDefRatio;
+	public float SuperDamPerRatio;
+	public float SuperDamPerDefRatio;
+	public float ShieldPerRatio;
+	public float ShieldPerDefRatio;
+
+    public override int LoadKey(string _key)
+    {
+        int key = GetKey(_key);
+        return key;
+    }
+
+    public override void LoadConfig(string input)
+    {
+        try {
+        string[] tables = input.Split('\t');
+        int.TryParse(tables[0],out LV); 
+
+			float.TryParse(tables[1],out AtkRatio); 
+
+			float.TryParse(tables[2],out MaxHPRatio); 
+
+			float.TryParse(tables[3],out DefRatio); 
+
+			float.TryParse(tables[4],out StunRateRatio); 
+
+			float.TryParse(tables[5],out SuperHitRateRatio); 
+
+			float.TryParse(tables[6],out ComboRateRatio); 
+
+			float.TryParse(tables[7],out MissRateRatio); 
+
+			float.TryParse(tables[8],out ParryRateRatio); 
+
+			float.TryParse(tables[9],out SuckHPPerRatio); 
+
+			float.TryParse(tables[10],out StunRateDefRatio); 
+
+			float.TryParse(tables[11],out SuperHitRateDefRatio); 
+
+			float.TryParse(tables[12],out ComboRateDefRatio); 
+
+			float.TryParse(tables[13],out MissRateDefRatio); 
+
+			float.TryParse(tables[14],out ParryRateDefRatio); 
+
+			float.TryParse(tables[15],out SuckHPPerDefRatio); 
+
+			float.TryParse(tables[16],out NormalSkillPerRatio); 
+
+			float.TryParse(tables[17],out NormalSkillPerDefRatio); 
+
+			float.TryParse(tables[18],out AngerSkillPerRatio); 
+
+			float.TryParse(tables[19],out AngerSkillPerDefRatio); 
+
+			float.TryParse(tables[20],out SuperDamPerRatio); 
+
+			float.TryParse(tables[21],out SuperDamPerDefRatio); 
+
+			float.TryParse(tables[22],out ShieldPerRatio); 
+
+			float.TryParse(tables[23],out ShieldPerDefRatio); 
+        }
+        catch (Exception exception)
+        {
+            Debug.LogError(exception);
+        }
+    }
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Config/Configs/FightPowerRatioConfig.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Config/Configs/FightPowerRatioConfig.cs.meta
index e37f87e..f1becbd 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Config/Configs/FightPowerRatioConfig.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 600fc786340faaf44aed165f5642df6d
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/Config/Configs/HeroConfig.cs b/Main/Config/Configs/HeroConfig.cs
index 738f35d..4b8810f 100644
--- a/Main/Config/Configs/HeroConfig.cs
+++ b/Main/Config/Configs/HeroConfig.cs
@@ -1,6 +1,6 @@
 锘�//--------------------------------------------------------
 //    [Author]:           YYL
-//    [  Date ]:           Wednesday, August 6, 2025
+//    [  Date ]:           2025骞�8鏈�17鏃�
 //--------------------------------------------------------
 
 using System.Collections.Generic;
@@ -28,7 +28,7 @@
 	public int AtkInheritPer;
 	public int DefInheritPer;
 	public int HPInheritPer;
-	public string BatAttrDict;
+	public Dictionary<int, int> BatAttrDict;
 	public int[] FetterIDList;
 	public float UIScale;
 	public string Desc;
@@ -79,7 +79,7 @@
 
 			int.TryParse(tables[11],out HPInheritPer); 
 
-			BatAttrDict = tables[12];
+			BatAttrDict = ConfigParse.ParseIntDict(tables[12]); 
 
 			if (tables[13].Contains("["))
 			{
diff --git a/Main/Config/Configs/PlayerLVConfig.cs b/Main/Config/Configs/PlayerLVConfig.cs
index b864aa6..da8a466 100644
--- a/Main/Config/Configs/PlayerLVConfig.cs
+++ b/Main/Config/Configs/PlayerLVConfig.cs
@@ -1,6 +1,6 @@
 锘�//--------------------------------------------------------
 //    [Author]:           YYL
-//    [  Date ]:           2025骞�8鏈�5鏃�
+//    [  Date ]:           2025骞�8鏈�16鏃�
 //--------------------------------------------------------
 
 using System.Collections.Generic;
@@ -18,6 +18,9 @@
 
     public int LV;
 	public long EXP;
+	public int MaxHP;
+	public int Atk;
+	public int Def;
 
     public override int LoadKey(string _key)
     {
@@ -32,6 +35,12 @@
         int.TryParse(tables[0],out LV); 
 
 			long.TryParse(tables[1],out EXP); 
+
+			int.TryParse(tables[2],out MaxHP); 
+
+			int.TryParse(tables[3],out Atk); 
+
+			int.TryParse(tables[4],out Def); 
         }
         catch (Exception exception)
         {
diff --git a/Main/Config/PartialConfigs/HeroConfig.cs b/Main/Config/PartialConfigs/HeroConfig.cs
deleted file mode 100644
index 6727d54..0000000
--- a/Main/Config/PartialConfigs/HeroConfig.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Collections.Generic;
-using UnityEngine;
-using LitJson;
-
-public partial class HeroConfig : ConfigBase<int, HeroConfig>
-{
-    public Dictionary<HeroAttrType, int> inheritPropertiesDict = new Dictionary<HeroAttrType, int>();
-
-    protected override void OnConfigParseCompleted()
-    {
-        base.OnConfigParseCompleted();
-
-        inheritPropertiesDict.Clear();
-
-        inheritPropertiesDict.Add(HeroAttrType.attack, AtkInheritPer);
-        inheritPropertiesDict.Add(HeroAttrType.defense, DefInheritPer);
-        inheritPropertiesDict.Add(HeroAttrType.hp, HPInheritPer);
-
-
-        // json鏍煎紡
-        // {"灞炴�D":鍊�, ...}
-        // 灞炴�D瀵瑰簲灞炴�ф潯鐩〃鐨処D
-        // 鏈夊�肩殑閰嶅嵆鍙紝娌℃湁閰嶇疆鐨勫睘鎬ч粯璁�0
-        JsonData jsonData = JsonMapper.ToObject(BatAttrDict);
-
-        foreach (var attrId in jsonData.Keys)
-        {
-            if (int.TryParse(attrId.ToString(), out int attrTypeId))
-            {
-                HeroAttrType attrType = (HeroAttrType)attrTypeId;
-                if (jsonData[attrId] != null && int.TryParse(jsonData[attrId].ToString(), out int value))
-                {
-                    if (inheritPropertiesDict.ContainsKey(attrType))
-                    {
-                        Debug.LogError($"HeroTalentConfig: 灞炴�� {attrType} 宸茬粡瀛樺湪锛屾棤娉曢噸澶嶆坊鍔犮�傝妫�鏌ラ厤缃枃浠躲��");
-                    }
-                    else
-                    {
-                        inheritPropertiesDict.Add(attrType, value);
-                    }
-                }
-            }
-        }
-    }
-
-    public int GetInheritPercent(HeroAttrType attrType)
-    {
-        if (inheritPropertiesDict.TryGetValue(attrType, out int perc))
-        {
-            return perc;
-        }
-
-        return 0;
-    }
-}
\ No newline at end of file
diff --git a/Main/Config/PartialConfigs/HeroConfig.cs.meta b/Main/Config/PartialConfigs/HeroConfig.cs.meta
deleted file mode 100644
index c03e9a5..0000000
--- a/Main/Config/PartialConfigs/HeroConfig.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: dd269c7c73dcf3f4f87848b25f007405
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Main/Config/PartialConfigs/HeroTalentConfig.cs b/Main/Config/PartialConfigs/HeroTalentConfig.cs
index f568ac8..83e7bbd 100644
--- a/Main/Config/PartialConfigs/HeroTalentConfig.cs
+++ b/Main/Config/PartialConfigs/HeroTalentConfig.cs
@@ -3,10 +3,6 @@
 
 public partial class HeroTalentConfig : ConfigBase<int, HeroTalentConfig>
 {
-    // public int Quality;
-    // public int AttrID;
-
-    // Quality, List<HeroTalentConfig>
     public static Dictionary<int, List<HeroTalentConfig>> configDics = new Dictionary<int, List<HeroTalentConfig>>();
 
     protected override void OnConfigParseCompleted()
diff --git a/Main/Config/PartialConfigs/PlayerPropertyConfig.cs b/Main/Config/PartialConfigs/PlayerPropertyConfig.cs
index 82ce7a6..f7014ae 100644
--- a/Main/Config/PartialConfigs/PlayerPropertyConfig.cs
+++ b/Main/Config/PartialConfigs/PlayerPropertyConfig.cs
@@ -5,6 +5,11 @@
 
 public partial class PlayerPropertyConfig : ConfigBase<int, PlayerPropertyConfig>
 {
+    public const int baseType = 1;  //鍩虹灞炴��
+    public const int fightType = 2; //鎴樻枟灞炴��
+    public const int fightAntiType = 3; //鎴樻枟鎶楁��
+    public const int specialType = 4; //鐗规畩灞炴��
+
     // 鎸夋樉绀虹被鍨嬪垎
     private static Dictionary<int, List<int>> m_PlayerPropertyDict = new Dictionary<int, List<int>>();
     public static Dictionary<int, List<int>> playerPropertyDict
@@ -17,42 +22,21 @@
         }
     }
 
-    private static int[] m_inheritAttrs;//鏀婚槻琛� 缁ф壙鐨勭櫨鍒嗘瘮
-    public static int[] inheritAttrs
-    { 
-        get
-        {
-            if (m_inheritAttrs.IsNullOrEmpty())
-            {
-                if (playerPropertyDict.ContainsKey(5))
-                { 
-                    m_inheritAttrs = playerPropertyDict[5].ToArray();
-                }
-            }
-            return m_inheritAttrs;
-        }
-    }
+    public static int[] inheritAttrs = new int[] { 13, 14, 15 };
 
-    private static int[] m_basePerAttrs;    //鏀婚槻琛� 鍔犳垚鐧惧垎姣�
-    public static int[] basePerAttrs
-    { 
-        get
-        {
-            if (m_basePerAttrs.IsNullOrEmpty())
-            {
-                if (playerPropertyDict.ContainsKey(6))
-                { 
-                    m_basePerAttrs = playerPropertyDict[6].ToArray();
-                }
-            }
-            return m_basePerAttrs;
-        }
-    }
 
-    public const int baseType = 1;  //鍩虹灞炴��
-    public const int fightType = 2; //鎴樻枟灞炴��
-    public const int fightAntiType = 3; //鎴樻枟鎶楁��
-    public const int specialType = 4; //鐗规畩灞炴��
+    //涓嶅悓鐨勫姛鑳借〃璋冪敤瀵瑰簲鑷繁鐨勫姛鑳藉惈涔夊拰浣跨敤鏂瑰紡锛屽闃靛鍏夌幆閰嶇疆灏辩敤鍦ㄥ搴旇绠楀眰
+    public static int[] basePerAttrs = new int[] { 16, 17, 18 };
+
+    public static int[] baseAttrs = new int[] { 6, 7, 8 };
+
+    public static Dictionary<int, int> baseAttr2perDict = new Dictionary<int, int>()
+    { 
+        { 6,16 },
+        { 7,17 },
+        { 8,18 }
+    };
+
     private static Dictionary<int, List<int>> RefreshShowDict()
     {
         if (m_PlayerPropertyDict.IsNullOrEmpty())
diff --git a/Main/Core/GameEngine/Common/Equation.cs b/Main/Core/GameEngine/Common/Equation.cs
deleted file mode 100644
index 9fd64c8..0000000
--- a/Main/Core/GameEngine/Common/Equation.cs
+++ /dev/null
@@ -1,455 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.RegularExpressions;
-using UnityEngine;
-
- 
-/// <summary>
-/// 鍏紡璁$畻妯″潡锛屾敮鎸佸姩鎬佸彉閲忔浛鎹㈠拰澶氱杩愮畻绗﹁绠椼��
-/// </summary>
-public class Equation : Singleton<Equation>
-{
-    /// <summary>
-    /// 鏀寔鐨勮繍绠楃鍒楄〃銆�
-    /// </summary>
-    public static readonly List<char> operatorList = new List<char>() { '*', '-', '+', '/', '^', '!', '@', '%', ';', '#', '$', '~', '&' };
-
-    /// <summary>
-    /// 鐢ㄤ簬涓存椂瀛楃涓叉瀯寤虹殑鍏变韩瀹炰緥銆�
-    /// </summary>
-    public static StringBuilder textBuilder = new StringBuilder();
-
-    /// <summary>
-    /// 瀛樺偍鍙橀噺鍚嶅拰鍊肩殑閿�煎鍒楄〃銆�
-    /// </summary>
-    private List<KeyValuePair<string, string>> keyValueDic = new List<KeyValuePair<string, string>>();
-
-    /// <summary>
-    /// 鐢ㄤ簬鍖归厤鍙橀噺鍚嶇殑姝e垯琛ㄨ揪寮忋��
-    /// </summary>
-    public static readonly Regex replaecRegex = new Regex(@"[a-zA-Z]{2,50}");
-
-    public T Eval<T>(string equation) where T : struct
-    {
-        keyValueDic.Sort(Compare);
-        equation = GetEquation(equation);
-        T val = default(T);
-        try
-        {
-            EquationParse equationParse = new EquationParse(equation);
-            val = (T)Convert.ChangeType(equationParse.result, typeof(T));
-            equationParse = null;
-        }
-        catch (Exception e)
-        {
-            Debug.LogError(e.Message);
-        }
-        return val;
-    }
-
-    /// <summary>
-    /// 鏇挎崲鍏紡涓殑鍙橀噺鍚嶄负瀵瑰簲鐨勫�笺��
-    /// </summary>
-    /// <param name="equation">鍘熷鍏紡瀛楃涓层��</param>
-    /// <returns>鏇挎崲鍚庣殑鍏紡瀛楃涓层��</returns>
-    private string GetEquation(string equation)
-    {
-        try
-        {
-            MatchCollection matchArray = replaecRegex.Matches(equation);
-            textBuilder.Length = 0;
-            int length = 0;
-
-            if (matchArray != null)
-            {
-                foreach (Match match in matchArray)
-                {
-                    // 娣诲姞闈炲彉閲忛儴鍒�
-                    textBuilder.Append(equation, length, match.Index - length);
-                    length = match.Index + match.Length;
-
-                    // 鏌ユ壘鍙橀噺瀵瑰簲鐨勫��
-                    var foundPair = keyValueDic.Find(x => x.Key.Equals(match.Value));
-                    string replacement = foundPair.Equals(default(KeyValuePair<string, string>)) ? "0" : foundPair.Value;
-                    textBuilder.Append(replacement ?? "0");
-                }
-            }
-
-            // 娣诲姞鍓╀綑閮ㄥ垎
-            textBuilder.Append(equation, length, equation.Length - length);
-        }
-        catch (Exception e)
-        {
-            Debug.LogError($"鍏紡鍙橀噺鏇挎崲澶辫触: {e.Message}");
-        }
-
-        return textBuilder.ToString();
-    }
-
-    public void AddKeyValue(string key, object val)
-    {
-        int index = keyValueDic.FindIndex((x) => {
-            return x.Key == key;
-        });
-        if (index == -1)
-        {
-            KeyValuePair<string, string> keyValuePair = new KeyValuePair<string, string>(key, val.ToString());
-            keyValueDic.Add(keyValuePair);
-        }
-    }
-
-    public void Clear()
-    {
-        keyValueDic.Clear();
-    }
-
-    int Compare(KeyValuePair<string, string> x, KeyValuePair<string, string> y)
-    {
-        return -x.Key.Length.CompareTo(y.Key.Length);
-    }
-
-    public class EquationParse
-    {
-        public string equation { get; private set; }
-        public string result { get; private set; }
-        private StringBuilder equationBuilder = new StringBuilder();
-        private List<EquationParse> subEquations = new List<EquationParse>();
-        private List<OperatorType> operatorTypeList = new List<OperatorType>();
-        private List<string> values = new List<string>();
-        public EquationParse(string _equation)
-        {
-            this.equation = _equation;
-            this.result = string.Empty;
-            equationBuilder.Length = 0;
-            if (CheckSplit())
-            {
-                return;
-            }
-            Subsection();
-            GetResult();
-        }
-        private bool CheckSplit()
-        {
-            var _left = string.Empty;
-            var _right = string.Empty;
-            OperatorType _operatorType;
-            if (Split(equation, out _left, out _right, out _operatorType))
-            {
-                var _leftResult = new EquationParse(_left);
-                var _rightResult = new EquationParse(_right);
-                result = GetResult(_leftResult.result, _rightResult.result, _operatorType).ToString();
-                return true;
-            }
-            return false;
-        }
-        /// <summary>
-        /// 瑙f瀽鍏紡涓殑瀛愯〃杈惧紡锛堟嫭鍙峰唴鐨勯儴鍒嗭級銆�
-        /// </summary>
-        private void Subsection()
-        {
-            try
-            {
-                int bracketCnt = 0;
-                int startIndex = 0;
-                int length = 0;
-                int index = 0;
-
-                for (int i = 0; i < equation.Length; i++)
-                {
-                    if (equation[i] == '(')
-                    {
-                        i++;
-                        startIndex = i;
-
-                        // 鎻愬彇鎷彿鍐呯殑瀛愯〃杈惧紡
-                        while (i < equation.Length && (equation[i] != ')' || bracketCnt > 0))
-                        {
-                            if (equation[i] == '(') bracketCnt++;
-                            else if (equation[i] == ')') bracketCnt--;
-                            length++;
-                            i++;
-                        }
-
-                        // 澶勭悊瀛愯〃杈惧紡
-                        subEquations.Add(new EquationParse(equation.Substring(startIndex, length)));
-                        equationBuilder.Append($"={index}=");
-                        index++;
-                        length = 0;
-                        continue;
-                    }
-
-                    equationBuilder.Append(equation[i]);
-                }
-            }
-            catch (Exception e)
-            {
-                Debug.LogError($"瀛愯〃杈惧紡瑙f瀽澶辫触: {e.Message}");
-            }
-        }
-
-        private void AnalysisEquation()
-        {
-            try
-            {
-                textBuilder.Length = 0;
-                for (int i = 0; i < subEquations.Count; i++)
-                {
-                    equationBuilder.Replace(string.Format("={0}=", i), subEquations[i].result);
-                    subEquations[i] = null;
-                }
-                subEquations.Clear();
-                var _result = equationBuilder.ToString();
-                char _lastChar = default(char);
-                for (int i = 0; i < _result.Length; i++)
-                {
-                    if (i - 1 > 0 && _result[i - 1] == 'E')
-                    {//杩囨护瓒呭ぇ鏁板�煎悗鐨勭鍙�
-                        textBuilder.Append(_result[i]);
-                        continue;
-                    }
-                    int index = operatorList.IndexOf(_result[i]);
-                    if (index != -1 && _lastChar >= '0' && _lastChar <= '9')
-                    {
-                        values.Add(textBuilder.ToString());
-                        textBuilder.Length = 0;
-                        operatorTypeList.Add(GetOperatorType(_result[i]));
-                    }
-                    else
-                    {
-                        textBuilder.Append(_result[i]);
-                    }
-                    _lastChar = _result[i];
-                }
-                values.Add(textBuilder.ToString());
-            }
-            catch (Exception e)
-            {
-                Debug.Log(e.Message);
-            }
-        }
-
-        public void GetResult()
-        {
-            AnalysisEquation();
-            try
-            {
-                for (int i = 0; i < operatorTypeList.Count; i++)
-                {
-                    if (IsPriorityOperator(operatorTypeList[i]))
-                    {
-                        double _result = 0;
-                        if (IsUnaryOperator(operatorTypeList[i]))
-                        {
-                            _result = GetResult(values[i], string.Empty, operatorTypeList[i]);
-                        }
-                        else
-                        {
-                            _result = GetResult(values[i], values[i + 1], operatorTypeList[i]);
-                            values.RemoveAt(i);
-                        }
-                        values.RemoveAt(i);
-                        values.Insert(i, _result.ToString());
-                        operatorTypeList.RemoveAt(i);
-                        i--;
-                    }
-                }
-                while (values.Count > 1 && operatorTypeList.Count > 0)
-                {
-                    double _result = 0;
-                    if (IsUnaryOperator(operatorTypeList[0]))
-                    {
-                        _result = GetResult(values[0], string.Empty, operatorTypeList[0]);
-                    }
-                    else
-                    {
-                        _result = GetResult(values[0], values[1], operatorTypeList[0]);
-                        values.RemoveAt(0);
-                    }
-                    values.RemoveAt(0);
-                    values.Insert(0, _result.ToString());
-                    operatorTypeList.RemoveAt(0);
-                }
-                if (operatorTypeList.Count == 1 && IsUnaryOperator(operatorTypeList[0]))
-                {
-                    result = GetResult(values[0], string.Empty, operatorTypeList[0]).ToString();
-                }
-                else
-                {
-                    result = values[0];
-                }
-            }
-            catch (Exception e)
-            {
-                Debug.Log(e.Message);
-            }
-        }
-
-        /// <summary>
-        /// 鏍规嵁杩愮畻绗︾被鍨嬭绠楃粨鏋溿��
-        /// </summary>
-        /// <param name="leftValue">宸︽搷浣滄暟銆�</param>
-        /// <param name="rightValue">鍙虫搷浣滄暟銆�</param>
-        /// <param name="operatorType">杩愮畻绗︾被鍨嬨��</param>
-        /// <returns>璁$畻缁撴灉銆�</returns>
-        public static double GetResult(string leftValue, string rightValue, OperatorType operatorType)
-        {
-            if (!double.TryParse(leftValue, out double _leftValue) || !double.TryParse(rightValue, out double _rightValue))
-            {
-                Debug.LogError($"鏃犳硶灏嗗瓧绗︿覆杞崲涓烘暟鍊�: {leftValue} 鎴� {rightValue}");
-                return 0;
-            }
-
-            switch (operatorType)
-            {
-                case OperatorType.Plus:      return _leftValue + _rightValue;
-                case OperatorType.Subtract:  return _leftValue - _rightValue;
-                case OperatorType.Ride:      return _leftValue * _rightValue;
-                case OperatorType.Divide:    return _rightValue == 0 ? _leftValue : _leftValue / _rightValue;
-                case OperatorType.Pow:       return Math.Pow(_leftValue, _rightValue);
-                case OperatorType.Min:       return Math.Min(_leftValue, _rightValue);
-                case OperatorType.Max:       return Math.Max(_leftValue, _rightValue);
-                case OperatorType.Remain:    return _leftValue % _rightValue;
-                case OperatorType.Random:    return UnityEngine.Random.Range((float)_leftValue, (float)_rightValue);
-                case OperatorType.Floor:     return Math.Floor(_leftValue);
-                case OperatorType.Ceil:      return Math.Ceiling((float)_leftValue);
-                case OperatorType.RandomInt: return UnityEngine.Random.Range((int)_leftValue, (int)_rightValue);
-                case OperatorType.Sqrt:      return Math.Sqrt(_leftValue);
-                default:                     return 0;
-            }
-        }
-        public static OperatorType GetOperatorType(char _operator)
-        {
-            switch (_operator)
-            {
-                case '+':
-                    return OperatorType.Plus;
-                case '-':
-                    return OperatorType.Subtract;
-                case '*':
-                    return OperatorType.Ride;
-                case '/':
-                    return OperatorType.Divide;
-                case '^':
-                    return OperatorType.Pow;
-                case '!':
-                    return OperatorType.Min;
-                case '@':
-                    return OperatorType.Max;
-                case '%':
-                    return OperatorType.Remain;
-                case ';':
-                    return OperatorType.Random;
-                case '#':
-                    return OperatorType.Floor;
-                case '$':
-                    return OperatorType.Ceil;
-                case '~':
-                    return OperatorType.RandomInt;
-                case '&':
-                    return OperatorType.Sqrt;
-            }
-            return OperatorType.Plus;
-        }
-        public static bool IsPriorityOperator(OperatorType operatorType)
-        {
-            if (operatorType == OperatorType.Plus || operatorType == OperatorType.Subtract)
-            {
-                return false;
-            }
-            return true;
-        }
-        public static bool IsUnaryOperator(OperatorType operatorType)
-        {
-            if (operatorType == OperatorType.Floor || operatorType == OperatorType.Ceil
-                || operatorType == OperatorType.Sqrt)
-            {
-                return true;
-            }
-            return false;
-        }
-
-        private static bool Split(string _source, out string _left, out string _right, out OperatorType _type)
-        {
-            _left = _right = string.Empty;
-            _type = OperatorType.Plus;
-            try
-            {
-                var _index = 0;
-                for (int i = _source.Length - 1; i >= 0; i--)
-                {
-                    if (_source[i] == ')')
-                    {
-                        _index++;
-                        continue;
-                    }
-                    else if (_source[i] == '(')
-                    {
-                        _index--;
-                        continue;
-                    }
-                    if (IsSplitOperator(_source[i], out _type) && _index == 0)
-                    {
-                        _left = _source.Substring(0, i);
-                        if (i + 1 < _source.Length)
-                        {
-                            _right = _source.Substring(i + 1);
-                        }
-                        return true;
-                    }
-                }
-            }
-            catch (Exception e)
-            {
-                Debug.Log(e.Message);
-            }
-            return false;
-        }
-
-        public static bool IsSplitOperator(char _char, out OperatorType _type)
-        {
-            var _index = operatorList.FindIndex((x) =>
-            {
-                return x == _char;
-            });
-            _type = OperatorType.Plus;
-            if (_index == -1)
-            {
-                return false;
-            }
-            _type = GetOperatorType(_char);
-            switch (_type)
-            {
-                case OperatorType.Floor:
-                case OperatorType.Ceil:
-                case OperatorType.Min:
-                case OperatorType.Max:
-                case OperatorType.Remain:
-                case OperatorType.Pow:
-                case OperatorType.Random:
-                case OperatorType.RandomInt:
-                case OperatorType.Sqrt:
-                    return true;
-            }
-            return false;
-        }
-    }
-
-    public enum OperatorType
-    {
-        Plus,//+
-        Subtract,//-
-        Ride,//*
-        Divide,// /
-        Pow,// ^
-        Min,// !
-        Max,// @
-        Remain,// %
-        Random,
-        Floor,
-        Ceil,
-        RandomInt,
-        Sqrt
-    }
-}
diff --git a/Main/Core/GameEngine/Common/Equation.cs.meta b/Main/Core/GameEngine/Common/Equation.cs.meta
deleted file mode 100644
index 662170c..0000000
--- a/Main/Core/GameEngine/Common/Equation.cs.meta
+++ /dev/null
@@ -1,12 +0,0 @@
-fileFormatVersion: 2
-guid: 87f34f1073caa794c85c409b99178842
-timeCreated: 1513492313
-licenseType: Free
-MonoImporter:
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0403_tagPlayerLoginLoadOK.cs b/Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0403_tagPlayerLoginLoadOK.cs
index c006fc4..5a05687 100644
--- a/Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0403_tagPlayerLoginLoadOK.cs
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0403_tagPlayerLoginLoadOK.cs
@@ -7,7 +7,7 @@
     public static bool finishedLogin = false;
     public static event Action playerLoginOkEvent;
     //public static event Action mapInitOkEvent; 鏈嶅姟绔�氱煡鍦烘櫙鍒囨崲鎴愬姛
-    
+
 
     public override void Done(GameNetPackBasic vNetPack)
     {
@@ -31,6 +31,10 @@
             playerLoginOkEvent?.Invoke();
         }
         finishedLogin = true;
+        if (PlayerDatas.Instance.baseData.FightPoint == 0)
+        { 
+            BattleManager.Instance.MainFightRequest(1, 1);
+        }
         //if (mapInitOkEvent != null)
         //{
         //    mapInitOkEvent();
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB122_tagSCHeroInfo.cs b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB122_tagSCHeroInfo.cs
index ae72da7..aff48f1 100644
--- a/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB122_tagSCHeroInfo.cs
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB122_tagSCHeroInfo.cs
@@ -4,8 +4,10 @@
 // B1 22 姝﹀皢鍥鹃壌淇℃伅 #tagSCHeroInfo
 
 public class DTCB122_tagSCHeroInfo : DtcBasic {
-    public override void Done(GameNetPackBasic vNetPack) {
+    public override void Done(GameNetPackBasic vNetPack)
+    {
         base.Done(vNetPack);
         HB122_tagSCHeroInfo vNetData = vNetPack as HB122_tagSCHeroInfo;
+        HeroUIManager.Instance.UpdateHeroCollectInfo(vNetData);
     }
 }
diff --git a/Main/System/CustomizedGift/CustomizedGiftModel.cs b/Main/System/CustomizedGift/CustomizedGiftModel.cs
index 5f70623..e1e8096 100644
--- a/Main/System/CustomizedGift/CustomizedGiftModel.cs
+++ b/Main/System/CustomizedGift/CustomizedGiftModel.cs
@@ -14,7 +14,7 @@
 
     public const int activityType = (int)OpenServerActivityCenter.ActivityType.AT_Activity2;
     public const int activityID = (int)NewDayActivityID.CustomizedGiftWin;
-    public static Operation operaType = Operation.default35;
+    public static OperationType operaType = OperationType.default35;
 
     public int actNum; //瀵瑰簲鐣岄潰
     public event Action UpdateRechargeGiftActEvent;
@@ -56,7 +56,7 @@
         }
     }
 
-    private void OperationEndEvent(Operation type, int state)
+    private void OperationEndEvent(OperationType type, int state)
     {
         if (type == operaType && state == 0)
         {
@@ -68,7 +68,7 @@
         }
     }
 
-    private void OperationAdvanceEvent(Operation type)
+    private void OperationAdvanceEvent(OperationType type)
     {
         if (type == operaType)
         {
@@ -79,7 +79,7 @@
         }
     }
 
-    private void OperationStartEvent(Operation type, int state)
+    private void OperationStartEvent(OperationType type, int state)
     {
         if (type == operaType && state == 0)
         {
diff --git a/Main/System/Equip/EquipExchangeCell.cs b/Main/System/Equip/EquipExchangeCell.cs
index 62f9ce8..3c39c5e 100644
--- a/Main/System/Equip/EquipExchangeCell.cs
+++ b/Main/System/Equip/EquipExchangeCell.cs
@@ -97,11 +97,14 @@
 
             if (showFightPower < 0)
             {
-                fightPowerNum.text = UIHelper.AppendColor(TextColType.Red, $"-{UIHelper.ReplaceLargeNum(showFightPower)}", false);
+                fightPowerNum.text = UIHelper.AppendColor(TextColType.Red, $"-{UIHelper.ReplaceLargeNum(Math.Abs(showFightPower))}", false);
+                cmpResult = 2;
             }
             else
             {
+                cmpResult = showFightPower > 0 ? 1 : 0;
                 fightPowerNum.text = UIHelper.AppendColor(TextColType.Green, $"+{UIHelper.ReplaceLargeNum(showFightPower)}", false);
+
             }
         }
 
diff --git a/Main/System/Equip/EquipExchangeWin.cs b/Main/System/Equip/EquipExchangeWin.cs
index e39f807..fb3c1c2 100644
--- a/Main/System/Equip/EquipExchangeWin.cs
+++ b/Main/System/Equip/EquipExchangeWin.cs
@@ -17,11 +17,11 @@
 
     protected override void OnPreOpen()
     {
-
         // 閫氱煡涓绘垬鍦烘殏鍋�
         BattleManager.Instance.storyBattleField.IsPause = true;
         EquipModel.Instance.OnEquipOPResultAction += OnRefreshItem;
         Display();
+
         // if (EquipModel.Instance.newEquipIDToGuideID.ContainsKey(EquipModel.Instance.selectFloorEquip.itemId))
         // {
         //     // if (!NewBieCenter.Instance.IsGuideCompleted(EquipModel.Instance.newEquipIDToGuideID[EquipModel.Instance.selectFloorEquip.itemId]))
diff --git a/Main/System/Equip/EquipModel.cs b/Main/System/Equip/EquipModel.cs
index cb42268..5cf7211 100644
--- a/Main/System/Equip/EquipModel.cs
+++ b/Main/System/Equip/EquipModel.cs
@@ -8,8 +8,9 @@
 
 public class EquipModel : GameSystemManager<EquipModel>
 {
+    public const int TotleEquip = 12;  //瑁呭鏍忓ぇ灏�
     public bool waitEquipOPPack = false;
-    public event Action<bool, int > OnEquipOPResultAction;    //鏄惁鎹笂浜嗘柊瑁呭涓斿垎瑙d簡 瑁呭绱㈠紩
+    public event Action<bool, int> OnEquipOPResultAction;    //鏄惁鎹笂浜嗘柊瑁呭涓斿垎瑙d簡 瑁呭绱㈠紩
     public event Action<List<int>, RectTransform> OnItemDropEvent;
 
     //鐢ㄤ簬椋樺姩閫昏緫
@@ -79,7 +80,7 @@
     }
 
     void OnDropEvent(string guid, BattleDrops drops, Action action)
-    { 
+    {
         NotifyItemDrop(drops.dropItemPackIndex, drops.rectTransform);
         action?.Invoke();
     }
@@ -158,7 +159,7 @@
         else
         {
             if (!UIManager.Instance.IsOpened<EquipExchangeWin>())
-            { 
+            {
                 UIManager.Instance.OpenWindow<EquipExchangeWin>();
             }
         }
@@ -264,7 +265,7 @@
     {
         if (waitEquipOP.Count == 0)
             return null;
-        
+
         ItemModel item = PackManager.Instance.GetItemByIndex(PackType.DropItem, waitEquipOP.Dequeue());
         if (AutoFightModel.Instance.isAutoAttack)
         {
@@ -272,7 +273,7 @@
             return null;
 
         }
-        
+
         return item;
     }
 
@@ -344,9 +345,14 @@
     }
 
     public bool IsEquip(int itemID)
-    { 
+    {
         return ItemConfig.Get(itemID).EquipPlace != 0;
     }
+
+    public ItemModel GetEquip(int index)
+    { 
+        return PackManager.Instance.GetItemByIndex(PackType.Equip, index);
+    }
 }
 
 
diff --git a/Main/System/Hero/HeroAttrType.cs b/Main/System/Hero/HeroAttrType.cs
index 9e7e2d3..3a6cd67 100644
--- a/Main/System/Hero/HeroAttrType.cs
+++ b/Main/System/Hero/HeroAttrType.cs
@@ -2,12 +2,12 @@
 
 public enum HeroAttrType
 {
-	// 鐢熷懡
-    hp,
     // 鏀诲嚮鍔�
-    attack,
+    attack = 6,
     // 闃插尽鍔�
-    defense,
+    defense = 7,
+	// 鐢熷懡
+    hp = 8,
     //鐪╂檿姒傜巼
     stunRate,
     //鏆村嚮姒傜巼
diff --git a/Main/System/Hero/HeroInfo.Awake.cs b/Main/System/Hero/HeroInfo.Awake.cs
index 8be924d..0dd7b23 100644
--- a/Main/System/Hero/HeroInfo.Awake.cs
+++ b/Main/System/Hero/HeroInfo.Awake.cs
@@ -5,23 +5,10 @@
 {
 
     //  瑙夐啋閰嶇疆
-    public HeroAwakeConfig awakeConfig
-    { 
-        get
-        {
-            return HeroAwakeConfig.GetHeroAwakeConfig(heroId, awakeLevel);
-        }
-    }
+    public HeroAwakeConfig awakeConfig { get; private set; }
 
-    
     //  鍝佽川瑙夐啋閰嶇疆
-    public HeroQualityAwakeConfig qualityAwakeConfig
-    { 
-        get
-        {
-            return HeroQualityAwakeConfig.GetQualityAwakeConfig(Quality, awakeLevel);
-        }
-    }
+    public HeroQualityAwakeConfig qualityAwakeConfig { get; private set; }
 
     //  姝﹀皢瑙夐啋绛夌骇
     public int awakeLevel
@@ -34,9 +21,65 @@
         }
     }
 
-    protected int GetIFByInheritAwakePercent(HeroAttrType attrType)
-    {
-    	//	YYL TODO
-    	return 0;
-    }
+
+    Dictionary<int, int> awakeAttrs = new Dictionary<int, int>();   
+	//璁$畻瑙夐啋灞炴��
+	public void RefreshAwakeAttr()
+	{
+		awakeAttrs.Clear();
+
+		for (int i = 0; i < awakeLevel; i++)
+		{
+			var tmpAwakeConfig = HeroAwakeConfig.GetHeroAwakeConfig(heroId, awakeLevel);
+			if (tmpAwakeConfig == null)
+				continue;
+			for(int j = 0; j < tmpAwakeConfig.AttrIDList.Length; j++)
+			{
+				int id = tmpAwakeConfig.AttrIDList[j];
+				if (!breakAttrs.ContainsKey(id))
+				{
+					breakAttrs.Add(id, tmpAwakeConfig.AttrValueList[j]);
+				}
+				else
+				{
+					breakAttrs[id] += tmpAwakeConfig.AttrValueList[j];
+				}
+			}
+
+			if (tmpAwakeConfig.SkillID != 0)
+			{
+				var skillConfig = SkillConfig.Get(tmpAwakeConfig.SkillID);
+				if (allSkillTypeIDToID.ContainsKey(skillConfig.SkillTypeID))
+				{
+					var tmpSkillConfig = SkillConfig.Get(allSkillTypeIDToID[skillConfig.SkillTypeID]);
+					if (skillConfig.SkillID > tmpSkillConfig.SkillID)
+					{
+						//鍙栨渶澶ф妧鑳�
+						allSkillTypeIDToID[skillConfig.SkillTypeID] = tmpAwakeConfig.SkillID;
+					}
+				}
+				else
+				{
+					allSkillTypeIDToID[skillConfig.SkillTypeID] = tmpAwakeConfig.SkillID;
+				}
+			}
+		}
+	}
+
+	public int GetAwakeAttrValue(int attrType)
+	{
+		int value = 0;
+		awakeAttrs.TryGetValue(attrType, out value);
+		return value;
+	}
+
+	public int GetAwakeAttrPer(int attrType)
+	{ 
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrType))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrType];
+            return awakeAttrs.ContainsKey(pertype) ? awakeAttrs[pertype] : 0;
+        }
+        return 0;
+	}
 }
\ No newline at end of file
diff --git a/Main/System/Hero/HeroInfo.Break.cs b/Main/System/Hero/HeroInfo.Break.cs
index e91b69c..43158c8 100644
--- a/Main/System/Hero/HeroInfo.Break.cs
+++ b/Main/System/Hero/HeroInfo.Break.cs
@@ -1,19 +1,15 @@
 
 
+//姝﹀皢绐佺牬锛氭灏嗗崌绾у悗闇�瑕佺獊鐮存墠鑳界户缁崌绾э紝绐佺牬鍙幏寰楁綔鑳�
+using System.Collections.Generic;
 
 public partial class HeroInfo
 {
 	//  绐佺牬閰嶇疆
-    public HeroBreakConfig breakConfig
-	{
-		get
-		{
-			return HeroBreakConfig.GetHeroBreakConfig(heroId, breakLevel);
-		}
-	}
+	public HeroBreakConfig breakConfig { get; private set;}
 
 	//  鍝佽川绐佺牬閰嶇疆
-	public HeroQualityBreakConfig qualityBreakConfig;
+	public HeroQualityBreakConfig qualityBreakConfig { get; private set;}
 
 	//  姝﹀皢绐佺牬绛夌骇
 	public int breakLevel
@@ -26,17 +22,64 @@
 		}
 	}
 
-	protected int GetBreakCultivationPercent(HeroAttrType attrType)
+	Dictionary<int, int> breakAttrs = new Dictionary<int, int>();   //娼滆兘灞炴�d锛氭綔鑳藉��
+	//璁$畻娼滆兘灞炴��
+	public void RefreshBreakAttr()
 	{
-		//	YYL TODO
-		return 0;
-	}
-	
-	protected int GetIFByInheritBreakPercent(HeroAttrType attrType)
-	{
-		//	YYL TODO
-		return 0;
-	}
-	
+		breakAttrs.Clear();
+		for (int i = 0; i < breakLevel; i++)
+		{
+			var tmpBreakConfig = HeroBreakConfig.GetHeroBreakConfig(heroId, i);
+			if (tmpBreakConfig == null)
+				continue;
+			for(int j = 0; j < tmpBreakConfig.AttrIDList.Length; j++)
+			{
+				int id = tmpBreakConfig.AttrIDList[j];
+				if (!breakAttrs.ContainsKey(id))
+				{
+					breakAttrs.Add(id, tmpBreakConfig.AttrValueList[j]);
+				}
+				else
+				{
+					breakAttrs[id] += tmpBreakConfig.AttrValueList[j];
+				}
+			}
 
+			if (tmpBreakConfig.SkillID != 0)
+			{
+				var skillConfig = SkillConfig.Get(tmpBreakConfig.SkillID);
+				if (skillConfig == null) continue;
+				if (allSkillTypeIDToID.ContainsKey(skillConfig.SkillTypeID))
+				{
+					var tmpSkillConfig = SkillConfig.Get(allSkillTypeIDToID[skillConfig.SkillTypeID]);
+					if (skillConfig.SkillID > tmpSkillConfig.SkillID)
+					{
+						//鍙栨渶澶ф妧鑳�
+						allSkillTypeIDToID[skillConfig.SkillTypeID] = tmpBreakConfig.SkillID;
+					}
+				}
+				else
+				{
+					allSkillTypeIDToID[skillConfig.SkillTypeID] = tmpBreakConfig.SkillID;
+				}
+			}
+		}
+	}
+
+	public int GetBreakAttrValue(int attrType)
+	{
+		int value = 0;
+		breakAttrs.TryGetValue(attrType, out value);
+		return value;
+	}
+
+	public int GetBreakAttrPer(int attrType)
+	{ 
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrType))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrType];
+            return breakAttrs.ContainsKey(pertype) ? breakAttrs[pertype] : 0;
+        }
+        return 0;
+	}
 }
diff --git a/Main/System/Hero/HeroInfo.Equip.cs b/Main/System/Hero/HeroInfo.Equip.cs
deleted file mode 100644
index 223adc7..0000000
--- a/Main/System/Hero/HeroInfo.Equip.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-public partial class HeroInfo
-{
-	protected int GetEquipStableProperties(HeroAttrType attrType)
-	{
-		//	YYL TODO
-		return 0;
-	}
-
-}
diff --git a/Main/System/Hero/HeroInfo.Equip.cs.meta b/Main/System/Hero/HeroInfo.Equip.cs.meta
deleted file mode 100644
index 72b571d..0000000
--- a/Main/System/Hero/HeroInfo.Equip.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 27a10dfeaeacac3439ab8ecc85001796
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Main/System/Hero/HeroInfo.Fetter.cs b/Main/System/Hero/HeroInfo.Fetter.cs
index 9175f06..98f01cd 100644
--- a/Main/System/Hero/HeroInfo.Fetter.cs
+++ b/Main/System/Hero/HeroInfo.Fetter.cs
@@ -1,15 +1,56 @@
-
-
-
 using System.Collections.Generic;
 using System.Linq;
 
+//缇佺粖锛氫笂闃靛悗鐨勬灏嗙粍鍚堝彲婵�娲荤緛缁�
 public partial class HeroInfo
 {
-	//  缇佺粖閰嶇疆
-	public HeroFetterConfig fetterConfig;
+	Dictionary<int, int> fetterAttrs = new Dictionary<int, int>(); //缇佺粖灞炴�D锛氬睘鎬у��
 
 
+	//涓嶅悓闃靛缇佺粖灞炴�т笉鍚岋紝瀹炴椂璁$畻锛屼笌鍏朵粬缂撳瓨鐨勪笉鍚�
+	public void RefreshFetterAttrsWhenCalcPower(TeamType teamType)
+	{
+		fetterAttrs.Clear();
+		var list = GetActiveFetter(heroConfig, TeamManager.Instance.GetTeam(teamType));
+
+		foreach (var id in list)
+		{
+			var config = HeroFetterConfig.Get(id);
+			var attrIDs = config.AttrIDList;
+			var attrValues = config.AttrValueList;
+			for (int i = 0; i < attrIDs.Length; i++)
+			{
+				if (!fetterAttrs.ContainsKey(attrIDs[i]))
+				{
+					fetterAttrs.Add(attrIDs[i], attrValues[i]);
+				}
+				else
+				{
+					fetterAttrs[attrIDs[i]] += attrValues[i];
+				}
+			}
+		}
+	}
+
+
+	public int GetFetterAttrValue(int attrType)
+	{
+		int value = 0;
+		fetterAttrs.TryGetValue(attrType, out value);
+		return value;
+	}
+
+	public int GetFetterAttrPer(int attrType)
+	{ 
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrType))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrType];
+            return fetterAttrs.ContainsKey(pertype) ? fetterAttrs[pertype] : 0;
+        }
+        return 0;
+	}
+
+	
 	public List<int> GetActiveFetter(HeroConfig config, TeamBase teamBase)
 	{
 		List<int> list = new List<int>();
diff --git a/Main/System/Hero/HeroInfo.InheritPer.cs b/Main/System/Hero/HeroInfo.InheritPer.cs
new file mode 100644
index 0000000..bcd084e
--- /dev/null
+++ b/Main/System/Hero/HeroInfo.InheritPer.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.Linq;
+
+public partial class HeroInfo
+{
+	//缁ф壙鐧惧垎姣斿搴斾笁鍥达紝瀵瑰簲灞炴�ф潯鐩〃閲岀殑ID
+	Dictionary<int, int> inheritPer
+	{
+		get
+		{
+			return new Dictionary<int, int>() {
+				{ 6, heroConfig.AtkInheritPer },
+				{ 7, heroConfig.DefInheritPer },
+				{ 8, heroConfig.HPInheritPer },
+			};
+		}
+	}
+	
+	public int GetInheritAttrPer(int attrType)
+	{
+		return inheritPer.ContainsKey(attrType) ? inheritPer[attrType] : 0;
+	}
+	
+}
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/System/Hero/HeroInfo.InheritPer.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/System/Hero/HeroInfo.InheritPer.cs.meta
index e37f87e..ff98ed8 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/System/Hero/HeroInfo.InheritPer.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: 43bccdd0f6575254591fc49d33072614
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/System/Hero/HeroInfo.Level.cs b/Main/System/Hero/HeroInfo.Level.cs
deleted file mode 100644
index cf413f2..0000000
--- a/Main/System/Hero/HeroInfo.Level.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-public partial class HeroInfo
-{
-	//  姝﹀皢绛夌骇
-    public int heroLevel
-    {
-        get
-        {
-            if (itemHero == null)
-                return 0;
-            return itemHero.GetUseDataFirstValue(70);
-        }
-    }
-	
-	protected int GetLevelCultivationPercent(HeroAttrType attrType)
-	{
-		//	YYL TODO
-		return 0;
-	}
-
-}
diff --git a/Main/System/Hero/HeroInfo.Level.cs.meta b/Main/System/Hero/HeroInfo.Level.cs.meta
deleted file mode 100644
index 781b376..0000000
--- a/Main/System/Hero/HeroInfo.Level.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 2a7ad855fa3dd5249a06835e88181ddd
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Main/System/Hero/HeroInfo.Lineup.cs b/Main/System/Hero/HeroInfo.Lineup.cs
index eac9cd5..7c23aa8 100644
--- a/Main/System/Hero/HeroInfo.Lineup.cs
+++ b/Main/System/Hero/HeroInfo.Lineup.cs
@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using UnityEngine;
 
+//闃靛甯冮樀
 public partial class HeroInfo
 {
 	public Dictionary<TeamType, KeyValuePair<int, int>> GetTeamTypeShapeTypePositionDict()
@@ -29,7 +30,7 @@
 				teamTypeShapeTypePositionDict.Add((TeamType)teamType, shapeTypePosition);
 			}
 		}
-		
+
 		return teamTypeShapeTypePositionDict;
 	}
 }
diff --git a/Main/System/Hero/HeroInfo.Properties.cs b/Main/System/Hero/HeroInfo.Properties.cs
index 4b383dd..fc5bb2b 100644
--- a/Main/System/Hero/HeroInfo.Properties.cs
+++ b/Main/System/Hero/HeroInfo.Properties.cs
@@ -1,14 +1,12 @@
 
 
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
 public partial class HeroInfo
 {
-    // 鎴樻枟灞炴�х殑瑙﹀彂鍑犵巼璁$畻鏄�1瀵�1鐨勶紝鍙悓鏃惰Е鍙戝涓睘鎬т笖澶氭瑙﹀彂锛岃Е鍙戝嚑鐜囦笉浼氶殢鐫�鐩爣鏁伴噺澶氬皯鑰屾敼鍙橈紝鍙細鏍规嵁鍚屼竴鐩爣鐨勮Е鍙戞鏁板澶氳�岄檷浣庛��
-    // 鍗冲湪涓�鍥炲悎鍐咃紝鍚屼竴涓洰鏍囧弽鍑汇�佽繛鍑汇�佸嚮鏅曘�佹毚鍑汇�侀棯閬垮彲浠ュ悓鏃跺苟澶氭瑙﹀彂锛岃Е鍙戝嚑鐜囬殢鏈鸿Е鍙戞鏁扮殑澧炲姞鑰岄檷浣庯紝姣忎釜灞炴�у紑鍏紡鏉ワ紙鍙傛暟锛氳Е鍙戝嚑鐜囥�佹姷鎶楀嚑鐜囥�佸凡瑙﹀彂娆℃暟锛�
-    // 浼樺厛鍒ゆ柇鏄惁鍛戒腑锛屽繀涓悗鍚屾椂鍒ゆ柇姝ゆ鏀诲嚮鏄惁杩炲嚮銆佸嚮鏅曘�佹毚鍑荤敓鏁�
-    // 鍙嶅嚮鏃跺繀鍛戒腑鐩爣
-    // 姝﹀皢灞炴�ч渶鏃舵椂璁$畻锛屾牴鎹妧鑳姐�丅UFF銆佽澶囩瓑灞炴�ф潵婧愭敼鍙樿�屾敼鍙�
 
-    // 鍩虹灞炴��
     // 鐢熷懡
     public int hp = 0;
     // 鏀诲嚮鍔�
@@ -85,96 +83,75 @@
     public int shieldSkillReduce;
 
 
+
     //璁$畻涓汉/鑱屼笟/绉嶆棌鍏绘垚灞炴�у姞鎴�
     public void CalculateProperties()
     {
-        // 姝﹀皢鍗曚綋灞炴�э紝鏍规嵁缁ф壙姣斾緥锛屼粠鍏ㄤ綋灞炴�х户鎵垮緱鍒�
-        // 渚嬪锛氭灏嗘敾鍑诲姏=
-        // [锛堣澶囧熀纭�鍥哄畾鍊�+鍏跺畠妯″潡鐨勫浐瀹氬�硷級* 锛�1+鍒濆鍔犳垚%+姝﹀皢鍗囩骇鍔犳垚%+姝﹀皢绐佺牬鍔犳垚%+姝﹀皢鍚炲櫖鏄熺骇鍔犳垚%+鍥鹃壌鍔犳垚%锛塢
-        // *
-        // [ 缁ф壙姣斾緥*锛�1+缇佺粖鍔犳垚%+娼滆兘鍔犳垚%+澶╄祴鍔犳垚%+瑙夐啋鏁堟灉鍔犳垚%锛塢
-        hp = GetProperties(HeroAttrType.hp);
-        attack = GetProperties(HeroAttrType.attack);
-        defense = GetProperties(HeroAttrType.defense);
-        stunRate = GetProperties(HeroAttrType.stunRate);
-        critRate = GetProperties(HeroAttrType.critRate);
-        comboRate = GetProperties(HeroAttrType.comboRate);
-        blockRate = GetProperties(HeroAttrType.blockRate);
-        counterAttackRate = GetProperties(HeroAttrType.counterAttackRate);
-        recoverRate = GetProperties(HeroAttrType.recoverRate);
-        stunResist = GetProperties(HeroAttrType.stunResist);
-        critResist = GetProperties(HeroAttrType.critResist);
-        comboResist = GetProperties(HeroAttrType.comboResist);
-        blockResist = GetProperties(HeroAttrType.blockResist);
-        counterAttackResist = GetProperties(HeroAttrType.counterAttackResist);
-        recoverResist = GetProperties(HeroAttrType.recoverResist);
-    }
+        allSkillTypeIDToID.Clear();
+        var skill = SkillConfig.Get(heroConfig.AtkSkillID);
+        if (skill != null)
+        { 
+            allSkillTypeIDToID[skill.SkillTypeID] = heroConfig.AtkSkillID;
+        }
+        skill = SkillConfig.Get(heroConfig.AngerSkillID);
+        if (skill != null)
+        { 
+            allSkillTypeIDToID[skill.SkillTypeID] = heroConfig.AngerSkillID;
+        }
 
-    protected int GetProperties(HeroAttrType attrType)
-    {
-        return GetStableProperties(attrType)
-                    * GetCultivationPercent(attrType)
-                    * GetInheritRate(attrType)
-                    * GetTotalPercent(attrType);
+        RefreshTalentAttr();
+        RefreshBreakAttr();
+        RefreshAwakeAttr();
     }
 
 
-    //  鍥哄畾鍊煎睘鎬�
-    public int GetStableProperties(HeroAttrType attrType)
-    {
-        int stableValue = 0;
-        stableValue += GetEquipStableProperties(attrType);
-        return stableValue;
-    }
 
-    //  鍩瑰吇鐧惧垎姣�
-    public int GetCultivationPercent(HeroAttrType attrType)
+    long tmpFightPower = 0;
+    public long CalculatePower(bool forceRefresh = true)
     {
-        int cultivationPercent = 100;
-        cultivationPercent += GetQualityCultivationPercent(attrType);//鍒濆鍔犳垚鏍规嵁姝﹀皢鍝佽川鍐冲畾锛屼笉鍚屽搧璐ㄦ灏嗗垵濮嬪姞鎴愪笉鍚� HeroInfo.Quality.cs
-        cultivationPercent += GetLevelCultivationPercent(attrType);// 绛夌骇缁欑殑鐧惧垎姣� HeroInfo.Level.cs
-        cultivationPercent += GetBreakCultivationPercent(attrType);// 绐佺牬缁欑殑鐧惧垎姣� HeroInfo.Break.cs
-        cultivationPercent += GetStarCultivationPercent(attrType);// 鍚炲櫖鏄熺骇缁欑殑鐧惧垎姣� HeroInfo.Star.cs
-        return cultivationPercent;
-    }
-
-    //  琚户鎵挎瘮渚嬪奖鍝嶇殑鐧惧垎姣斿睘鎬�
-    public int GetTotalPercent(HeroAttrType attrType)
-    {
-        // 锛�1+缇佺粖鍔犳垚%+娼滆兘鍔犳垚%+澶╄祴鍔犳垚%+瑙夐啋鏁堟灉鍔犳垚%锛�
-
-        int IFByInheritPercent = 100;
-        // IFByInheritPercent += GetIFByInheritFetterPercent(attrType); //缇佺粖鍔犳垚 HeroInfo.Fetter
-        // IFByInheritPercent += GetIFByInheritBreakPercent(attrType);  //娼滆兘鍔犳垚 HeroInfo.Break
-        // IFByInheritPercent += GetIFByInheritTalentPercent(attrType); //澶╄祴鍔犳垚 HeroInfo.Talent
-        // IFByInheritPercent += GetIFByInheritAwakePercent(attrType); //瑙夐啋鍔犳垚 HeroInfo.Awake
-        return IFByInheritPercent;
-    }
-
-    public int CalculatePower()
-    {
-        //  鏆傝
-        return hp +
-                attack +
-                defense +
-                stunRate +
-                critRate +
-                comboRate +
-                blockRate +
-                counterAttackRate +
-                recoverRate +
-                stunResist +
-                critResist +
-                comboResist +
-                blockResist +
-                counterAttackResist +
-                recoverResist;
+        if (forceRefresh || tmpFightPower == 0)
+        { 
+            tmpFightPower = FightPowerManager.Instance.GetHeroFightPower(this);
+        }
+        return tmpFightPower;
     }
 
 
     //涓婇樀灞炴��:鏀婚槻琛�
     public int GetOnBattleAddPer()
-    { 
+    {
         return qualityConfig.InitAddPer + qualityConfig.LVAddPer * heroLevel + qualityConfig.BreakLVAddPer * breakLevel + qualityConfig.StarAddPer * heroStar;
     }
+
+    public int GetLineupLVAddPer()
+    {
+        return qualityConfig.LVAddPer * heroLevel;
+    }
+
+    public int GetLineupBreakLVAddPer()
+    {
+        return qualityConfig.BreakLVAddPer * breakLevel;
+    }
+
+    public int GetLineupStarAddPer()
+    {
+        return qualityConfig.StarAddPer * heroStar;
+    }
+
+    //棰濆閰嶇疆鐨勭櫨鍒嗙櫨姣斿姞鎴� 涓夊洿瀵瑰簲鐨� 鐧惧垎姣斿姞鎴�
+    public int GetSelfAddPer(int attrType)
+    {
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrType))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrType];
+            return heroConfig.BatAttrDict.ContainsKey(pertype) ? heroConfig.BatAttrDict[pertype] : 0;
+        }
+        return 0;
+    }
+
+    public int GetSelfAddValue(int attrType)
+    {
+        return heroConfig.BatAttrDict.ContainsKey(attrType) ? heroConfig.BatAttrDict[attrType] : 0;
+    }
+    
 }
\ No newline at end of file
diff --git a/Main/System/Hero/HeroInfo.Quality.cs b/Main/System/Hero/HeroInfo.Quality.cs
deleted file mode 100644
index f320710..0000000
--- a/Main/System/Hero/HeroInfo.Quality.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-public partial class HeroInfo
-{
-    //  鍝佽川閰嶇疆
-    public HeroQualityConfig qualityConfig;
-	
-	public int Quality
-	{
-		get
-		{
-			return heroConfig.Quality;
-		}
-	}
-
-	protected int GetQualityCultivationPercent(HeroAttrType attrType)
-	{
-		//	YYL TODO
-		return 0;
-	}
-
-}
diff --git a/Main/System/Hero/HeroInfo.Star.cs b/Main/System/Hero/HeroInfo.Star.cs
deleted file mode 100644
index e966eec..0000000
--- a/Main/System/Hero/HeroInfo.Star.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-public partial class HeroInfo
-{
-	//  姝﹀皢鏄熺骇
-    public int heroStar
-    {
-        get
-        {
-            if (itemHero == null)
-                return 0;
-            return itemHero.GetUseDataFirstValue(72);
-        }
-    }
-
-
-	protected int GetStarCultivationPercent(HeroAttrType attrType)
-	{
-		//	YYL TODO
-		return 0;
-	}
-
-}
diff --git a/Main/System/Hero/HeroInfo.Star.cs.meta b/Main/System/Hero/HeroInfo.Star.cs.meta
deleted file mode 100644
index d113b22..0000000
--- a/Main/System/Hero/HeroInfo.Star.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 01382d3bab4b6674ba541f0704dd7037
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Main/System/Hero/HeroInfo.Talent.cs b/Main/System/Hero/HeroInfo.Talent.cs
index f66f6b7..4e5108d 100644
--- a/Main/System/Hero/HeroInfo.Talent.cs
+++ b/Main/System/Hero/HeroInfo.Talent.cs
@@ -1,12 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
 
-
-
+//姝﹀皢澶╄祴锛氬崌鏄熻幏寰楀ぉ璧嬶紝鍙礂缁�
 public partial class HeroInfo
 {
-	protected int GetIFByInheritTalentPercent(HeroAttrType attrType)
+
+	public int heroStar
+    {
+        get
+        {
+            if (itemHero == null)
+                return 0;
+            return itemHero.GetUseDataFirstValue(72);
+        }
+    }
+	
+	public List<HeroTalentInfo> talentList = new List<HeroTalentInfo>();
+
+
+	Dictionary<int, int> talentAttrDic = new Dictionary<int, int>();   //灞炴�D : 澶╄祴灞炴�у��
+																	   // 71 # 鑻遍泟澶╄祴ID鍒楄〃
+																	   // 73 # 鑻遍泟澶╄祴ID绛夌骇鍒楄〃锛屽搴�71澶╄祴ID鐨勭瓑绾�
+																	   // 75 # 鑻遍泟澶╄祴娲楃偧閿佸畾绱㈠紩鍒楄〃锛屽搴�71澶╄祴ID绱㈠紩
+																	   // 77 # 鑻遍泟澶╄祴娲楃偧闅忔満ID鍒楄〃
+																	   // 79 # 鑻遍泟瑙夐啋鏃堕殢鏈哄ぉ璧嬮�夐」ID鍒楄〃
+	protected void RefreshTalentAttr()
 	{
-		//	YYL TODO
-		return 0;
+		// 71 # 鑻遍泟澶╄祴ID鍒楄〃
+		List<int> talentIDList = itemHero.GetUseData(71);
+		// // 73 # 鑻遍泟澶╄祴ID绛夌骇鍒楄〃锛屽搴�71澶╄祴ID鐨勭瓑绾�
+		List<int> talentLvList = itemHero.GetUseData(73);
+		talentAttrDic.Clear();
+
+		for (int i = 0; i < talentIDList.Count; i++)
+		{
+			if (talentIDList[i] == 0)
+			{
+				continue;
+			}
+			var config = HeroTalentConfig.Get(talentIDList[i]);
+			if (!talentAttrDic.ContainsKey(config.AttrID))
+			{
+				//澶╄祴灞炴�э紝鍊�*澶╄祴绛夌骇
+				talentAttrDic[config.AttrID] = config.AttrValue * talentLvList[i];
+			}
+			else
+			{ 
+				talentAttrDic[config.AttrID] += config.AttrValue * talentLvList[i];
+			}
+		}
+
+	}
+
+	public int GetTalentAttrValue(int attrType)
+	{
+		int value = 0;
+		talentAttrDic.TryGetValue(attrType, out value);
+		return value;
+	}
+
+	public int GetTalentAttrPer(int attrType)
+	{ 
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrType))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrType];
+            return talentAttrDic.ContainsKey(pertype) ? talentAttrDic[pertype] : 0;
+        }
+        return 0;
 	}
 
 }
diff --git a/Main/System/Hero/HeroInfo.cs b/Main/System/Hero/HeroInfo.cs
index 2080d00..2d0ae87 100644
--- a/Main/System/Hero/HeroInfo.cs
+++ b/Main/System/Hero/HeroInfo.cs
@@ -9,7 +9,7 @@
     public int heroId;
 
     //  姝﹀皢閰嶇疆
-    public HeroConfig heroConfig;
+    public HeroConfig heroConfig { get; private set; }
     public ItemModel itemHero;  //寮曠敤鑳屽寘閲岀殑 鏁版嵁鍚屾
 
     public HeroCountry heroCountry
@@ -20,7 +20,27 @@
         }
     }
 
+    //  鍝佽川閰嶇疆
+    public HeroQualityConfig qualityConfig { get; private set; }
 
+    public int Quality
+    {
+        get
+        {
+            return heroConfig.Quality;
+        }
+    }
+
+    //  姝﹀皢绛夌骇
+    public int heroLevel
+    {
+        get
+        {
+            if (itemHero == null)
+                return 0;
+            return itemHero.GetUseDataFirstValue(70);
+        }
+    }
     // 浼樺厛鍔熻兘鎻愰啋绫诲瀷锛�1瑙夐啋 2鍗囩骇 3绐佺牬 4鍗囨槦
     // 浼樺厛椤哄簭锛�
     public int funcState
@@ -40,8 +60,8 @@
     }
 
 
-
-    public List<HeroTalentInfo> talentList = new List<HeroTalentInfo>();
+    //宸插涔犵殑鎵�鏈夋妧鑳藉彇鏈�楂樼骇鐨勬妧鑳�
+    Dictionary<int, int> allSkillTypeIDToID = new Dictionary<int, int>();  //鎶�鑳界被鍨婭D锛�  鏈�楂樻妧鑳絀D
 
 
     public HeroInfo(ItemModel _itemHero)
@@ -49,17 +69,7 @@
         UpdateHero(_itemHero);
     }
 
-#if UNITY_EDITOR
-    public HeroInfo()
-    {
-        heroId = 520001; // 榛樿鑻遍泟ID
 
-        heroConfig = HeroConfig.Get(heroId);
-        qualityConfig = HeroQualityConfig.Get(Quality);
-        qualityBreakConfig = HeroQualityBreakConfig.GetQualityBreakConfig(Quality, awakeLevel);
-        CalculateProperties();
-    }
-#endif
 
 
     public void UpdateHero(ItemModel _itemHero)
@@ -68,28 +78,9 @@
         // HeroConfigUtility
         heroId = itemHero.config.ID;
 
-
         InitConfigs();
-        // 71 # 鑻遍泟澶╄祴ID鍒楄〃
-        // List<int> talentSkillList = itemHero.GetUseData(71);
-        // // 73 # 鑻遍泟澶╄祴ID绛夌骇鍒楄〃锛屽搴�71澶╄祴ID鐨勭瓑绾�
-        // List<int> talentLvList = itemHero.GetUseData(73);
-        // // 75 # 鑻遍泟澶╄祴娲楃偧閿佸畾绱㈠紩鍒楄〃锛屽搴�71澶╄祴ID绱㈠紩
-        // List<int> talentLockList = itemHero.GetUseData(75);
 
-
-        // if (talentLockList.Count != talentLvList.Count || talentLvList.Count != talentSkillList.Count)
-        // {
-        //     Debug.LogError("澶╄祴ID鍒楄〃鍙婂悗缁殑鏁版嵁鏁伴噺娌″涓�");
-        // }
-
-        // //  澶╄祴
-        // talentList.Clear();
-        // for (int i = 0; i < talentSkillList.Count; i++)
-        // {
-        //     talentList.Add(new HeroTalentInfo(this, talentSkillList[i], talentLvList[i], talentLockList[i]));
-        // }
-
+        CalculateProperties();
         // //  缇佺粖
         // fetterInfoList.Clear();
         // for (int i = 0; i < heroConfig.FetterIDList.Length; i++)
@@ -97,8 +88,6 @@
         //     fetterInfoList.Add(new HeroFetterInfo(this, heroConfig.FetterIDList[i]));
         // }
 
-        // 77 # 鑻遍泟澶╄祴娲楃偧闅忔満ID鍒楄〃
-        // 79 # 鑻遍泟瑙夐啋鏃堕殢鏈哄ぉ璧嬮�夐」ID鍒楄〃
         // 80 # 涓婚樀鍨嬩笂闃典綅缃�
     }
 
@@ -106,33 +95,34 @@
     {
         //  姝﹀皢閰嶇疆
         heroConfig = HeroConfig.Get(heroId);
-
-
         //  鍝佽川閰嶇疆
         qualityConfig = HeroQualityConfig.Get(Quality);
 
         //  鍝佽川绐佺牬閰嶇疆
         qualityBreakConfig = HeroQualityBreakConfig.GetQualityBreakConfig(Quality, breakLevel);
 
+        breakConfig = HeroBreakConfig.GetHeroBreakConfig(heroId, breakLevel);
+        awakeConfig = HeroAwakeConfig.GetHeroAwakeConfig(heroId, awakeLevel);
+        qualityAwakeConfig = HeroQualityAwakeConfig.GetQualityAwakeConfig(Quality, awakeLevel);
+
     }
 
-    public int GetInheritRate(HeroAttrType attrType)
-    {
-        return heroConfig.GetInheritPercent(attrType);
-    }
-    
-    //鏄惁涓妜闃� 81 # 鎵�鍦ㄩ樀瀹逛俊鎭垪琛� [闃靛绫诲瀷*10000+闃靛瀷绫诲瀷*100+浣嶇疆缂栧彿, ...]
+
+
+    //鏄惁涓妜闃� 鏈嶅姟绔槦浼�
     public bool IsInTeamByTeamType(TeamType teamType)
     {
-        var list = itemHero.GetUseData(81);
-        if (list != null && list.Count > 0)
-        {
-            var index = list.FindIndex((item) => item / 10000 == (int)teamType);
-            if (index >= 0)
-            {
-                return true;
-            }
-        }
-        return false;
+        return TeamManager.Instance.GetTeam(teamType).HasHeroInServer(itemHero.guid);
     }
+
+    public long GetSkillsFightPower()
+    {
+        long fightPower = 0;
+        foreach (var skillID in allSkillTypeIDToID.Values)
+        {
+            fightPower += SkillConfig.Get(skillID).FightPower;
+        }
+        return fightPower;
+    }
+
 }
\ No newline at end of file
diff --git a/Main/System/Hero/HeroManager.cs b/Main/System/Hero/HeroManager.cs
index b7259a0..668ba42 100644
--- a/Main/System/Hero/HeroManager.cs
+++ b/Main/System/Hero/HeroManager.cs
@@ -124,8 +124,8 @@
 
         heroList.Sort((a, b) =>
         {
-            int power1 = a.CalculatePower();
-            int power2 = b.CalculatePower();
+            long power1 = a.CalculatePower(false);
+            long power2 = b.CalculatePower(false);
 
             if (power1 == power2)
             {
diff --git a/Main/System/HeroUI/HeroFormationCell.cs b/Main/System/HeroUI/HeroFormationCell.cs
index 8290d71..d12a38c 100644
--- a/Main/System/HeroUI/HeroFormationCell.cs
+++ b/Main/System/HeroUI/HeroFormationCell.cs
@@ -12,7 +12,7 @@
 
     public void Display(int index)
     {
-        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType);
+        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType, true);
 
         var config = HeroLineupHaloConfig.GetConfig(result.x, result.y);
         bool sameCountry = result.x == (index + 1);
diff --git a/Main/System/HeroUI/HeroFormationWin.cs b/Main/System/HeroUI/HeroFormationWin.cs
index 4c8e657..a2c0369 100644
--- a/Main/System/HeroUI/HeroFormationWin.cs
+++ b/Main/System/HeroUI/HeroFormationWin.cs
@@ -24,7 +24,7 @@
         scroller.OnRefreshCell += OnRefreshCell;
         CreateScroller();
 
-        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType);
+        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType, true);
 
         var config = HeroLineupHaloConfig.GetConfig(result.x, result.y);
         if (config == null)
diff --git a/Main/System/HeroUI/HeroLVBreakWin.cs b/Main/System/HeroUI/HeroLVBreakWin.cs
index e2603a3..015731a 100644
--- a/Main/System/HeroUI/HeroLVBreakWin.cs
+++ b/Main/System/HeroUI/HeroLVBreakWin.cs
@@ -80,7 +80,7 @@
         }
         if (nextQualityBreakConfig.SkillID != 0)
         {
-            attrStrArr.Add(SkillConfig.Get(nextQualityBreakConfig.SkillID).Description);
+            attrStrArr.Add(SkillConfig.Get(nextQualityBreakConfig.SkillID)?.Description);
         }
         potentialText.text = Language.Get("L1100", Language.Get("herocard56"), string.Join(Language.Get("L1112"), attrStrArr));
     }
diff --git a/Main/System/HeroUI/HeroPosWin.cs b/Main/System/HeroUI/HeroPosWin.cs
index 5689a2c..75bc0db 100644
--- a/Main/System/HeroUI/HeroPosWin.cs
+++ b/Main/System/HeroUI/HeroPosWin.cs
@@ -48,7 +48,7 @@
 
     private bool m_IsToggleOn = false;
     private bool isToggleOn
-    { 
+    {
         get { return m_IsToggleOn; }
         set
         {
@@ -114,6 +114,7 @@
         HeroUIManager.Instance.SortHeroOnTeamList();
         heroListScroller.OnRefreshCell += OnRefreshCell;
         HeroUIManager.Instance.OnTeamPosChangeEvent += TeamChangeEvent;
+        TeamManager.Instance.OnTeamChange += OnTeamChange;
         CreateScroller();
         Refresh();
     }
@@ -123,6 +124,7 @@
         CancelCurrentTask();
         heroListScroller.OnRefreshCell -= OnRefreshCell;
         HeroUIManager.Instance.OnTeamPosChangeEvent -= TeamChangeEvent;
+        TeamManager.Instance.OnTeamChange -= OnTeamChange;
         TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType).RestoreTeam();
     }
 
@@ -143,9 +145,9 @@
         fiterManager.Display(0, HeroUIManager.Instance.selectTeamPosJob, HeroUIManager.Instance.selectTeamPosCountry, SelectJobCountry);
 
 
-        fightPowerText.text = "1234k";
+        fightPowerText.text = UIHelper.ReplaceLargeArtNum(FightPowerManager.Instance.GetTeamFightPower(HeroUIManager.Instance.selectTeamType, true));
 
-        
+
     }
 
     void RefreshFlyHead()
@@ -176,7 +178,7 @@
     }
 
     void RefreshEmptyTip()
-    { 
+    {
         if (HeroUIManager.Instance.heroOnTeamSortList.Count <= 0)
         {
             heroListEmpty.SetActive(true);
@@ -237,7 +239,7 @@
     //涓婇樀姝﹀皢鍥藉鍏夌幆婵�娲�
     void RefreshOnTeamCountry(bool playEffect = false)
     {
-        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType);
+        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType, true);
 
         var config = HeroLineupHaloConfig.GetConfig(result.x, result.y);
         if (config == null)
@@ -380,10 +382,12 @@
             sequence.onComplete = () => { flyHead.transform.localScale = Vector3.zero; };
 
             if (isToggleOn)
-            { 
+            {
                 ShowFetter(flyHero);
             }
         }
+        fightPowerText.text = UIHelper.ReplaceLargeArtNum(FightPowerManager.Instance.GetTeamFightPower(HeroUIManager.Instance.selectTeamType, true));
+
     }
 
     void ShowFetter(HeroInfo hero)
@@ -392,7 +396,7 @@
         var fetterList = hero.GetActiveFetter(heroConfig, TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType));
         for (int i = 0; i < fetterList.Count; i++)
         {
-            if(!showConnectTipQueue.Contains(fetterList[i]))
+            if (!showConnectTipQueue.Contains(fetterList[i]))
                 showConnectTipQueue.Enqueue(fetterList[i]);
         }
 
@@ -472,12 +476,20 @@
         {
             return;
         }
-        
         HeroUIManager.Instance.selectTeamPosJob = 0;
         HeroUIManager.Instance.selectTeamPosCountry = 0;
-        HeroUIManager.Instance.SortHeroOnTeamList();
         HeroUIManager.Instance.selectTeamType = type;
+        HeroUIManager.Instance.SortHeroOnTeamList();
         Refresh();
         heroListScroller.m_Scorller.RefreshActiveCellViews();
     }
+    
+    protected void OnTeamChange(TeamType teamType)
+    {
+        if (HeroUIManager.Instance.selectTeamType == teamType)
+        {
+            HeroUIManager.Instance.SortHeroOnTeamList();
+            heroListScroller.m_Scorller.RefreshActiveCellViews();
+        }
+    }
 }
\ No newline at end of file
diff --git a/Main/System/HeroUI/HeroTrainWin.cs b/Main/System/HeroUI/HeroTrainWin.cs
index 492fd0e..513bb57 100644
--- a/Main/System/HeroUI/HeroTrainWin.cs
+++ b/Main/System/HeroUI/HeroTrainWin.cs
@@ -140,7 +140,7 @@
         jobImg.SetSprite(HeroUIManager.Instance.GetJobIconName(hero.heroConfig.Class));
         jobPosNameText.text = HeroUIManager.Instance.GetJobName(hero.heroConfig.Class);
         descText.text = hero.heroConfig.Desc;
-        fightPowerText.text = hero.CalculatePower().ToString();
+        fightPowerText.text = UIHelper.ReplaceLargeArtNum(hero.CalculatePower());
         lockImg.SetActive(hero.isLock);
         unLockImg.SetActive(!hero.isLock);
         nameText.text = hero.breakLevel == 0 ? hero.heroConfig.Name : Language.Get("herocardbreaklv", hero.heroConfig.Name, hero.breakLevel);
@@ -451,6 +451,8 @@
         for (int i = 0; i < list.Count; i++)
         {
             var nextQualityBreakConfig = HeroBreakConfig.GetHeroBreakConfig(hero.heroId, i + 1);
+            if (nextQualityBreakConfig == null)
+                break;
             List<string> attrStrArr = new List<string>();
             for (int j = 0; j < nextQualityBreakConfig.AttrIDList.Length; j++)
             {
@@ -459,7 +461,7 @@
             }
             if (nextQualityBreakConfig.SkillID != 0)
             {
-                attrStrArr.Add(SkillConfig.Get(nextQualityBreakConfig.SkillID).Description);
+                attrStrArr.Add(SkillConfig.Get(nextQualityBreakConfig.SkillID)?.Description);
             }
             if (i < hero.breakLevel)
             {
diff --git a/Main/System/HeroUI/HeroUIManager.Collect.cs b/Main/System/HeroUI/HeroUIManager.Collect.cs
new file mode 100644
index 0000000..92a1705
--- /dev/null
+++ b/Main/System/HeroUI/HeroUIManager.Collect.cs
@@ -0,0 +1,46 @@
+锘縰sing System;
+using System.Collections;
+using System.Collections.Generic;
+
+using UnityEngine;
+
+public partial class HeroUIManager : GameSystemManager<HeroUIManager>
+{
+
+    #region 鍥鹃壌鍜岀毊鑲�
+
+    //鍥鹃壌鍜岀毊鑲ょ殑婵�娲绘儏鍐�
+    public Dictionary<int, HB122_tagSCHeroInfo.tagSCHero> heroCollectInfoDic { get; private set; } = new Dictionary<int, HB122_tagSCHeroInfo.tagSCHero>();
+
+    public int bookPer;
+    public event Action OnHeroCollectEvent;
+
+    public void UpdateHeroCollectInfo(HB122_tagSCHeroInfo netPack)
+    {
+        for (int i = 0; i < netPack.HeroCnt; i++)
+        {
+            heroCollectInfoDic[(int)netPack.HeroInfoList[i].HeroID] = netPack.HeroInfoList[i];
+        }
+        bookPer = GetHeroCollectBookPer();
+        OnHeroCollectEvent?.Invoke();
+    }
+
+
+    public int GetHeroCollectBookPer()
+    {
+        int per = 0;
+        foreach (var kv in heroCollectInfoDic)
+        {
+            var config = HeroQualityConfig.Get(HeroConfig.Get(kv.Key).Quality);
+            if (kv.Value.BookInitState != 2)
+                continue;
+            per += config.BookInitAddPer;
+            per += kv.Value.BookStarLV * config.BookStarAddPer;
+            per += kv.Value.BookBreakLV * config.BookBreakLVAddPer;
+        }
+        return per;
+    }
+
+    #endregion
+}
+
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/System/HeroUI/HeroUIManager.Collect.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/System/HeroUI/HeroUIManager.Collect.cs.meta
index e37f87e..cc3dae1 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/System/HeroUI/HeroUIManager.Collect.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: fde2bfe0f6468dc49951c562fd445d51
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/System/HeroUI/HeroUIManager.OnTeam.cs b/Main/System/HeroUI/HeroUIManager.OnTeam.cs
new file mode 100644
index 0000000..b3f4167
--- /dev/null
+++ b/Main/System/HeroUI/HeroUIManager.OnTeam.cs
@@ -0,0 +1,311 @@
+锘縰sing System;
+using System.Collections;
+using System.Collections.Generic;
+
+using UnityEngine;
+
+public partial class HeroUIManager : GameSystemManager<HeroUIManager>
+{
+    
+
+    #region 甯冮樀鐣岄潰
+    public List<string> heroOnTeamSortList { get; private set; } = new List<string>();    //涓嶅悓涓婇樀鐨勫垪琛ㄦ帓搴�
+
+    private TeamType m_SelectTeamType = TeamType.Story; //褰撳墠閫変腑鐨勬槸鍝釜闃靛, 甯冮樀鐩稿叧閫昏緫浣跨敤
+    public TeamType selectTeamType
+    {
+        get { return m_SelectTeamType; }
+        set
+        {
+            if (m_SelectTeamType == value)
+                return;
+            //涓婁竴涓樀瀹归渶瑕佹仮澶嶅埌鍘熺姸鎬�
+            if (m_SelectTeamType != TeamType.None)
+            {
+                TeamManager.Instance.GetTeam(m_SelectTeamType).RestoreTeam();
+            }
+
+            m_SelectTeamType = value;
+        }
+    }
+
+    public int selectTeamPosJob = 0;    //甯冮樀鐣岄潰 绛涢�夎亴涓�
+    public int selectTeamPosCountry = 0;    //甯冮樀鐣岄潰 绛涢�夊浗瀹�
+
+    public const float clickFlyPosTime = 0.5f;  //鐐瑰嚮鍒楄〃涓殑姝﹀皢鍥炬爣鏃�, 椋炲叆甯冮樀鐨勬椂闂�
+
+    public event Action<List<int>, int, Vector3> OnTeamPosChangeEvent; //甯冮樀鍙樺寲 浣嶇疆锛屽垪琛ㄧ殑璧烽绱㈠紩锛岃捣椋炲潗鏍�
+
+
+    // 銆愰樀瀹瑰睘鎬с�� - 璇ラ樀瀹规墍鏈夋灏嗘湁鏁�
+    // 鍒濆鍔犳垚    lineupInitAddPer
+    // 鍗囩骇鍔犳垚    lineupLVAddPer
+    // 绐佺牬鍔犳垚    lineupBreakLVAddPer
+    // 鍚炲櫖鍔犳垚    lineupStarAddPer
+    // 闃靛鍏夌幆    lineupHaloValue 銆� lineupHaloPer
+
+    /// <summary>
+    /// 鎸夐槦浼嶆眹鎬讳笂闃靛睘鎬�
+    /// </summary>
+    /// <param name="type"></param>
+    /// <param name="isPreview">true 瀹㈡埛绔瑙堥樀瀹癸紝榛樿false 鏈嶅姟鍣ㄩ樀瀹�</param>
+    /// <returns></returns>
+    public Dictionary<string, int> GetLineupPer(TeamType type, bool isPreview = false)
+    {
+        Dictionary<string, int> lineUPPer = new Dictionary<string, int>()
+        {
+            {"lineupInitAddPer", 0},
+            {"lineupLVAddPer", 0},
+            {"lineupBreakLVAddPer", 0},
+            {"lineupStarAddPer", 0},
+        };
+
+        var team = TeamManager.Instance.GetTeam(type);
+        if (team == null)
+        {
+            return lineUPPer;
+        }
+        TeamHero[] teamHeroes = isPreview ? team.tempHeroes : team.serverHeroes;
+
+        foreach (var teamHero in teamHeroes)
+        {
+            if (teamHero == null)
+            {
+                continue;
+            }
+            var config = HeroQualityConfig.Get(HeroConfig.Get(teamHero.heroId).Quality);
+            lineUPPer["lineupInitAddPer"] += config.InitAddPer;
+
+            HeroInfo hero = HeroManager.Instance.GetHero(teamHero.guid);
+            if (hero == null) continue;
+            lineUPPer["lineupLVAddPer"] += hero.GetLineupLVAddPer();
+            lineUPPer["lineupBreakLVAddPer"] += hero.GetLineupBreakLVAddPer();
+            lineUPPer["lineupStarAddPer"] += hero.GetLineupStarAddPer();
+
+        }
+        return lineUPPer;
+    }
+
+    /// <summary>
+    /// 鎸夐槦浼嶈幏寰楅樀鍨嬶紙鍥藉鍏夌幆锛夊睘鎬�
+    /// </summary>
+    /// <param name="teamType"></param>
+    /// <param name="isPreview">true 瀹㈡埛绔瑙堥樀瀹癸紝榛樿false 鏈嶅姟鍣ㄩ樀瀹�</param>
+    /// <returns></returns>
+    public Dictionary<int, int> GetCountryAttrs(TeamType teamType, bool isPreview = false)
+    {
+        Dictionary<int, int> countryAttrs = new Dictionary<int, int>();
+        Int2 result = GetMaxCountHeroCountry(teamType, isPreview);
+        var lineupconfig = HeroLineupHaloConfig.GetConfig(result.x, result.y);
+        if (lineupconfig != null)
+        {
+            for (int i = 0; i < lineupconfig.AttrIDList.Length; i++)
+            {
+                countryAttrs[lineupconfig.AttrIDList[i]] = lineupconfig.AttrValueList[i];
+            }
+        }
+
+        return countryAttrs;
+    }
+
+    public void SortHeroOnTeamList()
+    {
+        heroOnTeamSortList = HeroManager.Instance.GetHeroGuidList(selectTeamPosJob, selectTeamPosCountry);
+        heroOnTeamSortList.Sort(CmpHeroByTeamType);
+    }
+
+
+    int CmpHeroByTeamType(string guidA, string guidB)
+    {
+        HeroInfo heroA = HeroManager.Instance.GetHero(guidA);
+        HeroInfo heroB = HeroManager.Instance.GetHero(guidB);
+        if (heroA == null || heroB == null)
+        {
+            return 0;
+        }
+
+        var team = TeamManager.Instance.GetTeam(selectTeamType);
+        // 鎺掑簭瑙勫垯锛氫笂闃�>姝﹀皢绛夌骇锛炵獊鐮寸瓑绾э紴姝﹀皢瑙夐啋闃剁骇锛炴灏嗗搧璐紴姝﹀皢鍚炲櫖鏄熺骇锛炴灏咺D
+        bool isInTeamA = team.HasHeroInServer(guidA);
+        bool isInTeamB = team.HasHeroInServer(guidB);
+        if (isInTeamA != isInTeamB)
+        {
+            return isInTeamA ? -1 : 1;
+        }
+        if (heroA.heroLevel != heroB.heroLevel)
+        {
+            return heroA.heroLevel > heroB.heroLevel ? -1 : 1;
+        }
+        if (heroA.breakLevel != heroB.breakLevel)
+        {
+            return heroA.breakLevel > heroB.breakLevel ? -1 : 1;
+        }
+        if (heroA.awakeLevel != heroB.awakeLevel)
+        {
+            return heroA.awakeLevel > heroB.awakeLevel ? -1 : 1;
+        }
+        if (heroA.Quality != heroB.Quality)
+        {
+            return heroA.Quality > heroB.Quality ? -1 : 1;
+        }
+        if (heroA.heroStar != heroA.heroStar)
+        {
+            return heroA.heroStar > heroB.heroStar ? -1 : 1;
+        }
+
+        return heroA.heroId.CompareTo(heroB.heroId);
+    }
+
+
+
+
+    /// <summary>
+    /// 涓婇樀闃熶紞涓悇涓浗瀹剁殑姝﹀皢鏁伴噺
+    /// </summary>
+    /// <param name="teamType"></param>
+    /// <param name="isPreview">true 瀹㈡埛绔瑙堥樀瀹癸紝榛樿false 鏈嶅姟鍣ㄩ樀瀹�</param>
+    /// <returns></returns>
+    public Dictionary<HeroCountry, int> GetCountryHeroCountByTeamType(TeamType teamType, bool isPreview = false)
+    {
+        Dictionary<HeroCountry, int> heroCountryCount = new Dictionary<HeroCountry, int>();
+
+        var team = TeamManager.Instance.GetTeam(teamType);
+        if (team == null)
+        { 
+            return heroCountryCount;
+        }
+        TeamHero[] teamHeroes = isPreview ? team.tempHeroes : team.serverHeroes;
+
+        for (int i = 0; i < teamHeroes.Length; i++)
+        {
+            if (teamHeroes[i] == null)
+                continue;
+            var country = teamHeroes[i].Country;
+
+            if (!heroCountryCount.ContainsKey(country))
+            {
+                heroCountryCount.Add(country, 1);
+            }
+            else
+            {
+                heroCountryCount[country] += 1;
+            }
+
+        }
+
+
+        return heroCountryCount;
+    }
+
+    /// <summary>
+    /// 鑾峰緱涓婇樀涓灏嗘暟閲忔渶澶х殑鍥藉鍜屾暟閲�
+    /// </summary>
+    /// <param name="teamType"></param>
+    /// <param name="isPreview">true 瀹㈡埛绔瑙堥樀瀹癸紝榛樿false 鏈嶅姟鍣ㄩ樀瀹�</param>
+    /// <returns></returns>
+    public Int2 GetMaxCountHeroCountry(TeamType teamType, bool isPreview = false)
+    {
+        var countryCountDict = GetCountryHeroCountByTeamType(teamType, isPreview);
+        //鎵惧埌鏈�澶х殑鍥藉鍜屾暟閲�
+        HeroCountry country = HeroCountry.None;
+        int maxValue = 0;
+        foreach (var data in countryCountDict)
+        {
+            if (data.Value > maxValue)
+            {
+                country = data.Key;
+                maxValue = data.Value;
+            }
+        }
+        return new Int2((int)country, maxValue);
+    }
+
+    //鍦ㄤ笉鍚岄〉绛句笅閫堿ttackType 0 鏀诲嚮闃靛 1 闃插畧闃靛
+    public int GetSelectTeamTypeByAttackType(int AttackType)
+    {
+        if (selectTeamType == TeamType.Arena || selectTeamType == TeamType.ArenaDefense)
+        {
+            return AttackType == 0 ? (int)TeamType.Arena : (int)TeamType.ArenaDefense;
+        }
+
+
+        return (int)TeamType.Story;
+    }
+
+
+    public void NotifyOnTeamPosChangeEvent(List<int> posList, int flyIndex, Vector3 startPos)
+    {
+        OnTeamPosChangeEvent?.Invoke(posList, flyIndex, startPos);
+    }
+
+    //鎺ㄨ崘闃靛
+    public List<string> SelectRecommend()
+    {
+        //鎺ㄨ崘闃靛鐨勭畻娉曢�昏緫
+        //鑷姩閫夋嫨浼樺厛绾э細姝﹀皢绛夌骇锛炵獊鐮寸瓑绾э紴姝﹀皢瑙夐啋闃剁骇锛炴灏嗗搧璐紴姝﹀皢鍚炲櫖鏄熺骇锛炴灏咺D
+        var tmpList = HeroManager.Instance.GetHeroGuidList();
+        tmpList.Sort(CmpHeroRecommend);
+
+
+        //鎺ㄨ崘鏈�澶�6涓紝瀛樺湪鐩稿悓heroid锛屽垯璺宠繃
+        List<string> selectHeroList = new List<string>();
+        List<int> selectHeroIDList = new List<int>();
+        for (int i = 0; i < tmpList.Count; i++)
+        {
+            if (selectHeroList.Count >= TeamConst.MaxTeamHeroCount)
+                break;
+
+            string guid = tmpList[i];
+            HeroInfo heroInfo = HeroManager.Instance.GetHero(guid);
+            if (selectHeroIDList.Contains(heroInfo.heroId))
+                continue;
+            //濡傛灉閲嶅浜�,璺宠繃
+            if (selectHeroList.Contains(guid))
+                continue;
+            selectHeroList.Add(guid);
+            selectHeroIDList.Add(heroInfo.heroId);
+        }
+        return selectHeroList;
+    }
+
+
+    int CmpHeroRecommend(string guidA, string guidB)
+    {
+        HeroInfo heroA = HeroManager.Instance.GetHero(guidA);
+        HeroInfo heroB = HeroManager.Instance.GetHero(guidB);
+        if (heroA == null || heroB == null)
+        {
+            return 0;
+        }
+
+        // 鎺掑簭瑙勫垯锛氭灏嗙瓑绾э紴绐佺牬绛夌骇锛炴灏嗚閱掗樁绾э紴姝﹀皢鍝佽川锛炴灏嗗悶鍣槦绾э紴姝﹀皢ID
+        if (heroA.heroLevel != heroB.heroLevel)
+        {
+            return heroA.heroLevel > heroB.heroLevel ? -1 : 1;
+        }
+        if (heroA.breakLevel != heroB.breakLevel)
+        {
+            return heroA.breakLevel > heroB.breakLevel ? -1 : 1;
+        }
+        if (heroA.awakeLevel != heroB.awakeLevel)
+        {
+            return heroA.awakeLevel > heroB.awakeLevel ? -1 : 1;
+        }
+        if (heroA.Quality != heroB.Quality)
+        {
+            return heroA.Quality > heroB.Quality ? -1 : 1;
+        }
+        if (heroA.heroStar != heroA.heroStar)
+        {
+            return heroA.heroStar > heroB.heroStar ? -1 : 1;
+        }
+
+        return heroA.heroId.CompareTo(heroB.heroId);
+    }
+
+
+    #endregion
+
+    
+}
+
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta
index e37f87e..4e8ae78 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: c8e0c0a46a38f1e49856253add1794e3
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/System/HeroUI/HeroUIManager.Reborn.cs b/Main/System/HeroUI/HeroUIManager.Reborn.cs
new file mode 100644
index 0000000..2ad8871
--- /dev/null
+++ b/Main/System/HeroUI/HeroUIManager.Reborn.cs
@@ -0,0 +1,83 @@
+锘縰sing System;
+using System.Collections;
+using System.Collections.Generic;
+
+using UnityEngine;
+
+public partial class HeroUIManager : GameSystemManager<HeroUIManager>
+{
+    
+
+    #region 閲嶇敓 閬f暎
+    public int awakeRebirthCnt { get; private set; }
+    public int payBackMoneyType;
+    public int payBackMoney;
+    public Dictionary<int, ulong> GetHeroLVPayBack(int quality, int lv)
+    {
+        //姹囨�昏繑杩樻�绘暟閲�
+        Dictionary<int, ulong> itemCounDic = new Dictionary<int, ulong>();
+        for (int i = 1; i < lv; i++)
+        {
+            var config = HeroQualityLVConfig.GetQualityLVConfig(quality, lv);
+            var itemID = config.UPCostItem[0];
+            var count = (ulong)config.UPCostItem[1];
+            if (!itemCounDic.ContainsKey(itemID))
+            {
+                itemCounDic[itemID] = count;
+            }
+            itemCounDic[itemID] = itemCounDic[itemID] + count;
+        }
+
+        return itemCounDic;
+    }
+
+
+    public Dictionary<int, ulong> GetHeroBreakPayBack(int quality, int lv)
+    {
+        //姹囨�昏繑杩樻�绘暟閲�
+        Dictionary<int, ulong> itemCounDic = new Dictionary<int, ulong>();
+        for (int i = 0; i < lv; i++)
+        {
+            var config = HeroQualityBreakConfig.GetQualityBreakConfig(quality, lv);
+            var itemID = config.UPCostItem[0];
+            var count = (ulong)config.UPCostItem[1];
+            if (!itemCounDic.ContainsKey(itemID))
+            {
+                itemCounDic[itemID] = count;
+            }
+            itemCounDic[itemID] = itemCounDic[itemID] + count;
+        }
+
+        return itemCounDic;
+
+
+    }
+
+    public Dictionary<int, ulong> GetHeroQualityAwakePayBack(int quality, int lv)
+    {
+        //姹囨�昏繑杩樻�绘暟閲�
+        Dictionary<int, ulong> itemCounDic = new Dictionary<int, ulong>();
+        for (int i = 0; i < lv; i++)
+        {
+            var config = HeroQualityAwakeConfig.GetQualityAwakeConfig(quality, lv);
+            var itemID = config.UPCostItem[0];
+            var count = (ulong)config.UPCostItem[1];
+            if (!itemCounDic.ContainsKey(itemID))
+            {
+                itemCounDic[itemID] = count;
+            }
+            itemCounDic[itemID] = itemCounDic[itemID] + count;
+        }
+
+        return itemCounDic;
+    }
+
+    public void UpdateHeroInfo(HB125_tagSCPlayerHeroInfo netPack)
+    {
+        awakeRebirthCnt = netPack.AwakeRebirthCnt;
+    }
+
+    #endregion
+    
+}
+
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/System/HeroUI/HeroUIManager.Reborn.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/System/HeroUI/HeroUIManager.Reborn.cs.meta
index e37f87e..9d5e3ee 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/System/HeroUI/HeroUIManager.Reborn.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: bf68f7332b968f14491059586ac32df5
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2
diff --git a/Main/System/HeroUI/HeroUIManager.cs b/Main/System/HeroUI/HeroUIManager.cs
index 5b6b3d6..8480413 100644
--- a/Main/System/HeroUI/HeroUIManager.cs
+++ b/Main/System/HeroUI/HeroUIManager.cs
@@ -5,7 +5,7 @@
 using UnityEngine;
 
 //姝﹀皢鐩稿叧鐣岄潰鐨勬搷浣滄暟鎹鐞�
-public class HeroUIManager : GameSystemManager<HeroUIManager>
+public partial class HeroUIManager : GameSystemManager<HeroUIManager>
 {
     #region 姝﹀皢鍒楄〃鐣岄潰
     public List<string> heroSortList { get; private set; } = new List<string>();  //涓婇樀涓轰富绾跨殑 GUID鍒楄〃 
@@ -14,7 +14,7 @@
     public string selectHeroGuid; //閫変腑鐨勬灏唅d
     #endregion
 
-    public WaitHeroFuncResponse waitResponse;    //璇锋眰姝﹀皢鍔熻兘
+    public WaitHeroFuncResponse waitResponse;    //璇锋眰姝﹀皢鍔熻兘锛屼笌鏈嶅姟绔氦浜�
 
 
     public override void Init()
@@ -43,6 +43,7 @@
         heroOnTeamSortList.Clear();
         awakeRebirthCnt = 0;
         waitResponse = default;
+        heroCollectInfoDic.Clear();
     }
 
     private void OnHeroChangeEvent(HeroInfo hero)
@@ -67,7 +68,7 @@
         }
 
         if (hero.itemHero.guid != waitResponse.guid)
-        { 
+        {
             return;
         }
 
@@ -77,7 +78,7 @@
         }
 
         waitResponse = default;
-        
+
     }
 
     #region 姝﹀皢UI甯哥敤鎺ュ彛
@@ -119,7 +120,7 @@
     }
 
     public bool IsLVMaxByBreakLevel(HeroInfo hero)
-    { 
+    {
         return hero.heroLevel == GetMaxLVByBreakLV(hero.Quality, hero.breakLevel);
     }
 
@@ -201,289 +202,11 @@
         return heroA.heroId.CompareTo(heroB.heroId);
     }
 
-    #region 甯冮樀鐣岄潰
-    public List<string> heroOnTeamSortList { get; private set; } = new List<string>();    //涓嶅悓涓婇樀鐨勫垪琛ㄦ帓搴�
 
-    private TeamType m_SelectTeamType = TeamType.Story; //褰撳墠閫変腑鐨勬槸鍝釜闃靛, 甯冮樀鐩稿叧閫昏緫浣跨敤
-    public TeamType selectTeamType
-    {
-        get { return m_SelectTeamType; }
-        set
-        {
-            if (m_SelectTeamType == value)
-                return;
-            //涓婁竴涓樀瀹归渶瑕佹仮澶嶅埌鍘熺姸鎬�
-            if (m_SelectTeamType != TeamType.None)
-            {
-                TeamManager.Instance.GetTeam(m_SelectTeamType).RestoreTeam();
-            }
-
-            m_SelectTeamType = value;
-        }
-    }
-
-    public int selectTeamPosJob = 0;    //甯冮樀鐣岄潰 绛涢�夎亴涓�
-    public int selectTeamPosCountry = 0;    //甯冮樀鐣岄潰 绛涢�夊浗瀹�
-
-    public const float clickFlyPosTime = 0.5f;  //鐐瑰嚮鍒楄〃涓殑姝﹀皢鍥炬爣鏃�, 椋炲叆甯冮樀鐨勬椂闂�
-
-    public event Action<List<int>, int, Vector3> OnTeamPosChangeEvent; //甯冮樀鍙樺寲 浣嶇疆锛屽垪琛ㄧ殑璧烽绱㈠紩锛岃捣椋炲潗鏍�
-
-
-    public void SortHeroOnTeamList()
-    {
-        heroOnTeamSortList = HeroManager.Instance.GetHeroGuidList(selectTeamPosJob, selectTeamPosCountry);
-        heroOnTeamSortList.Sort(CmpHeroByTeamType);
-    }
-
-
-    int CmpHeroByTeamType(string guidA, string guidB)
-    {
-        HeroInfo heroA = HeroManager.Instance.GetHero(guidA);
-        HeroInfo heroB = HeroManager.Instance.GetHero(guidB);
-        if (heroA == null || heroB == null)
-        {
-            return 0;
-        }
-
-        // 鎺掑簭瑙勫垯锛氫笂闃�>姝﹀皢绛夌骇锛炵獊鐮寸瓑绾э紴姝﹀皢瑙夐啋闃剁骇锛炴灏嗗搧璐紴姝﹀皢鍚炲櫖鏄熺骇锛炴灏咺D
-        bool isInTeamA = heroA.IsInTeamByTeamType(selectTeamType);
-        bool isInTeamB = heroB.IsInTeamByTeamType(selectTeamType);
-        if (isInTeamA != isInTeamB)
-        {
-            return isInTeamA ? -1 : 1;
-        }
-        if (heroA.heroLevel != heroB.heroLevel)
-        {
-            return heroA.heroLevel > heroB.heroLevel ? -1 : 1;
-        }
-        if (heroA.breakLevel != heroB.breakLevel)
-        {
-            return heroA.breakLevel > heroB.breakLevel ? -1 : 1;
-        }
-        if (heroA.awakeLevel != heroB.awakeLevel)
-        {
-            return heroA.awakeLevel > heroB.awakeLevel ? -1 : 1;
-        }
-        if (heroA.Quality != heroB.Quality)
-        {
-            return heroA.Quality > heroB.Quality ? -1 : 1;
-        }
-        if (heroA.heroStar != heroA.heroStar)
-        {
-            return heroA.heroStar > heroB.heroStar ? -1 : 1;
-        }
-
-        return heroA.heroId.CompareTo(heroB.heroId);
-    }
-
-
-
-
-    //涓婇樀闃熶紞涓悇涓浗瀹剁殑姝﹀皢鏁伴噺
-    public Dictionary<HeroCountry, int> GetCountryHeroCountByTeamType(TeamType teamType)
-    {
-        Dictionary<HeroCountry, int> heroCountryCount = new Dictionary<HeroCountry, int>();
-
-        var team = TeamManager.Instance.GetTeam(teamType);
-        if (team != null)
-        {
-            for (int i = 0; i < team.tempHeroes.Length; i++)
-            {
-                if (team.tempHeroes[i] == null)
-                    continue;
-                var country = (HeroCountry)team.tempHeroes[i].Country;
-
-                if (!heroCountryCount.ContainsKey(country))
-                {
-                    heroCountryCount.Add(country, 1);
-                }
-                else
-                {
-                    heroCountryCount[country] = heroCountryCount[country] + 1;
-                }
-
-            }
-
-        }
-
-        return heroCountryCount;
-    }
-
-    //鑾峰緱涓婇樀涓灏嗘暟閲忔渶澶х殑鍥藉鍜屾暟閲�
-    public Int2 GetMaxCountHeroCountry(TeamType teamType)
-    {
-        var countryCount = GetCountryHeroCountByTeamType(teamType);
-        //鎵惧埌鏈�澶х殑鍥藉鍜屾暟閲�
-        HeroCountry country = HeroCountry.None;
-        int maxValue = 0;
-        foreach (var data in countryCount)
-        {
-            if (data.Value > maxValue)
-            {
-                country = data.Key;
-                maxValue = data.Value;
-            }
-        }
-        return new Int2((int)country, maxValue);
-    }
-
-    //鍦ㄤ笉鍚岄〉绛句笅閫堿ttackType 0 鏀诲嚮闃靛 1 闃插畧闃靛
-    public int GetSelectTeamTypeByAttackType(int AttackType)
-    {
-        if (selectTeamType == TeamType.Arena || selectTeamType == TeamType.ArenaDefense)
-        {
-            return AttackType == 0 ? (int)TeamType.Arena : (int)TeamType.ArenaDefense;
-        }
-
-
-        return (int)TeamType.Story;
-    }
-
-
-    public void NotifyOnTeamPosChangeEvent(List<int> posList, int flyIndex, Vector3 startPos)
-    {
-        OnTeamPosChangeEvent?.Invoke(posList, flyIndex, startPos);
-    }
-
-    //鎺ㄨ崘闃靛
-    public List<string> SelectRecommend()
-    {
-        //鎺ㄨ崘闃靛鐨勭畻娉曢�昏緫
-        //鑷姩閫夋嫨浼樺厛绾э細姝﹀皢绛夌骇锛炵獊鐮寸瓑绾э紴姝﹀皢瑙夐啋闃剁骇锛炴灏嗗搧璐紴姝﹀皢鍚炲櫖鏄熺骇锛炴灏咺D
-        var tmpList = HeroManager.Instance.GetHeroGuidList();
-        tmpList.Sort(CmpHeroRecommend);
-
-
-        //鎺ㄨ崘鏈�澶�6涓紝瀛樺湪鐩稿悓heroid锛屽垯璺宠繃
-        List<string> selectHeroList = new List<string>();
-        List<int> selectHeroIDList = new List<int>();
-        for (int i = 0; i < tmpList.Count; i++)
-        {
-            if (selectHeroList.Count >= TeamConst.MaxTeamHeroCount)
-                break;
-
-            string guid = tmpList[i];
-            HeroInfo heroInfo = HeroManager.Instance.GetHero(guid);
-            if (selectHeroIDList.Contains(heroInfo.heroId))
-                continue;
-            //濡傛灉閲嶅浜�,璺宠繃
-            if (selectHeroList.Contains(guid))
-                continue;
-            selectHeroList.Add(guid);
-            selectHeroIDList.Add(heroInfo.heroId);
-        }
-        return selectHeroList;
-    }
-
-
-    int CmpHeroRecommend(string guidA, string guidB)
-    {
-        HeroInfo heroA = HeroManager.Instance.GetHero(guidA);
-        HeroInfo heroB = HeroManager.Instance.GetHero(guidB);
-        if (heroA == null || heroB == null)
-        {
-            return 0;
-        }
-
-        // 鎺掑簭瑙勫垯锛氭灏嗙瓑绾э紴绐佺牬绛夌骇锛炴灏嗚閱掗樁绾э紴姝﹀皢鍝佽川锛炴灏嗗悶鍣槦绾э紴姝﹀皢ID
-        if (heroA.heroLevel != heroB.heroLevel)
-        {
-            return heroA.heroLevel > heroB.heroLevel ? -1 : 1;
-        }
-        if (heroA.breakLevel != heroB.breakLevel)
-        {
-            return heroA.breakLevel > heroB.breakLevel ? -1 : 1;
-        }
-        if (heroA.awakeLevel != heroB.awakeLevel)
-        {
-            return heroA.awakeLevel > heroB.awakeLevel ? -1 : 1;
-        }
-        if (heroA.Quality != heroB.Quality)
-        {
-            return heroA.Quality > heroB.Quality ? -1 : 1;
-        }
-        if (heroA.heroStar != heroA.heroStar)
-        {
-            return heroA.heroStar > heroB.heroStar ? -1 : 1;
-        }
-
-        return heroA.heroId.CompareTo(heroB.heroId);
-    }
-
-
-    #endregion
-
-    #region 閲嶇敓 閬f暎
-    public int awakeRebirthCnt { get; private set; }
-    public int payBackMoneyType;
-    public int payBackMoney;
-    public Dictionary<int, ulong> GetHeroLVPayBack(int quality, int lv)
-    {
-        //姹囨�昏繑杩樻�绘暟閲�
-        Dictionary<int, ulong> itemCounDic = new Dictionary<int, ulong>();
-        for (int i = 1; i < lv; i++)
-        {
-            var config = HeroQualityLVConfig.GetQualityLVConfig(quality, lv);
-            var itemID = config.UPCostItem[0];
-            var count = (ulong)config.UPCostItem[1];
-            if (!itemCounDic.ContainsKey(itemID))
-            {
-                itemCounDic[itemID] = count;
-            }
-            itemCounDic[itemID] = itemCounDic[itemID] + count;
-        }
-
-        return itemCounDic;
-    }
-
-
-    public Dictionary<int, ulong> GetHeroBreakPayBack(int quality, int lv)
-    {
-        //姹囨�昏繑杩樻�绘暟閲�
-        Dictionary<int, ulong> itemCounDic = new Dictionary<int, ulong>();
-        for (int i = 0; i < lv; i++)
-        {
-            var config = HeroQualityBreakConfig.GetQualityBreakConfig(quality, lv);
-            var itemID = config.UPCostItem[0];
-            var count = (ulong)config.UPCostItem[1];
-            if (!itemCounDic.ContainsKey(itemID))
-            {
-                itemCounDic[itemID] = count;
-            }
-            itemCounDic[itemID] = itemCounDic[itemID] + count;
-        }
-
-        return itemCounDic;
-
-
-    }
-
-    public Dictionary<int, ulong> GetHeroQualityAwakePayBack(int quality, int lv)
-    {
-        //姹囨�昏繑杩樻�绘暟閲�
-        Dictionary<int, ulong> itemCounDic = new Dictionary<int, ulong>();
-        for (int i = 0; i < lv; i++)
-        {
-            var config = HeroQualityAwakeConfig.GetQualityAwakeConfig(quality, lv);
-            var itemID = config.UPCostItem[0];
-            var count = (ulong)config.UPCostItem[1];
-            if (!itemCounDic.ContainsKey(itemID))
-            {
-                itemCounDic[itemID] = count;
-            }
-            itemCounDic[itemID] = itemCounDic[itemID] + count;
-        }
-
-        return itemCounDic;
-    }
-
-    public void UpdateHeroInfo(HB125_tagSCPlayerHeroInfo netPack)
-    { 
-        awakeRebirthCnt = netPack.AwakeRebirthCnt;
-    }
     
-    #endregion
+
+
+    
 }
 
 public struct WaitHeroFuncResponse
diff --git a/Main/System/KnapSack/Logic/ItemLogicUtility.cs b/Main/System/KnapSack/Logic/ItemLogicUtility.cs
index 750cbf2..104a518 100644
--- a/Main/System/KnapSack/Logic/ItemLogicUtility.cs
+++ b/Main/System/KnapSack/Logic/ItemLogicUtility.cs
@@ -262,26 +262,7 @@
     #endregion
 
 
-    private bool CheckIsExtendGrid(int itemId)
-    {
-        SinglePack singlePack = packModel.GetSinglePack(PackType.Item);
-        if (singlePack == null) return false;
 
-        int startLockIndex = singlePack.unlockedGridCount - PackManager.Instance.initBagGridCount;
-        FuncConfigConfig _tagFuncModel = FuncConfigConfig.Get("OpenBagItem");
-        var haveCount = packModel.GetItemCountByID(PackType.Item, itemId);
-        Equation.Instance.Clear();
-        Equation.Instance.AddKeyValue("index", startLockIndex + 1);
-        int needTool = Equation.Instance.Eval<int>(_tagFuncModel.Numerical2);
-        if (haveCount >= (ulong)needTool)
-        {
-            return true;
-        }
-        else
-        {
-            return false;
-        }
-    }
 
     /// <summary>
     /// <param name="packType 鑳屽寘绫诲瀷"></param>
diff --git a/Main/System/Main/FightPowerManager.cs b/Main/System/Main/FightPowerManager.cs
index dfc2a0f..bd3233f 100644
--- a/Main/System/Main/FightPowerManager.cs
+++ b/Main/System/Main/FightPowerManager.cs
@@ -1,236 +1,479 @@
-锘縰sing System.Collections;
+锘縰sing System;
+using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
 using UnityEngine;
+using LitJson;
+using Spine;
 
-
+//锛佸崟鑻遍泟鏌ョ湅鎴樺姏 鍙畻鑷繁鐨勪笂闃靛睘鎬�  涓嶇畻缇佺粖 鎬讳笂闃靛睘鎬�  鍏夌幆
+// 鎴樺姏鐨勮绠楁柟寮�
+//  鍏堢畻涓婇樀鐨勫崟姝﹀皢鎴樺姏鎸夊叕寮忎竴涓�绠楀嚭鍚勪釜灞炴�э紙鍩虹/鎴樻枟锛夛紝鍐嶆妸绠楀嚭鏉ョ殑鍚勪釜灞炴�т唬鍏ュ埌鎴樺姏鍏紡
+//  鎵�鏈夋灏嗘垬鍔涘姞璧锋潵 + 鎶�鑳芥垬鍔涙眹鎬伙紙鍏紡锛夊氨鏄暣涓彿鐨勬垬鍔�
 public class FightPowerManager : Singleton<FightPowerManager>
 {
-    string propertyFormula;
+    public string propertyFormula;
+    public string fightPropertyFormula;
+    public string fightPowerFormula;
+    public string skillFightPowerFormula;
+
+    Dictionary<string, double> propertyVariables = new Dictionary<string, double>();
+    Dictionary<string, double> fightPowerVariables = new Dictionary<string, double>();  //鎬绘垬鍔涗腑鐨勫崟姝﹀皢鎴樺姏
+
 
     public FightPowerManager()
     {
+        // 鏁板��1锛氬熀纭�涓夌淮灞炴�ц绠楀叕寮�
+        // 鏁板��2锛氭垬鏂楀睘鎬�/鎴樻枟鎶楁��/鐗规畩灞炴�ц绠楀叕寮�
+        // 鏁板��3锛氬睘鎬ф垬鍔涜绠楀叕寮忥紝璁$畻鍙傛暟璇﹁ S.灞炴�ф潯鐩厤缃�
         var config = FuncConfigConfig.Get("HeroAttrFormula");
         propertyFormula = config.Numerical1;
+        fightPropertyFormula = config.Numerical2;
+        fightPowerFormula = config.Numerical3;
+        skillFightPowerFormula = config.Numerical4;
     }
 
+    #region 鍒濆鍖栨垬鍔涜绠楃殑淇℃伅
+    TeamType teamTypeCalc = TeamType.Story; //涓嶅悓闃靛鎴樺姏涓嶅悓
+    bool isPreviewTeamPower;  //棰勮闃靛锛堥槦浼嶏級鎴樺姏
+    int dropIndexCalc = -1; //鎺夎惤瑁呭鍦ㄩ樀瀹圭殑绱㈠紩锛岀敤浜庨瑙堟垬鍔涘姣�
 
-    //瑁呭鎴樺姏涓烘渶缁堟�绘垬鍔涚殑缁撴灉姣旓紙鎻愬崌鏁翠釜瑙掕壊鎬绘垬鍔涳級
-    public int CalculatePower(int level)
+    //璁$畻闃靛鎴樺姏锛岃澶囧姣旂瓑鎯呭喌闇�瑕佷唬鍏�
+    /// <summary>
+    /// 
+    /// </summary>
+    /// <param name="teamType">闃靛绫诲瀷</param>
+    /// <param name="dropindex">鎺夎惤瑁呭鐨勭储寮曪紝-1浠h〃涓嶆浛鎹㈣绠�</param>
+    /// <param name="ispreview">棰勮闃靛鎴樺姏</param>
+    public void InitFightPowerParam(TeamType teamType = TeamType.Story, int dropindex = -1, bool ispreview = false)
     {
-        // Equation.Instance.Clear();
-        // Equation.Instance.AddKeyValue("equipScoreTotal", CountEquipScore(level));
-        // var power = Equation.Instance.Eval<int>(scoreFormula);
+        teamTypeCalc = teamType;
+        isPreviewTeamPower = ispreview;
 
-        // var propertyContainer = new Properties();
-
-        // Equation.Instance.Clear();
-        // var keys = propertyContainer.keys;
-        // for (int i = 0; i < keys.Count; i++)
-        // {
-        //     var id = keys[i];
-        //     var value = propertyContainer[id];
-        //     var config = PlayerPropertyConfig.Get(id);
-        //     Equation.Instance.AddKeyValue(config.Parameter, value);
-        // }
-
-        // var propertyPower = Equation.Instance.Eval<int>(propertyFormula);
-        // power += propertyPower;
-
-
-        return 0;
+        dropIndexCalc = dropindex;
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氬垵濮嬪寲鍙傛暟 dropIndex锛�" + dropIndexCalc + " 闃靛瀷锛�" + teamTypeCalc + " ispreview:" + ispreview);
+#endif
     }
+    #endregion
 
-    //鍜岃韩涓婅澶囧姣�
-    public long GetFightPowerChange(ItemModel item)
+
+
+    #region 鍏堣绠楁墍鏈夊姛鑳界殑姹囨�诲睘鎬�
+
+    //鍔熻兘灞炴�� 绫诲瀷锛氬��
+    public Dictionary<int, int> lvAttrs = new Dictionary<int, int>();  //绛夌骇灞炴��
+
+    //鍒嗗紑瀛樺偍棰勮鍜� 鐪熷疄灞炴��
+    public Dictionary<int, int> equipAttrs = new Dictionary<int, int>();   //瑁呭灞炴��
+    public Dictionary<string, int> lineUpPerDict = new Dictionary<string, int>();  //闃靛灞炴�у姞鎴�
+    public Dictionary<int, int> countryAttrs = new Dictionary<int, int>();   //闃靛鍥藉锛堝厜鐜級灞炴��
+
+    //绛夌骇灞炴��
+    void RefreshLVAttrs()
     {
-        return 0;
-    }
-
-
-
-
-    class Properties
-    {
-        Dictionary<int, int> tables = new Dictionary<int, int>();
-
-        public List<int> keys { get { return new List<int>(tables.Keys); } }
-
-        public int this[int id] { get { return tables[id]; } }
-
-        public void Add(int id, int value)
+        lvAttrs.Clear();
+        var playerLVConfig = PlayerLVConfig.Get(PlayerDatas.Instance.baseData.LV);
+        foreach (var attrType in PlayerPropertyConfig.baseAttrs)
         {
-            if (id == 7)
+            lvAttrs[attrType] = GetPlayerLVValue(playerLVConfig, attrType);
+        }
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氱瓑绾у睘鎬� " + JsonMapper.ToJson(lvAttrs));
+#endif
+
+    }
+
+    public int GetPlayerLVValue(PlayerLVConfig config, int type)
+    {
+        if (type == 6)
+        {
+            return config.Atk;
+        }
+        else if (type == 7)
+        {
+            return config.Def;
+        }
+        else if (type == 8)
+        {
+            return config.MaxHP;
+        }
+        return 0;
+    }
+
+    //瑁呭灞炴��:韬笂瑁呭姹囨��
+    void RefrehEquipAttrs()
+    {
+        equipAttrs.Clear();  //韬笂瑁呭灞炴�ч噸缃�
+        for (int i = 0; i < EquipModel.TotleEquip; i++)
+        {
+            var equip = EquipModel.Instance.GetEquip(i);
+            if (dropIndexCalc != -1)
             {
-                Add(67, value);
-                Add(68, value);
+                var dropEquip = PackManager.Instance.GetItemByIndex(PackType.DropItem, dropIndexCalc);
+                if (dropEquip.config.EquipPlace - 1 == i)
+                {
+                    equip = dropEquip;  //鏇挎崲璁$畻鎬绘垬鍔�
+                }
+            }
+            if (equip == null)
+            {
+                continue;
+            }
+            var baseIDAttrs = EquipModel.Instance.GetEquipBaseAttrs(equip);
+            var baseVauleAttrs = EquipModel.Instance.GetEquipBaseValues(equip);
+            if (baseIDAttrs != null)
+            {
+                for (int j = 0; j < baseIDAttrs.Count; j++)
+                {
+                    if (!equipAttrs.ContainsKey(baseIDAttrs[j]))
+                    {
+                        equipAttrs[baseIDAttrs[j]] = baseVauleAttrs[j];
+                    }
+                    else
+                    {
+                        equipAttrs[baseIDAttrs[j]] += baseVauleAttrs[j];
+                    }
+                }
+            }
+
+            var fightIDAttrs = EquipModel.Instance.GetEquipFightAttrs(equip);
+            var fightValueAttrs = EquipModel.Instance.GetEquipFightValues(equip);
+            if (fightIDAttrs != null)
+            {
+                for (int j = 0; j < fightIDAttrs.Count; j++)
+                {
+                    if (!equipAttrs.ContainsKey(fightIDAttrs[j]))
+                    {
+                        equipAttrs[fightIDAttrs[j]] = fightValueAttrs[j];
+                    }
+                    else
+                    {
+                        equipAttrs[fightIDAttrs[j]] += fightValueAttrs[j];
+                    }
+                }
+            }
+        }
+
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氳澶囧睘鎬� " + JsonMapper.ToJson(equipAttrs));
+#endif
+    }
+
+    // 璁$畻闃熶紞涓笂闃电殑鎵�鏈夋灏嗙殑涓婇樀灞炴�� 鍜� 鍏夌幆
+    void RefreshTeamAttrs()
+    {
+        //闃靛灞炴��
+        // 闃靛锛氭墍鏈夋灏嗕笂闃靛睘鎬�
+        lineUpPerDict = HeroUIManager.Instance.GetLineupPer(teamTypeCalc, isPreviewTeamPower);
+
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氫笂闃靛睘鎬� " + JsonMapper.ToJson(lineUpPerDict));
+#endif
+        // 闃靛锛氬浗瀹讹紙鍏夌幆锛夊睘鎬�
+        countryAttrs = HeroUIManager.Instance.GetCountryAttrs(teamTypeCalc, isPreviewTeamPower);
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氬浗瀹讹紙鍏夌幆锛夊睘鎬� " + JsonMapper.ToJson(countryAttrs));
+#endif
+    }
+
+    #endregion
+
+    //鍗曞睘鎬у叕寮忓垎鍩虹涓夌淮鍜屾垬鏂楀睘鎬�
+    // 銆愪富鍏睘鎬с��
+    // 绛夌骇灞炴��    lvValue
+    // 瑁呭灞炴��    equipValue
+    // 鍥鹃壌灞炴��    bookValue 銆� bookPer
+
+    // 銆愰樀瀹瑰睘鎬с�� - 璇ラ樀瀹规墍鏈夋灏嗘湁鏁�
+    // 鍒濆鍔犳垚    lineupInitAddPer
+    // 鍗囩骇鍔犳垚    lineupLVAddPer
+    // 绐佺牬鍔犳垚    lineupBreakLVAddPer
+    // 鍚炲櫖鍔犳垚    lineupStarAddPer
+    // 闃靛鍏夌幆    lineupHaloValue 銆� lineupHaloPer
+
+    // 銆愭灏嗗睘鎬с��
+    // 缁ф壙姣斾緥    inheritPer
+    // 鑷韩灞炴��    heroSelfValue 銆� heroSelfPer
+    // 鍚炲櫖灞炴��    starTalentValue 銆� starTalentPer
+    // 绐佺牬灞炴��    breakLVValue 銆� breakLVPer
+    // 瑙夐啋灞炴��    awakeTalentValue 銆� awakeTalentPer
+    // 缇佺粖灞炴��    fetterValue 銆� fetterPer
+
+    #region 灞炴�у叕寮�
+    // 鍗曞熀纭�灞炴�ц绠�
+    public double GetPropertyVaule(int attrType, HeroInfo hero, string formula)
+    {
+        propertyVariables.Clear();
+        propertyVariables["lvValue"] = lvAttrs.ContainsKey(attrType) ? lvAttrs[attrType] : 0;
+        propertyVariables["equipValue"] = equipAttrs.ContainsKey(attrType) ? equipAttrs[attrType] : 0;
+        propertyVariables["bookValue"] = 0;
+        propertyVariables["bookPer"] = GetBookPer(attrType) / 10000.0f;
+
+        //锛侊紒锛佸崟姝﹀皢鎴樺姏棰勮鐨勮瘽闇�瑕佹帓闄ら槦浼嶅奖鍝嶆垬鍔涳紝鍙畻姝﹀皢鑷韩鐨勪笂闃靛睘鎬�
+        propertyVariables["lineupInitAddPer"] = GetLineUpPer(attrType, "lineupInitAddPer") / 10000.0f;
+        propertyVariables["lineupLVAddPer"] = GetLineUpPer(attrType, "lineupLVAddPer") / 10000.0f;
+        propertyVariables["lineupBreakLVAddPer"] = GetLineUpPer(attrType, "lineupBreakLVAddPer") / 10000.0f;
+        propertyVariables["lineupStarAddPer"] = GetLineUpPer(attrType, "lineupStarAddPer") / 10000.0f;
+
+        //闃靛鍏夌幆 涓夊洿鐧惧垎姣斿姞鎴�
+        propertyVariables["lineupHaloValue"] = countryAttrs.ContainsKey(attrType) ? countryAttrs[attrType] : 0;
+        propertyVariables["lineupHaloPer"] = GetCountryPer(attrType) / 10000.0f;
+
+
+        //姝﹀皢灞炴��
+        propertyVariables["inheritPer"] = hero.GetInheritAttrPer(attrType) / 10000.0f;
+        propertyVariables["heroSelfValue"] = hero.GetSelfAddValue(attrType);
+        propertyVariables["heroSelfPer"] = hero.GetSelfAddPer(attrType) / 10000.0f;
+        propertyVariables["starTalentValue"] = hero.GetTalentAttrValue(attrType);
+        propertyVariables["starTalentPer"] = hero.GetTalentAttrPer(attrType) / 10000.0f;
+        propertyVariables["breakLVValue"] = hero.GetBreakAttrValue(attrType);
+        propertyVariables["breakLVPer"] = hero.GetBreakAttrPer(attrType) / 10000.0f;
+        propertyVariables["awakeTalentValue"] = hero.GetAwakeAttrValue(attrType);
+        propertyVariables["awakeTalentPer"] = hero.GetAwakeAttrPer(attrType) / 10000.0f;
+        propertyVariables["fetterValue"] = hero.GetFetterAttrValue(attrType);
+        propertyVariables["fetterPer"] = hero.GetFetterAttrPer(attrType) / 10000.0f;
+
+#if UNITY_EDITOR
+        //鎺掗櫎鍊间负0鐨勫睘鎬ц緭鍑�
+        var tmpPropertyVariables = propertyVariables.Where(x => x.Value > 0).ToDictionary(x => x.Key, x => x.Value);
+        if (!tmpPropertyVariables.IsNullOrEmpty())
+            propertyStrForDebug += $"灞炴�D {attrType} - {JsonMapper.ToJson(tmpPropertyVariables)}";
+#endif
+        return JaceCalculator.Calculate(formula, propertyVariables);
+    }
+
+
+    int GetLineUpPer(int attrType, string key)
+    {
+        if (!PlayerPropertyConfig.baseAttrs.Contains(attrType))
+        {
+            return 0;
+        }
+
+        return lineUpPerDict[key];
+    }
+
+    int GetBookPer(int attrType)
+    {
+        if (!PlayerPropertyConfig.baseAttrs.Contains(attrType))
+        {
+            return 0;
+        }
+        return HeroUIManager.Instance.bookPer;
+    }
+
+    int GetCountryPer(int attrType)
+    {
+        if (PlayerPropertyConfig.baseAttr2perDict.ContainsKey(attrType))
+        {
+            var pertype = PlayerPropertyConfig.baseAttr2perDict[attrType];
+            return countryAttrs.ContainsKey(pertype) ? countryAttrs[pertype] : 0;
+        }
+
+        return 0;
+    }
+
+
+
+    #endregion
+
+
+    #region 璁$畻鎴樺姏
+    //濡傛灉鏈嶅姟绔垬鍔涜绠楁湁鍘嬪姏锛屽彲鏀规垚鍏抽敭鐐圭粨绠楋紙濡傚悓姝ユ帓琛屾璺ㄦ湇绛夛級锛岃〃鐜扮敱瀹㈡埛绔嚜宸辫绠�
+    //瑁呭鎴樺姏涓烘渶缁堟�绘垬鍔涚殑缁撴灉姣旓紙鎻愬崌鏁翠釜瑙掕壊鎬绘垬鍔涳級
+
+    //璁$畻鎬绘垬鍔涗腑鐨勬灏嗘垬鍔涳紝鍑犱釜姝﹀皢鍔犺捣鏉ュ氨鏄�绘垬鍔涳紝鍏朵粬鍔熻兘灞炴�ц绠楀簲璇ユ兜鐩栧湪鑻遍泟閲�
+    public long CalculatePower()
+    {
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氬紑濮嬭绠�");
+#endif
+        // --- 鍏堣绠楁墍鏈夊姛鑳界殑姹囨�诲睘鎬� ---
+        RefreshLVAttrs();
+        RefrehEquipAttrs();
+        RefreshTeamAttrs();
+
+
+        // --- 绠楀崟姝﹀皢鍔熻兘灞炴�ф垬鍔� 鍚庣浉鍔�---
+        long fightPower = 0;
+        var team = TeamManager.Instance.GetTeam(teamTypeCalc);
+        if (team == null)
+        {
+            return fightPower;
+        }
+        TeamHero[] teamHeroes = isPreviewTeamPower ? team.tempHeroes : team.serverHeroes;
+        foreach (var hero in teamHeroes)
+        {
+            if (hero == null)
+            {
+                continue;
+            }
+            HeroInfo heroInfo = HeroManager.Instance.GetHero(hero.guid);
+            if (heroInfo == null)
+            {
+                continue;
+            }
+
+            fightPower += CalculateTeamHeroPower(heroInfo);
+
+        }
+
+#if UNITY_EDITOR
+        Debug.Log("鎴樺姏锛氳绠楀畬姣� " + fightPower);
+#endif
+        return fightPower;
+    }
+
+#if UNITY_EDITOR
+    string propertyStrForDebug = "";
+#endif
+
+    //璁$畻闃靛涓灏嗘垬鍔�
+    public long CalculateTeamHeroPower(HeroInfo hero)
+    {
+
+        fightPowerVariables.Clear();
+        hero.RefreshFetterAttrsWhenCalcPower(teamTypeCalc); //缇佺粖灞炴�ц瀹炴椂绠�
+
+#if UNITY_EDITOR
+        propertyStrForDebug = "";
+#endif
+        foreach (var config in PlayerPropertyConfig.GetValues())
+        {
+            if (config.showType < 1 || config.showType > 4)
+            {
+                continue;
+            }
+            if (config.showType == 1)
+            {
+                fightPowerVariables[config.Parameter] = (ulong)GetPropertyVaule(config.ID, hero, propertyFormula);
             }
             else
             {
-                if (tables.ContainsKey(id))
-                {
-                    tables[id] = tables[id] + value;
-                }
-                else
-                {
-                    tables[id] = value;
-                }
+                fightPowerVariables[config.Parameter] = (ulong)GetPropertyVaule(config.ID, hero, fightPropertyFormula);
             }
         }
 
-        public void AddRange(List<int> ids, List<int> values)
-        {
-            if (ids.IsNullOrEmpty() || values.IsNullOrEmpty())
-            {
-                return;
-            }
+#if UNITY_EDITOR
+        Debug.Log($"鎴樺姏锛氭灏咺D {hero.heroId} 灞炴�т俊鎭� {propertyStrForDebug}");
+#endif
 
-            var count = Mathf.Min(ids.Count, values.Count);
-            for (int i = 0; i < count; i++)
-            {
-                Add(ids[i], values[i]);
-            }
-        }
+        //灞炴�х郴鏁版牴鎹畼鑱岀瓑绾х殑鍔犳垚
+        var fightPowerRatioConfig = FightPowerRatioConfig.Get(PlayerDatas.Instance.baseData.realmLevel);
 
+        fightPowerVariables["AtkRatio"] = fightPowerRatioConfig.AtkRatio;
+        fightPowerVariables["MaxHPRatio"] = fightPowerRatioConfig.MaxHPRatio;
+        fightPowerVariables["DefRatio"] = fightPowerRatioConfig.DefRatio;
+        fightPowerVariables["StunRateRatio"] = fightPowerRatioConfig.StunRateRatio;
+        fightPowerVariables["SuperHitRateRatio"] = fightPowerRatioConfig.SuperHitRateRatio;
+        fightPowerVariables["ComboRateRatio"] = fightPowerRatioConfig.ComboRateRatio;
+        fightPowerVariables["MissRateRatio"] = fightPowerRatioConfig.MissRateRatio;
+        fightPowerVariables["ParryRateRatio"] = fightPowerRatioConfig.ParryRateRatio;
+        fightPowerVariables["SuckHPPerRatio"] = fightPowerRatioConfig.SuckHPPerRatio;
+        fightPowerVariables["StunRateDefRatio"] = fightPowerRatioConfig.StunRateDefRatio;
+        fightPowerVariables["SuperHitRateDefRatio"] = fightPowerRatioConfig.SuperHitRateDefRatio;
+        fightPowerVariables["ComboRateDefRatio"] = fightPowerRatioConfig.ComboRateDefRatio;
+        fightPowerVariables["MissRateDefRatio"] = fightPowerRatioConfig.MissRateDefRatio;
+        fightPowerVariables["ParryRateDefRatio"] = fightPowerRatioConfig.ParryRateDefRatio;
+        fightPowerVariables["SuckHPPerDefRatio"] = fightPowerRatioConfig.SuckHPPerDefRatio;
+        fightPowerVariables["NormalSkillPerRatio"] = fightPowerRatioConfig.NormalSkillPerRatio;
+        fightPowerVariables["NormalSkillPerDefRatio"] = fightPowerRatioConfig.NormalSkillPerDefRatio;
+        fightPowerVariables["AngerSkillPerRatio"] = fightPowerRatioConfig.AngerSkillPerRatio;
+        fightPowerVariables["AngerSkillPerDefRatio"] = fightPowerRatioConfig.AngerSkillPerDefRatio;
+        fightPowerVariables["SuperDamPerRatio"] = fightPowerRatioConfig.SuperDamPerRatio;
+        fightPowerVariables["SuperDamPerDefRatio"] = fightPowerRatioConfig.SuperDamPerDefRatio;
+        fightPowerVariables["ShieldPerRatio"] = fightPowerRatioConfig.ShieldPerRatio;
+        fightPowerVariables["ShieldPerDefRatio"] = fightPowerRatioConfig.ShieldPerDefRatio;
+
+
+        long fightPower = (long)JaceCalculator.Calculate(fightPowerFormula, fightPowerVariables);
+#if UNITY_EDITOR
+        //鎺掗櫎鍊间负0鐨勫睘鎬ц緭鍑�
+        var tmpFightPowerVariables = fightPowerVariables.Where(x => x.Value > 0).ToDictionary(x => x.Key, x => x.Value);
+        if (!tmpFightPowerVariables.IsNullOrEmpty())
+            Debug.Log($"鎴樺姏锛氭灏咺D {hero.heroId} 灞炴�ф垬鍔� {fightPower} 灞炴�ф垬鍔涘弬鏁� {JsonMapper.ToJson(tmpFightPowerVariables)}");
+#endif
+
+        //鍔犱笂鎶�鑳芥垬鍔�
+        fightPowerVariables.Clear();
+        fightPowerVariables["PlayerLV"] = PlayerDatas.Instance.baseData.LV;
+        fightPowerVariables["OfficialLV"] = PlayerDatas.Instance.baseData.realmLevel;
+        fightPowerVariables["SkillPower"] = hero.GetSkillsFightPower();
+
+        long skillPower = (long)JaceCalculator.Calculate(skillFightPowerFormula, fightPowerVariables);
+
+#if UNITY_EDITOR
+        Debug.Log($"鎴樺姏锛氭灏咺D {hero.heroId} 鎶�鑳芥垬鍔� {skillPower} 鎶�鑳藉弬鏁� {JsonMapper.ToJson(fightPowerVariables)}");
+
+        Debug.Log($"鎴樺姏锛氭灏咺D {hero.heroId} 鎬绘垬鍔� {fightPower + skillPower}");
+#endif
+
+        return fightPower + skillPower;
     }
 
 
+    /// <summary>
+    /// 鍜岃韩涓婅澶囧姣斿樊
+    /// </summary>
+    /// <param name="item">鍦版澘瑁呭</param>
+    /// <returns></returns>
+    public long GetFightPowerChange(ItemModel item)
+    {
+        InitFightPowerParam();
+        var fightPower = CalculatePower();
 
-    #region 璁$畻鎴樻枟鍔�
-    public static readonly string FightPowerFormula = "FightpowerFormula";
+        InitFightPowerParam(dropindex: item.gridIndex);
+        var tmpFightPower = CalculatePower();
+        return tmpFightPower - fightPower;
+    }
 
-    // public static int GetFightPower(Dictionary<int, int> _propertyDict)
-    // {
-    //     Equation.Instance.Clear();
-    //     if (_propertyDict == null || _propertyDict.Count == 0)
-    //     {
-    //         return 0;
-    //     }
 
-    //     foreach (var _key in _propertyDict.Keys)
-    //     {
-    //         PlayerPropertyConfig cfg = PlayerPropertyConfig.Get(_key);
-    //         if (cfg != null)
-    //         {
-    //             if (_key == 7)
-    //             {
-    //                 Equation.Instance.AddKeyValue("MinAtk", _propertyDict[_key]);
-    //                 Equation.Instance.AddKeyValue("MaxAtk", _propertyDict[_key]);
-    //             }
-    //             else if (_key == 24)
-    //             {
-    //                 Equation.Instance.AddKeyValue("PetMinAtk", _propertyDict[_key]);
-    //                 Equation.Instance.AddKeyValue("PetMaxAtk", _propertyDict[_key]);
-    //             }
-    //             else
-    //             {
-    //                 ulong attrValue = (ulong)_propertyDict[_key];
-    //                 var fightParm = GetFightPowerParmByAttrId(_key);
-    //                 if (_key == 11)
-    //                 {
-    //                     var playerLv = PlayerDatas.Instance.baseData.LV;
-    //                     var paramConfig = FightPowerParamConfig.Get(playerLv);
-    //                     Equation.Instance.AddKeyValue("AtkSpeedParameter", paramConfig.AtkSpeedParameter);
-    //                 }
-    //                 else
-    //                 {
-    //                     if (fightParm != 0)
-    //                     {
-    //                         attrValue = attrValue * (ulong)fightParm;
-    //                     }
-    //                 }
-    //                 Equation.Instance.AddKeyValue(cfg.Parameter, attrValue);
-    //             }
-    //         }
+    // 鍗曡嫳闆勬煡鐪嬫垬鍔� 
+    // 1. 涓婇樀鑻遍泟鏄剧ず锛屽湪涓荤嚎闃靛涓嬬殑鎴樺姏
+    // 2. 闈炰笂闃垫垨鍏朵粬涓婇樀闃靛锛氫笂闃典笉瓒�6涓汉鐨勶紝鎸夊鍔犵殑鏂瑰紡鐨勮绠楋紱浜烘暟婊$殑鎯呭喌涓嬫寜鏇挎崲6鍙蜂綅璁$畻
+    public long GetHeroFightPower(HeroInfo heroInfo)
+    {
+        bool ispreview = false;
+        var team = TeamManager.Instance.GetTeam(TeamType.Story);
+        if (!team.HasHero(heroInfo.itemHero.guid))
+        {
+            //鏇挎崲涓婇樀浣嶇疆
+            ispreview = true;
+            var index = team.GetEmptyPosition();
+            if (index < 0)
+            {
+                team.AddHero(heroInfo, 5);
+            }
+            else
+            {
+                team.AddHero(heroInfo, index);
+            }
+        }
 
-    //     }
-    //     FuncConfigConfig funcCfg = FuncConfigConfig.Get(FightPowerFormula);
-    //     return Equation.Instance.Eval<int>(funcCfg.Numerical1);
-    // }
+        InitFightPowerParam(ispreview: ispreview);
+        RefreshLVAttrs();
+        RefrehEquipAttrs();
+        RefreshTeamAttrs();
 
-    // public static int GetFightPowerParmByAttrId(int attrId)
-    // {
-    //     int playerLv = PlayerDatas.Instance.baseData.LV;
-    //     FightPowerParamConfig paramConfig = FightPowerParamConfig.Get(playerLv);
-    //     PlayerPropertyConfig cfg = PlayerPropertyConfig.Get(attrId);
-    //     if (paramConfig == null || cfg == null) return 0;
+        var fightPower = CalculateTeamHeroPower(heroInfo);
 
-    //     switch (cfg.Parameter)
-    //     {
-    //         case "Hit":
-    //             return paramConfig.Hit;
-    //         case "Miss":
-    //             return paramConfig.Miss;
-    //         case "IgnoreDefRate":
-    //             return paramConfig.IgnoreDefRate;
-    //         case "DamChanceDef":
-    //             return paramConfig.DamChanceDef;
-    //         case "FaintRate":
-    //             return paramConfig.FaintRate;
-    //         case "LuckyHitRateReduce":
-    //             return paramConfig.LuckyHitRateReduce;
-    //         case "SkillAtkRate":
-    //             return paramConfig.SkillAtkRate;
-    //         case "SkillAtkRateReduce":
-    //             return paramConfig.SkillAtkRateReduce;
-    //         case "DamagePerPVP":
-    //             return paramConfig.DamagePerPVP;
-    //         case "DamagePerPVPReduce":
-    //             return paramConfig.DamagePerPVPReduce;
-    //         case "DamBackPer":
-    //             return paramConfig.DamBackPer;
-    //         case "IgnoreDefRateReduce":
-    //             return paramConfig.IgnoreDefRateReduce;
-    //         case "FaintDefRate":
-    //             return paramConfig.FaintDefRate;
-    //         case "AtkSpeedParameter":
-    //             return paramConfig.AtkSpeedParameter;
-    //         case "JobAHurtAddPer":
-    //             return paramConfig.JobAHurtAddPer;
-    //         case "JobBHurtAddPer":
-    //             return paramConfig.JobBHurtAddPer;
-    //         case "JobCHurtAddPer":
-    //             return paramConfig.JobCHurtAddPer;
-    //         case "JobAAtkReducePer":
-    //             return paramConfig.JobAAtkReducePer;
-    //         case "JobBAtkReducePer":
-    //             return paramConfig.JobBAtkReducePer;
-    //         case "JobCAtkReducePer":
-    //             return paramConfig.JobCAtkReducePer;
-    //         case "SuperHitRate":
-    //             return paramConfig.SuperHitRate;
-    //         case "LuckyHitRate":
-    //             return paramConfig.LuckyHitRate;
-    //         case "SuperHitRateReduce":
-    //             return paramConfig.SuperHitRateReduce;
-    //         case "FinalHurtPer":
-    //             return paramConfig.FinalHurtPer;
-    //         case "FinalHurtReducePer":
-    //             return paramConfig.FinalHurtReducePer;
-    //         case "NPCHurtAddPer":
-    //             return paramConfig.NPCHurtAddPer;
-    //         case "NormalHurtPer":
-    //             return paramConfig.NormalHurtPer;
-    //         case "FabaoHurtPer":
-    //             return paramConfig.FabaoHurtPer;
-    //         case "AffairSpeedPer":
-    //             return paramConfig.AffairSpeedPer;
-    //         case "FamilyBossHurtPer":
-    //             return paramConfig.FamilyBossHurtPer;
-    //         case "FamilyWarHPPer":
-    //             return paramConfig.FamilyWarHPPer;
-    //         case "FamilyWarAtkPer":
-    //             return paramConfig.FamilyWarAtkPer;
-    //         case "FamilySitExpPer":
-    //             return paramConfig.FamilySitExpPer;
-    //         case "BossFinalHurtPer":
-    //             return paramConfig.BossFinalHurtPer;
-    //     }
+        //璁$畻瀹屾仮澶嶉槦浼�
+        if (ispreview)
+            team.RestoreTeam();
+        return fightPower;
+    }
 
-    //     return 0;
-    // }
 
+    //鏌ョ湅闃靛鎴樺姏
+    public long GetTeamFightPower(TeamType team, bool isPreview)
+    {
+        InitFightPowerParam(team, -1, isPreview);
+        return CalculatePower();
+    }
     #endregion
 
+
 }
 
 
diff --git a/Main/System/OpenServerActivity/OperationTimeHepler.cs b/Main/System/OpenServerActivity/OperationTimeHepler.cs
index 94ecc3c..bd6219d 100644
--- a/Main/System/OpenServerActivity/OperationTimeHepler.cs
+++ b/Main/System/OpenServerActivity/OperationTimeHepler.cs
@@ -6,16 +6,16 @@
 
 public class OperationTimeHepler : Singleton<OperationTimeHepler>
 {
-    public Dictionary<Operation, OperationBase> operationDict = new Dictionary<Operation, OperationBase>();
+    public Dictionary<OperationType, OperationBase> operationDict = new Dictionary<OperationType, OperationBase>();
 
     public static StringBuilder textBuilder = new StringBuilder();
 
-    public event Action<Operation> operationTimeUpdateEvent;
-    public event Action<Operation> operationServerCloseEvent;//鐗规畩鎯呭喌涓嬭Е鍙�
-    public event Action<Operation, int> operationEndEvent;//娲诲姩缁撴潫鏃堕棿瑙﹀彂  绗簩涓弬鏁�0--杩囨椿鍔ㄦ椂闂磋Е鍙�  1--杩囨椿鍔ㄥぉ瑙﹀彂
-    public event Action<Operation, int> operationStartEvent;//娲诲姩寮�濮嬫椂闂村苟涓旀弧瓒冲紑鍚潯浠惰Е鍙� 绗簩涓弬鏁�0--娲诲姩鏃堕棿瑙﹀彂  1--娲诲姩澶╄Е鍙�
+    public event Action<OperationType> operationTimeUpdateEvent;
+    public event Action<OperationType> operationServerCloseEvent;//鐗规畩鎯呭喌涓嬭Е鍙�
+    public event Action<OperationType, int> operationEndEvent;//娲诲姩缁撴潫鏃堕棿瑙﹀彂  绗簩涓弬鏁�0--杩囨椿鍔ㄦ椂闂磋Е鍙�  1--杩囨椿鍔ㄥぉ瑙﹀彂
+    public event Action<OperationType, int> operationStartEvent;//娲诲姩寮�濮嬫椂闂村苟涓旀弧瓒冲紑鍚潯浠惰Е鍙� 绗簩涓弬鏁�0--娲诲姩鏃堕棿瑙﹀彂  1--娲诲姩澶╄Е鍙�
     public event Action<int> dayResetEvent;//娲诲姩閲嶇疆浜嬩欢0-0鐐� 1-5鐐�
-    public event Action<Operation> operationAdvanceEvent;//娲诲姩鍦ㄦ彁鍓嶅紑鍚殑鏃堕棿鍐�
+    public event Action<OperationType> operationAdvanceEvent;//娲诲姩鍦ㄦ彁鍓嶅紑鍚殑鏃堕棿鍐�
 
     public OperationTimeHepler()
     {
@@ -38,30 +38,30 @@
         {
             return;
         }
-        for (int i = 0; i < (int)Operation.max; i++)
+        for (int i = 0; i < (int)OperationType.max; i++)
         {
-            if (operationDict.ContainsKey((Operation)i))
+            if (operationDict.ContainsKey((OperationType)i))
             {
-                var operation = operationDict[(Operation)i];
+                var operation = operationDict[(OperationType)i];
                 if (!operation.inDateNotify && operation.SatisfyOpenCondition()
                     && operation.InDay(TimeUtility.ServerNow))
                 {
                     operation.inDateNotify = true;
                     operation.stepDateNotify = false;
-                    Debug.LogFormat("{0}  娲诲姩澶╁紑濮�", (Operation)i);
+                    Debug.LogFormat("{0}  娲诲姩澶╁紑濮�", (OperationType)i);
                     if (operationStartEvent != null)
                     {
-                        operationStartEvent((Operation)i, 1);
+                        operationStartEvent((OperationType)i, 1);
                     }
                 }
                 else if (!operation.stepDateNotify && !operation.InDay(TimeUtility.ServerNow))
                 {
                     operation.inDateNotify = false;
                     operation.stepDateNotify = true;
-                    Debug.LogFormat("{0}  娲诲姩澶╃粨鏉�", (Operation)i);
+                    Debug.LogFormat("{0}  娲诲姩澶╃粨鏉�", (OperationType)i);
                     if (operationEndEvent != null)
                     {
-                        operationEndEvent((Operation)i, 1);
+                        operationEndEvent((OperationType)i, 1);
                     }
                 }
                 if (!operation.inTimeNotify && operation.SatisfyOpenCondition()
@@ -69,10 +69,10 @@
                 {
                     operation.inTimeNotify = true;
                     operation.stepTimeNotify = false;
-                    Debug.LogFormat("{0}  娲诲姩鏃堕棿寮�濮�", (Operation)i);
+                    Debug.LogFormat("{0}  娲诲姩鏃堕棿寮�濮�", (OperationType)i);
                     if (operationStartEvent != null)
                     {
-                        operationStartEvent((Operation)i, 0);
+                        operationStartEvent((OperationType)i, 0);
                     }
                 }
                 else if (!operation.stepTimeNotify && !operation.InTime(TimeUtility.ServerNow))
@@ -80,10 +80,10 @@
                     operation.inTimeNotify = false;
                     operation.stepTimeNotify = true;
                     operation.inAdvanceNotify = false;
-                    Debug.LogFormat("{0}  娲诲姩鏃堕棿缁撴潫", (Operation)i);
+                    Debug.LogFormat("{0}  娲诲姩鏃堕棿缁撴潫", (OperationType)i);
                     if (operationEndEvent != null)
                     {
-                        operationEndEvent((Operation)i, 0);
+                        operationEndEvent((OperationType)i, 0);
                     }
                 }
 
@@ -91,10 +91,10 @@
                     && operation.InAdvanceTime(TimeUtility.ServerNow))
                 {
                     operation.inAdvanceNotify = true;
-                    Debug.LogFormat("{0}  娲诲姩鎻愬墠寮�鍚�", (Operation)i);
+                    Debug.LogFormat("{0}  娲诲姩鎻愬墠寮�鍚�", (OperationType)i);
                     if (operationAdvanceEvent != null)
                     {
-                        operationAdvanceEvent((Operation)i);
+                        operationAdvanceEvent((OperationType)i);
                     }
                 }
             }
@@ -684,7 +684,7 @@
     //     }
     // }
 
-    public void ForceStopOperation(Operation operationType)
+    public void ForceStopOperation(OperationType operationType)
     {
         if (operationDict.ContainsKey(operationType))
         {
@@ -701,12 +701,12 @@
         }
     }
 
-    public bool TryGetOperationTime(Operation type, out OperationBase operation)
+    public bool TryGetOperationTime(OperationType type, out OperationBase operation)
     {
         return operationDict.TryGetValue(type, out operation);
     }
 
-    public bool TryGetOperation<T>(Operation type, out T operation) where T : OperationBase
+    public bool TryGetOperation<T>(OperationType type, out T operation) where T : OperationBase
     {
         operation = null;
         if (operationDict.ContainsKey(type))
@@ -717,7 +717,7 @@
         return false;
     }
 
-    public bool InOperationTime(Operation type)
+    public bool InOperationTime(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -727,7 +727,7 @@
         return false;
     }
 
-    public bool InOperationTime(Operation type, DateTime time)
+    public bool InOperationTime(OperationType type, DateTime time)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -737,7 +737,7 @@
         return false;
     }
 
-    public bool InOperationJoinTime(Operation type, DateTime time)
+    public bool InOperationJoinTime(OperationType type, DateTime time)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -747,7 +747,7 @@
         return false;
     }
 
-    public bool InOperationDay(Operation type)
+    public bool InOperationDay(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -757,7 +757,7 @@
         return false;
     }
 
-    public int GetOperationSurplusTime(Operation type)
+    public int GetOperationSurplusTime(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -767,7 +767,7 @@
         return 0;
     }
 
-    public bool InOperationAdvance(Operation type)
+    public bool InOperationAdvance(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -777,7 +777,7 @@
         return false;
     }
 
-    public int GetOperationSecondsBeforeStart(Operation type)
+    public int GetOperationSecondsBeforeStart(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -787,7 +787,7 @@
         return 0;
     }
 
-    public bool SatisfyOpenCondition(Operation type)
+    public bool SatisfyOpenCondition(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -797,7 +797,7 @@
         return false;
     }
 
-    public bool SatisfyOpenCondition(Operation type, DateTime time)
+    public bool SatisfyOpenCondition(OperationType type, DateTime time)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -808,7 +808,7 @@
     }
 
     //娲诲姩寮�鍚腑锛屾湁鍙備笌杩涜鏃堕棿娈�
-    public bool SatisfyJoinCondition(Operation type, DateTime time)
+    public bool SatisfyJoinCondition(OperationType type, DateTime time)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -818,7 +818,7 @@
         return false;
     }
 
-    public bool IsPrepareTime(Operation type, DateTime time)
+    public bool IsPrepareTime(OperationType type, DateTime time)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -828,7 +828,7 @@
         return false;
     }
 
-    public bool SatisfyAdvanceCondition(Operation type)
+    public bool SatisfyAdvanceCondition(OperationType type)
     {
         OperationBase operation;
         if (TryGetOperationTime(type, out operation))
@@ -838,7 +838,7 @@
         return false;
     }
 
-    public void ProcessConditionError(Operation type)
+    public void ProcessConditionError(OperationType type)
     {
         if (SatisfyOpenCondition(type))
         {
@@ -1043,7 +1043,7 @@
     }
 }
 
-public enum Operation
+public enum OperationType
 {
     MultipleExp,
     ConsumeRebate,
diff --git a/Main/System/Store/StoreModel.cs b/Main/System/Store/StoreModel.cs
index 93d17e5..63460e9 100644
--- a/Main/System/Store/StoreModel.cs
+++ b/Main/System/Store/StoreModel.cs
@@ -63,7 +63,7 @@
     //鎺掕姒滄椿鍔ㄧ殑鍟嗗簵
     public int rankActStore_MoneyType;
     public int rankActStore_StoreType;
-    public Operation rankActStore_ActType;
+    public OperationType rankActStore_ActType;
 
     public override void Init()
     {
diff --git a/Main/System/Team/TeamBase.cs b/Main/System/Team/TeamBase.cs
index f578df0..30933ec 100644
--- a/Main/System/Team/TeamBase.cs
+++ b/Main/System/Team/TeamBase.cs
@@ -226,6 +226,18 @@
         return false;
     }
 
+    public bool HasHeroInServer(string guid)
+    {
+        foreach (var hero in serverHeroes)
+        {
+            if (hero != null && hero.guid == guid)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
     //瀹㈡埛绔粠0寮�濮嬶紝鏈嶅姟绔粠1寮�濮�
     public int GetEmptyPosition()
     {
@@ -270,17 +282,7 @@
             return;
         }
 
-        TeamHero targetHero = tempHeroes[targetPosition];
-
-        if (null == targetHero)
-        {
-            TeamHero newHero = new TeamHero(heroInfo, targetPosition, this);
-            SetTeamHero(targetPosition, newHero);
-        }
-        else
-        {
-            SetTeamHero(targetPosition, new TeamHero(heroInfo, targetPosition, this));
-        }
+        SetTeamHero(targetPosition, new TeamHero(heroInfo, targetPosition, this));
     }
 
 
diff --git a/Main/System/Team/TeamHero.cs b/Main/System/Team/TeamHero.cs
index c5af4ad..f28f8b9 100644
--- a/Main/System/Team/TeamHero.cs
+++ b/Main/System/Team/TeamHero.cs
@@ -1,5 +1,4 @@
 using UnityEngine;
-using UnityEngine.PlayerLoop;
 
 public partial class TeamHero
 {
@@ -79,6 +78,7 @@
         heroId = heroInfo.itemHero.config.ID;
         SkinID = heroInfo.SkinID;
         skinConfig = heroInfo.skinConfig;
+        Country = heroInfo.heroCountry;
 
         teamBase = _teamBase;
 
diff --git a/Main/System/Tip/PowerAddWin.cs b/Main/System/Tip/PowerAddWin.cs
index 10c3b7b..3bd2e58 100644
--- a/Main/System/Tip/PowerAddWin.cs
+++ b/Main/System/Tip/PowerAddWin.cs
@@ -140,9 +140,9 @@
                             sequence.Append(txtBase.DOText(DisplayBasePowerNum(nowValue), rollTime).SetEase(rollEaseType).OnComplete(() => { SetInitPosition(nowValue, changePower, isAdd); }));
                         }
                     }
-#if UNITY_EDITOR
-                    Debug.Log($"count {i} changeValue {changeValue} nowValue {nowValue} isAdd {isAdd}  basePower {basePower} changePower {changePower} nowPower {nowPower} rollTime {rollTime}");
-#endif
+// #if UNITY_EDITOR
+//                     Debug.Log($"count {i} changeValue {changeValue} nowValue {nowValue} isAdd {isAdd}  basePower {basePower} changePower {changePower} nowPower {nowPower} rollTime {rollTime}");
+// #endif
                 }
                 sequence.Join(textChangeSequence);
 
diff --git a/Main/System/UIBase/UIBase.cs b/Main/System/UIBase/UIBase.cs
index 5266c16..e81b824 100644
--- a/Main/System/UIBase/UIBase.cs
+++ b/Main/System/UIBase/UIBase.cs
@@ -121,7 +121,7 @@
         }
         catch (Exception e)
         {
-            Debug.LogError($"{uiName}鐣岄潰鐨処nitComponentInternal鎶ラ敊: {e.Message}");
+            Debug.LogError($"{uiName}鐣岄潰鐨処nitComponentInternal鎶ラ敊: {e.StackTrace}");
         }
 
         try
@@ -130,7 +130,7 @@
         }
         catch (Exception e)
         {
-            Debug.LogError($"{uiName}鐣岄潰鐨処nitComponent鎶ラ敊: {e.Message}");
+            Debug.LogError($"{uiName}鐣岄潰鐨処nitComponent鎶ラ敊: {e.StackTrace}");
         }
 
         // 淇濆瓨鍘熷鍊肩敤浜庡姩鐢�
@@ -285,7 +285,7 @@
         }
         catch (Exception e)
         {
-            Debug.LogError($"{uiName}鐣岄潰鐨凮nPreOpen鎶ラ敊: {e.Message}");
+            Debug.LogError($"{uiName}鐣岄潰鐨凮nPreOpen鎶ラ敊: {e.StackTrace}");
         }
 
         StopCurrentAnimation();
@@ -306,7 +306,7 @@
         }
         catch (Exception e)
         {
-            Debug.LogError($"{uiName}鐣岄潰鐨凮nOpen鎶ラ敊: {e.Message}");
+            Debug.LogError($"{uiName}鐣岄潰鐨凮nOpen鎶ラ敊: {e.StackTrace}");
         }
 
         ApplyClickEmptySpaceClose();
@@ -319,7 +319,7 @@
             }
             catch (Exception e)
             {
-                Debug.LogError($"{uiName}鐣岄潰鐨凬extFrameAfterOpen鎶ラ敊: {e.Message}");
+                Debug.LogError($"{uiName}鐣岄潰鐨凬extFrameAfterOpen鎶ラ敊: {e.StackTrace}");
             }
         });
     }
@@ -344,7 +344,7 @@
         }
         catch (Exception e)
         {
-            Debug.LogError($"{uiName}鐣岄潰鐨凮nPreClose鎶ラ敊: {e.Message}");
+            Debug.LogError($"{uiName}鐣岄潰鐨凮nPreClose鎶ラ敊: {e.StackTrace}");
         }
 
         StopCurrentAnimation();
@@ -365,7 +365,7 @@
         }
         catch (Exception e)
         {
-            Debug.LogError($"{uiName}鐣岄潰鐨凮nClose鎶ラ敊: {e.Message}");
+            Debug.LogError($"{uiName}鐣岄潰鐨凮nClose鎶ラ敊: {e.StackTrace}");
         }
 
         if (closeAnimationType == UIAnimationType.None)
@@ -376,7 +376,7 @@
             }
             catch (Exception e)
             {
-                Debug.LogError($"{uiName}鐣岄潰鐨凜ompleteClose鎶ラ敊: {e.Message}");
+                Debug.LogError($"{uiName}鐣岄潰鐨凜ompleteClose鎶ラ敊: {e.StackTrace}");
             }
         }
         // 鍚﹀垯鍦ㄥ姩鐢诲畬鎴愬悗绂佺敤娓告垙瀵硅薄锛堝湪PlayCloseAnimation涓鐞嗭級
@@ -628,7 +628,7 @@
         }
         catch (System.Exception e)
         {
-            Debug.LogError($"鎾斁鎵撳紑鍔ㄧ敾鏃跺嚭閿�: {e.Message}");
+            Debug.LogError($"鎾斁鎵撳紑鍔ㄧ敾鏃跺嚭閿�: {e.StackTrace}");
 
             // 鍑洪敊鏃剁‘淇漊I鍙骞跺彲浜や簰
             if (canvasGroup != null)
@@ -751,7 +751,7 @@
                     }
                     catch (Exception e)
                     {
-                        Debug.LogError($"{uiName}鐣岄潰鐨凜ompleteClose鎶ラ敊: {e.Message}");
+                        Debug.LogError($"{uiName}鐣岄潰鐨凜ompleteClose鎶ラ敊: {e.StackTrace}");
                     }
                 }
             });
@@ -760,7 +760,7 @@
         }
         catch (System.Exception e)
         {
-            Debug.LogError($"鎾斁鍏抽棴鍔ㄧ敾鏃跺嚭閿�: {e.Message}");
+            Debug.LogError($"鎾斁鍏抽棴鍔ㄧ敾鏃跺嚭閿�: {e.StackTrace}");
             
             // 鍑洪敊鏃剁洿鎺ュ畬鎴愬叧闂�
             isAnimating = false;
diff --git a/Main/Utility/JaceCalculator.cs b/Main/Utility/JaceCalculator.cs
new file mode 100644
index 0000000..545b595
--- /dev/null
+++ b/Main/Utility/JaceCalculator.cs
@@ -0,0 +1,36 @@
+using Jace;
+using System;
+using System.Collections.Generic;
+
+public static class JaceCalculator
+{
+    private static readonly CalculationEngine Engine = new CalculationEngine();
+
+    /// <summary>
+    /// 瑙f瀽骞惰绠楁暟瀛﹁〃杈惧紡
+    /// </summary>
+    /// <param name="formula">鏁板琛ㄨ揪寮忓瓧绗︿覆</param>
+    /// <param name="variables">鍙橀噺瀛楀吀</param>
+    /// <returns>璁$畻缁撴灉</returns>
+    public static double Calculate(string formula, Dictionary<string, double> variables)
+    {
+        if (string.IsNullOrEmpty(formula))
+        {
+            throw new ArgumentException("Formula cannot be null or empty.");
+        }
+
+        if (variables == null || variables.Count == 0)
+        {
+            throw new ArgumentException("Variables dictionary cannot be null or empty.");
+        }
+
+        try
+        {
+            return Engine.Calculate(formula, variables);
+        }
+        catch (Exception ex)
+        {
+            throw new InvalidOperationException($"Failed to calculate formula: {ex.Message}", ex);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Hero/HeroInfo.Quality.cs.meta b/Main/Utility/JaceCalculator.cs.meta
similarity index 83%
copy from Main/System/Hero/HeroInfo.Quality.cs.meta
copy to Main/Utility/JaceCalculator.cs.meta
index e37f87e..50a6ca1 100644
--- a/Main/System/Hero/HeroInfo.Quality.cs.meta
+++ b/Main/Utility/JaceCalculator.cs.meta
@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 8540c5ed5104dfe40b0bff4aaf9a080b
+guid: ebee4528e9596394886463d65c214b23
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

--
Gitblit v1.8.0