hch
2025-08-18 5c5a5cc66227439be7a7b61da5d1ff68cf187ac3
117 【武将】武将系统 - 战力计算
35个文件已修改
11个文件已删除
57 文件已复制
54个文件已添加
2 文件已重命名
6511 ■■■■ 已修改文件
Main/Common/Jace.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/AstBuilder.cs 372 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/AstBuilder.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/CalculationEngine.cs 483 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/CalculationEngine.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/DataType.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/DataType.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ConstantInfo.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ConstantInfo.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ConstantRegistry.cs 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ConstantRegistry.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/DynamicCompiler.cs 327 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/DynamicCompiler.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ExecutionMode.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ExecutionMode.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/FormulaBuilder.cs 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/FormulaBuilder.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/FunctionInfo.cs 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/FunctionInfo.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/FunctionRegistry.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/FunctionRegistry.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/IConstantRegistry.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/IConstantRegistry.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/IExecutor.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/IExecutor.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/IFunctionRegistry.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/IFunctionRegistry.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/Interpreter.cs 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/Interpreter.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ParameterInfo.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Execution/ParameterInfo.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/FormulaContext.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/FormulaContext.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/JaceOptions.cs 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/JaceOptions.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Addition.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Addition.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/And.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/And.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Constant.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Constant.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Division.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Division.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Equal.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Equal.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Exponentiation.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Exponentiation.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Function.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Function.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/GreaterOrEqualThan.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/GreaterThan.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/GreaterThan.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/LessOrEqualThan.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/LessOrEqualThan.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/LessThan.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/LessThan.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Modulo.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Modulo.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Multiplication.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Multiplication.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/NotEqual.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/NotEqual.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Operation.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Operation.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Or.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Or.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Subtraction.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Subtraction.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/UnaryMinus.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/UnaryMinus.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Variable.cs 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Operations/Variable.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Optimizer.cs 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Optimizer.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/ParseException.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/ParseException.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer/Token.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer/Token.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer/TokenReader.cs 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer/TokenReader.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer/TokenType.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Tokenizer/TokenType.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/EngineUtil.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/EngineUtil.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/FuncAdapter.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/FuncAdapter.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/MathExtended.cs 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/MathExtended.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/MathUtil.cs 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/MathUtil.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/MemoryCache.cs 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/MemoryCache.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/TypeExtensions.cs 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/Util/TypeExtensions.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/VariableNotDefinedException.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace/VariableNotDefinedException.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/UIEffectPlayer.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FightPowerRatioConfig.cs 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FightPowerRatioConfig.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/HeroConfig.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/PlayerLVConfig.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/HeroConfig.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/HeroConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/HeroTalentConfig.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/PlayerPropertyConfig.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/GameEngine/Common/Equation.cs 455 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/GameEngine/Common/Equation.cs.meta 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H04_Scene/DTC0403_tagPlayerLoginLoadOK.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB1_Role/DTCB122_tagSCHeroInfo.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/CustomizedGift/CustomizedGiftModel.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Equip/EquipExchangeCell.cs 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Equip/EquipExchangeWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Equip/EquipModel.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroAttrType.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Awake.cs 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Break.cs 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Equip.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Equip.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Fetter.cs 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.InheritPer.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.InheritPer.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Level.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Level.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Lineup.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Properties.cs 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Quality.cs 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Star.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Star.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Talent.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.cs 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroManager.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationCell.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroLVBreakWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroPosWin.cs 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroTrainWin.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.Collect.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.Collect.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.OnTeam.cs 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.Reborn.cs 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.Reborn.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.cs 295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/KnapSack/Logic/ItemLogicUtility.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/FightPowerManager.cs 627 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/OpenServerActivity/OperationTimeHepler.cs 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Store/StoreModel.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamBase.cs 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamHero.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Tip/PowerAddWin.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/UIBase/UIBase.cs 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/JaceCalculator.cs 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/JaceCalculator.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Common/Jace.meta
copy from Main/Core/GameEngine/Common.meta copy to Main/Common/Jace.meta
File was copied from Main/Core/GameEngine/Common.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
guid: ca353b9337f7a0749a543c104493ccc5
folderAsset: yes
DefaultImporter:
  externalObjects: {}
Main/Common/Jace/AstBuilder.cs
New file
@@ -0,0 +1,372 @@
using 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;
        }
    }
}
Main/Common/Jace/AstBuilder.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/AstBuilder.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: de824ec81c42eae4888376c8a458a540
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/CalculationEngine.cs
New file
@@ -0,0 +1,483 @@
using 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");
            }
        }
    }
}
Main/Common/Jace/CalculationEngine.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/CalculationEngine.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 7605496412e5ed546a6ab8ccba648580
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/DataType.cs
New file
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Jace
{
    public enum DataType
    {
        Integer,
        FloatingPoint
    }
}
Main/Common/Jace/DataType.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/DataType.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: f30cb87ce5accbe4ebcbc950c4eaae28
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution.meta
File was renamed from Main/Core/GameEngine/Common.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
guid: 9d45a5ae0f6671f4783425dac0689ce4
folderAsset: yes
DefaultImporter:
  externalObjects: {}
Main/Common/Jace/Execution/ConstantInfo.cs
New file
@@ -0,0 +1,23 @@
using 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; }
    }
}
Main/Common/Jace/Execution/ConstantInfo.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/ConstantInfo.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 80e7b1aba7b9c2245b2b872f018c07d6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/ConstantRegistry.cs
New file
@@ -0,0 +1,79 @@
using 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();
        }
    }
}
Main/Common/Jace/Execution/ConstantRegistry.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/ConstantRegistry.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 4c5b0410c8fc58445a69f737f244707e
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/DynamicCompiler.cs
New file
@@ -0,0 +1,327 @@
using 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.");
            }
        }
    }
}
Main/Common/Jace/Execution/DynamicCompiler.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/DynamicCompiler.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: c058c0ded31899643b05723eafcafc40
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/ExecutionMode.cs
New file
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Jace.Execution
{
    public enum ExecutionMode
    {
        Interpreted,
        Compiled
    }
}
Main/Common/Jace/Execution/ExecutionMode.cs.meta
File was renamed from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 0d9522fc6bc25a644b4074177b045fc6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/FormulaBuilder.cs
New file
@@ -0,0 +1,131 @@
using 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);
            });
        }
    }
}
Main/Common/Jace/Execution/FormulaBuilder.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/FormulaBuilder.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: b422f2fc8278c544db048c826ec43f40
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/FunctionInfo.cs
New file
@@ -0,0 +1,32 @@
using 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; }
    }
}
Main/Common/Jace/Execution/FunctionInfo.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/FunctionInfo.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 68afcf59c23204e4f83b55a9555fa566
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/FunctionRegistry.cs
New file
@@ -0,0 +1,119 @@
using 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();
        }
    }
}
Main/Common/Jace/Execution/FunctionRegistry.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/FunctionRegistry.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: a23e9d8bb99397f4b932d1802e5e1a30
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/IConstantRegistry.cs
New file
@@ -0,0 +1,15 @@
using 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);
    }
}
Main/Common/Jace/Execution/IConstantRegistry.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/IConstantRegistry.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: ac1d9141e70689a4ea802b6b9d653707
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/IExecutor.cs
New file
@@ -0,0 +1,16 @@
using 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);
    }
}
Main/Common/Jace/Execution/IExecutor.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/IExecutor.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 5cc438b6f6efc8143b63e3107ce1eb9c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/IFunctionRegistry.cs
New file
@@ -0,0 +1,13 @@
using 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);
    }
}
Main/Common/Jace/Execution/IFunctionRegistry.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/IFunctionRegistry.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 5c0e119822c96154a90ac4a9e37de0d9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/Interpreter.cs
New file
@@ -0,0 +1,252 @@
using 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());
            }
        }
    }
}
Main/Common/Jace/Execution/Interpreter.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/Interpreter.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 880be476462899f4aa9a1e05d9b1e50f
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Execution/ParameterInfo.cs
New file
@@ -0,0 +1,14 @@
using 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;
    }
}
Main/Common/Jace/Execution/ParameterInfo.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Execution/ParameterInfo.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: f118a37bdba42b8459abfd0f2cc2179b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/FormulaContext.cs
New file
@@ -0,0 +1,25 @@
using 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; }
    }
}
Main/Common/Jace/FormulaContext.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/FormulaContext.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 766328a00fcb8a545bd00380387bbc50
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/JaceOptions.cs
New file
@@ -0,0 +1,87 @@
using 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; }
    }
}
Main/Common/Jace/JaceOptions.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/JaceOptions.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: da15a14f53646d442988e85bc2545dc8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations.meta
copy from Main/Core/GameEngine/Common.meta copy to Main/Common/Jace/Operations.meta
File was copied from Main/Core/GameEngine/Common.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
guid: d825ff71751c9124885a7751c284959d
folderAsset: yes
DefaultImporter:
  externalObjects: {}
Main/Common/Jace/Operations/Addition.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Addition.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Addition.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 340301d5fb1dce54ab7dec7873ceea4d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/And.cs
New file
@@ -0,0 +1,19 @@
using 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; }
    }
}
Main/Common/Jace/Operations/And.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/And.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: c88e04049f2269a498cfedbd3fc9576c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Constant.cs
New file
@@ -0,0 +1,48 @@
using 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)
        {
        }
    }
}
Main/Common/Jace/Operations/Constant.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Constant.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 65575909546ce144e83dd420135e0a01
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Division.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Division.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Division.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 353c124902c7984429815571216959bf
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Equal.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Equal.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Equal.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 87cffdc974ef87a4ab8c92b5321424c6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Exponentiation.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Exponentiation.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Exponentiation.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 2491ad50c77e631489551371d36bab46
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Function.cs
New file
@@ -0,0 +1,33 @@
using 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;
            }
        }
    }
}
Main/Common/Jace/Operations/Function.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Function.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 8cea584356c80194eac8e7f5905d7764
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/GreaterOrEqualThan.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/GreaterOrEqualThan.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: a5f7058c1a38e5e4da81a637c5ca0fb8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/GreaterThan.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/GreaterThan.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/GreaterThan.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: aa4d6a9b5c1549743bd305b4e87c56b8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/LessOrEqualThan.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/LessOrEqualThan.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/LessOrEqualThan.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 22813b990f9481c41b94640a98d29719
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/LessThan.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/LessThan.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/LessThan.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 259c10599a2e2a74b86a83682dc05cc1
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Modulo.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Modulo.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Modulo.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 20b05091ee3aab546b21bcbde58b826c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Multiplication.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Multiplication.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Multiplication.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 6156d0f899a0b484383f5a552a76e328
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/NotEqual.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/NotEqual.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/NotEqual.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: b52548570632a8a449c662af67df863b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Operation.cs
New file
@@ -0,0 +1,23 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Operation.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Operation.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: f31df8e25e70f36488d2dc2f14ba66d7
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Or.cs
New file
@@ -0,0 +1,19 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Or.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Or.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: c39d4b13c7751e34587665bc39443e2a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Subtraction.cs
New file
@@ -0,0 +1,20 @@
using 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; }
    }
}
Main/Common/Jace/Operations/Subtraction.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Subtraction.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: ddba1b68f54ac7841ac6b91dfb55a185
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/UnaryMinus.cs
New file
@@ -0,0 +1,18 @@
using 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; }
    }
}
Main/Common/Jace/Operations/UnaryMinus.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/UnaryMinus.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 350a4f213d50975468899e32708859b1
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Operations/Variable.cs
New file
@@ -0,0 +1,37 @@
using 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();
        }
    }
}
Main/Common/Jace/Operations/Variable.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Operations/Variable.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 34fb9551f3d90cf418e9557742735a18
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Optimizer.cs
New file
@@ -0,0 +1,76 @@
using 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;
            }
        }
    }
}
Main/Common/Jace/Optimizer.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Optimizer.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 3d95e6f9d0000c54c9abd40e1f81ab4a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/ParseException.cs
New file
@@ -0,0 +1,19 @@
using 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)
        {
        }
    }
}
Main/Common/Jace/ParseException.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/ParseException.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: df2bb315758f36a429552a2b28b667e2
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Tokenizer.meta
copy from Main/Core/GameEngine/Common.meta copy to Main/Common/Jace/Tokenizer.meta
File was copied from Main/Core/GameEngine/Common.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
guid: 2b67a41b280e4f143861b982f7dbc630
folderAsset: yes
DefaultImporter:
  externalObjects: {}
Main/Common/Jace/Tokenizer/Token.cs
New file
@@ -0,0 +1,33 @@
using 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;
    }
}
Main/Common/Jace/Tokenizer/Token.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Tokenizer/Token.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 9fa03dbd0043c2b41a256b11a7669fc9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Tokenizer/TokenReader.cs
New file
@@ -0,0 +1,255 @@
using 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';
        }
    }
}
Main/Common/Jace/Tokenizer/TokenReader.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Tokenizer/TokenReader.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: e7f38551c1077ee4896e7b1d5aabd1a2
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Tokenizer/TokenType.cs
New file
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Jace.Tokenizer
{
    public enum TokenType
    {
        Integer,
        FloatingPoint,
        Text,
        Operation,
        LeftBracket,
        RightBracket,
        ArgumentSeparator
    }
}
Main/Common/Jace/Tokenizer/TokenType.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Tokenizer/TokenType.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: ca43eadac5024d040a3f89d8c15e0967
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Util.meta
copy from Main/Core/GameEngine/Common.meta copy to Main/Common/Jace/Util.meta
File was copied from Main/Core/GameEngine/Common.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6b68b2e87cfa2fc48aff8e7f40a5c555
guid: 78f08781a2381ff409b92d3767b449bc
folderAsset: yes
DefaultImporter:
  externalObjects: {}
Main/Common/Jace/Util/EngineUtil.cs
New file
@@ -0,0 +1,46 @@
using 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();
        }
    }
}
Main/Common/Jace/Util/EngineUtil.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Util/EngineUtil.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 3725a3ccea7f46f4bb6980cdd7823745
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Util/FuncAdapter.cs
New file
@@ -0,0 +1,126 @@
using 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;
            }
        }
    }
}
Main/Common/Jace/Util/FuncAdapter.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Util/FuncAdapter.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 878d5799cfe90cb4dbf7aebc38c3e9ae
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Util/MathExtended.cs
New file
@@ -0,0 +1,81 @@
using 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);
        }
    }
}
Main/Common/Jace/Util/MathExtended.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Util/MathExtended.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 9fd567b7004de6e41ae2720dd0b3c1ce
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Util/MathUtil.cs
New file
@@ -0,0 +1,30 @@
using 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);
        }
    }
}
Main/Common/Jace/Util/MathUtil.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Util/MathUtil.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: b51d1dc4b0eab6e4c9cfb6bdec1d86ea
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Util/MemoryCache.cs
New file
@@ -0,0 +1,165 @@
using 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);
            }
        }
    }
}
Main/Common/Jace/Util/MemoryCache.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Util/MemoryCache.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: da84d8440a462ad44b25f3e1ae8d0c36
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/Util/TypeExtensions.cs
New file
@@ -0,0 +1,43 @@
using 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.");
        }
    }
}
Main/Common/Jace/Util/TypeExtensions.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/Util/TypeExtensions.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: adedcd02cabd3f94d8be60ce8b3b4a1e
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Common/Jace/VariableNotDefinedException.cs
New file
@@ -0,0 +1,23 @@
using 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)
        {
        }
    }
}
Main/Common/Jace/VariableNotDefinedException.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Common/Jace/VariableNotDefinedException.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: b54823a4c30c0db4992f01e0a5b4bcb6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/Component/UI/Effect/UIEffectPlayer.cs
@@ -47,6 +47,7 @@
        if (!isInit)
        {
            InitComponent(showLog);
            //effeid 为0也初始化成功,避免重复处理,在变更effectid时会重新初始化
            isInit = true;
        }
        else
@@ -56,7 +57,8 @@
            {
                this.gameObject.SetActive(true);
            }
            if (effectConfig.isSpine != 0)
            //防范effeid 为0
            if (effectConfig != null && effectConfig.isSpine != 0)
            {
                PlayerTheSpineAnim();
            }
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 字典
Main/Config/Configs/FightPowerRatioConfig.cs
New file
@@ -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);
        }
    }
}
Main/Config/Configs/FightPowerRatioConfig.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Config/Configs/FightPowerRatioConfig.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 600fc786340faaf44aed165f5642df6d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
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("["))
            {
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)
        {
Main/Config/PartialConfigs/HeroConfig.cs
File was deleted
Main/Config/PartialConfigs/HeroConfig.cs.meta
File was deleted
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()
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())
Main/Core/GameEngine/Common/Equation.cs
File was deleted
Main/Core/GameEngine/Common/Equation.cs.meta
File was deleted
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();
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);
    }
}
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)
        {
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);
            }
        }
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]))
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;    //是否换上了新装备且分解了 装备索引
    public event Action<bool, int> OnEquipOPResultAction;    //是否换上了新装备且分解了 装备索引
    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);
    }
}
Main/System/Hero/HeroAttrType.cs
@@ -2,12 +2,12 @@
public enum HeroAttrType
{
    // 生命
    hp,
    // 攻击力
    attack,
    attack = 6,
    // 防御力
    defense,
    defense = 7,
    // 生命
    hp = 8,
    //眩晕概率
    stunRate,
    //暴击概率
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;
    }
}
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>();   //潜能属性id:潜能值
    //计算潜能属性
    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;
    }
}
Main/System/Hero/HeroInfo.Equip.cs
File was deleted
Main/System/Hero/HeroInfo.Equip.cs.meta
File was deleted
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>(); //羁绊属性ID:属性值
    //不同阵容羁绊属性不同,实时计算,与其他缓存的不同
    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>();
Main/System/Hero/HeroInfo.InheritPer.cs
New file
@@ -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;
    }
}
Main/System/Hero/HeroInfo.InheritPer.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/System/Hero/HeroInfo.InheritPer.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: 43bccdd0f6575254591fc49d33072614
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/Hero/HeroInfo.Level.cs
File was deleted
Main/System/Hero/HeroInfo.Level.cs.meta
File was deleted
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;
    }
}
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的,可同时触发多个属性且多次触发,触发几率不会随着目标数量多少而改变,只会根据同一目标的触发次数增多而降低。
    // 即在一回合内,同一个目标反击、连击、击晕、暴击、闪避可以同时并多次触发,触发几率随机触发次数的增加而降低,每个属性开公式来(参数:触发几率、抵抗几率、已触发次数)
    // 优先判断是否命中,必中后同时判断此次攻击是否连击、击晕、暴击生效
    // 反击时必命中目标
    // 武将属性需时时计算,根据技能、BUFF、装备等属性来源改变而改变
    // 基础属性
    // 生命
    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;
    }
}
Main/System/Hero/HeroInfo.Quality.cs
File was deleted
Main/System/Hero/HeroInfo.Star.cs
File was deleted
Main/System/Hero/HeroInfo.Star.cs.meta
File was deleted
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>();   //属性ID : 天赋属性值
                                                                       // 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;
    }
}
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>();  //技能类型ID:  最高技能ID
    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);
    }
    //是否上x阵 81 # 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...]
    //是否上x阵 服务端队伍
    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;
    }
}
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)
            {
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);
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)
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));
    }
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();
        }
    }
}
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)
            {
Main/System/HeroUI/HeroUIManager.Collect.cs
New file
@@ -0,0 +1,46 @@
using 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
}
Main/System/HeroUI/HeroUIManager.Collect.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/System/HeroUI/HeroUIManager.Collect.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: fde2bfe0f6468dc49951c562fd445d51
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/HeroUI/HeroUIManager.OnTeam.cs
New file
@@ -0,0 +1,311 @@
using 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);
        // 排序规则:上阵>武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        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);
    }
    //在不同页签下选AttackType 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()
    {
        //推荐阵容的算法逻辑
        //自动选择优先级:武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        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
}
Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/System/HeroUI/HeroUIManager.OnTeam.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: c8e0c0a46a38f1e49856253add1794e3
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/HeroUI/HeroUIManager.Reborn.cs
New file
@@ -0,0 +1,83 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public partial class HeroUIManager : GameSystemManager<HeroUIManager>
{
    #region 重生 遣散
    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
}
Main/System/HeroUI/HeroUIManager.Reborn.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/System/HeroUI/HeroUIManager.Reborn.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: bf68f7332b968f14491059586ac32df5
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
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; //选中的武将id
    #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;
        }
        // 排序规则:上阵>武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        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);
    }
    //在不同页签下选AttackType 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()
    {
        //推荐阵容的算法逻辑
        //自动选择优先级:武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        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 重生 遣散
    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
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>
Main/System/Main/FightPowerManager.cs
@@ -1,236 +1,479 @@
using System.Collections;
using 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代表不替换计算</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 += $"属性ID {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($"战力:武将ID {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($"战力:武将ID {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($"战力:武将ID {hero.heroId} 技能战力 {skillPower} 技能参数 {JsonMapper.ToJson(fightPowerVariables)}");
        Debug.Log($"战力:武将ID {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
}
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,
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()
    {
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));
    }
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;
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);
Main/System/UIBase/UIBase.cs
@@ -121,7 +121,7 @@
        }
        catch (Exception e)
        {
            Debug.LogError($"{uiName}界面的InitComponentInternal报错: {e.Message}");
            Debug.LogError($"{uiName}界面的InitComponentInternal报错: {e.StackTrace}");
        }
        try
@@ -130,7 +130,7 @@
        }
        catch (Exception e)
        {
            Debug.LogError($"{uiName}界面的InitComponent报错: {e.Message}");
            Debug.LogError($"{uiName}界面的InitComponent报错: {e.StackTrace}");
        }
        // 保存原始值用于动画
@@ -285,7 +285,7 @@
        }
        catch (Exception e)
        {
            Debug.LogError($"{uiName}界面的OnPreOpen报错: {e.Message}");
            Debug.LogError($"{uiName}界面的OnPreOpen报错: {e.StackTrace}");
        }
        StopCurrentAnimation();
@@ -306,7 +306,7 @@
        }
        catch (Exception e)
        {
            Debug.LogError($"{uiName}界面的OnOpen报错: {e.Message}");
            Debug.LogError($"{uiName}界面的OnOpen报错: {e.StackTrace}");
        }
        ApplyClickEmptySpaceClose();
@@ -319,7 +319,7 @@
            }
            catch (Exception e)
            {
                Debug.LogError($"{uiName}界面的NextFrameAfterOpen报错: {e.Message}");
                Debug.LogError($"{uiName}界面的NextFrameAfterOpen报错: {e.StackTrace}");
            }
        });
    }
@@ -344,7 +344,7 @@
        }
        catch (Exception e)
        {
            Debug.LogError($"{uiName}界面的OnPreClose报错: {e.Message}");
            Debug.LogError($"{uiName}界面的OnPreClose报错: {e.StackTrace}");
        }
        StopCurrentAnimation();
@@ -365,7 +365,7 @@
        }
        catch (Exception e)
        {
            Debug.LogError($"{uiName}界面的OnClose报错: {e.Message}");
            Debug.LogError($"{uiName}界面的OnClose报错: {e.StackTrace}");
        }
        if (closeAnimationType == UIAnimationType.None)
@@ -376,7 +376,7 @@
            }
            catch (Exception e)
            {
                Debug.LogError($"{uiName}界面的CompleteClose报错: {e.Message}");
                Debug.LogError($"{uiName}界面的CompleteClose报错: {e.StackTrace}");
            }
        }
        // 否则在动画完成后禁用游戏对象(在PlayCloseAnimation中处理)
@@ -628,7 +628,7 @@
        }
        catch (System.Exception e)
        {
            Debug.LogError($"播放打开动画时出错: {e.Message}");
            Debug.LogError($"播放打开动画时出错: {e.StackTrace}");
            // 出错时确保UI可见并可交互
            if (canvasGroup != null)
@@ -751,7 +751,7 @@
                    }
                    catch (Exception e)
                    {
                        Debug.LogError($"{uiName}界面的CompleteClose报错: {e.Message}");
                        Debug.LogError($"{uiName}界面的CompleteClose报错: {e.StackTrace}");
                    }
                }
            });
@@ -760,7 +760,7 @@
        }
        catch (System.Exception e)
        {
            Debug.LogError($"播放关闭动画时出错: {e.Message}");
            Debug.LogError($"播放关闭动画时出错: {e.StackTrace}");
            
            // 出错时直接完成关闭
            isAnimating = false;
Main/Utility/JaceCalculator.cs
New file
@@ -0,0 +1,36 @@
using Jace;
using System;
using System.Collections.Generic;
public static class JaceCalculator
{
    private static readonly CalculationEngine Engine = new CalculationEngine();
    /// <summary>
    /// 解析并计算数学表达式
    /// </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);
        }
    }
}
Main/Utility/JaceCalculator.cs.meta
copy from Main/System/Hero/HeroInfo.Quality.cs.meta copy to Main/Utility/JaceCalculator.cs.meta
File was copied from Main/System/Hero/HeroInfo.Quality.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 8540c5ed5104dfe40b0bff4aaf9a080b
guid: ebee4528e9596394886463d65c214b23
MonoImporter:
  externalObjects: {}
  serializedVersion: 2