├── calculator_of_Onmyoji ├── __init__.py ├── server.conf ├── static │ ├── favicon.ico │ ├── img │ │ ├── header.37ff70af.png │ │ └── background.c3af5934.jpg │ ├── fonts │ │ ├── element-icons.6f0a7632.ttf │ │ └── element-icons.2fad952a.woff │ ├── css │ │ └── app.5b01de06.css │ └── js │ │ ├── app.31814493.js │ │ └── app.5c0c5dc2.js ├── templates │ └── index.html ├── mac_wrapper.py ├── convert_json2xls.py ├── cm_server.py ├── pull_mitama.py ├── data_format.py ├── load_data.py ├── write_data.py ├── result_combination.py ├── cal_mitama.py └── algorithm.py ├── requirements.txt ├── example ├── data_Template.xls ├── Preset.psf └── data_Template.json ├── setup.py ├── setup.cfg ├── win_compile.txt ├── doc └── API.md ├── .gitignore ├── README.md └── LICENSE /calculator_of_Onmyoji/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/server.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | host = 127.0.0.1 3 | port = 2019 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | xlutils 2 | xlrd 3 | xlwt 4 | requests 5 | tqdm 6 | flask 7 | flask-cors 8 | webob 9 | -------------------------------------------------------------------------------- /example/data_Template.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiajunsu/calculator_of_Onmyoji/HEAD/example/data_Template.xls -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | setup_requires=['pbr>=2.0.0'], 5 | pbr=True) 6 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiajunsu/calculator_of_Onmyoji/HEAD/calculator_of_Onmyoji/static/favicon.ico -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/img/header.37ff70af.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiajunsu/calculator_of_Onmyoji/HEAD/calculator_of_Onmyoji/static/img/header.37ff70af.png -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/img/background.c3af5934.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiajunsu/calculator_of_Onmyoji/HEAD/calculator_of_Onmyoji/static/img/background.c3af5934.jpg -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/fonts/element-icons.6f0a7632.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiajunsu/calculator_of_Onmyoji/HEAD/calculator_of_Onmyoji/static/fonts/element-icons.6f0a7632.ttf -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/fonts/element-icons.2fad952a.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jiajunsu/calculator_of_Onmyoji/HEAD/calculator_of_Onmyoji/static/fonts/element-icons.2fad952a.woff -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = calculator_of_Onmyoji 3 | author = jiajunsu 4 | home-page = https://github.com/jiajunsu/calculator_of_Onmyoji 5 | 6 | [entry_points] 7 | console_scripts = 8 | cal_mitama = calculator_of_Onmyoji.cal_mitama:main 9 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/templates/index.html: -------------------------------------------------------------------------------- 1 | 《阴阳师》御魂计算器
2 | -------------------------------------------------------------------------------- /example/Preset.psf: -------------------------------------------------------------------------------- 1 | 吃星暴伤针女大天狗 2 | -M 针女,4 -P 暴击,90 -UP 暴击,95.速度,18 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3135.6,150,0 3 | 超星暴伤针女大天狗 4 | -M 针女,4 -P 暴击,90.速度,18 -UP 暴击,95 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3135.6,150,0 5 | 超星荒骷髅茨木 6 | -M 破势,4.荒骷髅,2 -P 暴击,90.速度,16 -UP 暴击,95 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3216,150,17120 7 | 超星普通茨木 8 | -M 破势,4 -P 暴击,90.速度,16 -UP 暴击,95 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3216,150,20510 9 | 吃星荒骷髅茨木 10 | -M 破势,4.荒骷髅,2 -P 暴击,90 -UP 暴击,95.速度,16 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3216,150,13170 11 | 吃星普通茨木 12 | -M 破势,4 -P 暴击,90 -UP 暴击,95.速度,16 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3216,150,15780 13 | 暴伤玉藻前 14 | -M 破势,4 -P 暴击,88 -UP 暴击,95 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89 -AO True -DL 3350,160,0 15 | 正堆+反堆暴伤针女 16 | -M 针女,4 -P 暴击,90.暴击伤害,80 -UP 暴击,95 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击伤害,89.暴击,55 -AO True 17 | 散件蜃气楼生生暴书翁 18 | -M 蜃气楼,2 -P 暴击,92 -UP 暴击,95 -2P 生命加成,55 -4P 生命加成,55 -6P 暴击,55 -H 11393,150,0 -AS False -------------------------------------------------------------------------------- /calculator_of_Onmyoji/mac_wrapper.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import threading 3 | import sys 4 | import argparse 5 | from calculator_of_Onmyoji import cm_server 6 | 7 | 8 | def main(): 9 | parser = argparse.ArgumentParser() 10 | parser.add_argument("data_path", 11 | type=str, 12 | help='御魂数据所在文件夹') 13 | 14 | parser.add_argument("-P", "--port", 15 | type=str, 16 | default='2019', 17 | help='服务器端口') 18 | 19 | parser.add_argument("-H", "--host", 20 | type=str, 21 | default='localhost', 22 | help='服务器地址') 23 | 24 | args = parser.parse_args() 25 | 26 | cm_server.work_path = args.data_path 27 | 28 | t = threading.Thread(target=cm_server.open_browser, 29 | args=(args.host, args.port)) 30 | t.start() 31 | 32 | cm_server.app.run(host=args.host, port=args.port, threaded=True) 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /win_compile.txt: -------------------------------------------------------------------------------- 1 | Destination platform: win 2 | ------------------------------- 3 | 4 | ### Requirments: 5 | > python >= 3.6 6 | 7 | ### Note: 8 | 1.Must run setup.py install before compile 9 | 10 | 2.Update UI files 11 | ```sed -i "s/\/\/cdn.suisuijiang.com\/calculator\//\"{{ url_for('static',filename='/g" calculator_of_Onmyoji/static/index.html 12 | sed -i "s/\.css/\.css')}}\"/g" calculator_of_Onmyoji/static/index.html 13 | sed -i "s/\.js/\.js')}}\"/g" calculator_of_Onmyoji/static/index.html 14 | sed -i "s/\.ico/\.ico')}}\"/g" calculator_of_Onmyoji/static/index.html 15 | ``` 16 | ```cp calculator_of_Onmyoji/templates dist/ -r; cp calculator_of_Onmyoji/static dist -r;cp calculator_of_Onmyoji/server.conf dist``` 17 | 18 | 19 | ### Compile: 20 | 21 | ```pyinstaller.exe --clean --win-private-assemblies -F calculator_of_Onmyoji/cal_mitama.py``` 22 | 23 | ```pyinstaller.exe --clean --win-private-assemblies -F calculator_of_Onmyoji/pull_mitama.py``` 24 | 25 | ```pyinstaller.exe --clean --win-private-assemblies -F calculator_of_Onmyoji/convert_json2xls.py``` 26 | 27 | ```pyinstaller.exe --clean --win-private-assemblies -F calculator_of_Onmyoji/result_combination.py``` 28 | 29 | ```pyinstaller.exe --clean --win-private-assemblies -F calculator_of_Onmyoji/cm_server.py -i calculator_of_Onmyoji/static/favicon.ico``` 30 | -------------------------------------------------------------------------------- /doc/API.md: -------------------------------------------------------------------------------- 1 | # cm\_server API 2 | 3 | ## / GET 4 | 5 | Return index.html 6 | _ _ _ 7 | 8 | ## /calculate POST 9 | 10 | ### Header 11 | | Key | Value | 12 | |--------|--------| 13 | |Content-Type|application/json| 14 | 15 | 16 | ### Body 17 | | Key | Type | Description | 18 | |--------|--------|--------| 19 | |src_filename|string|filename of source_data, file must be in same dir of cm_server| 20 | 21 | For other params, see [Readme](https://github.com/jiajunsu/calculator_of_Onmyoji/blob/master/README.md#usage-of-calculator) 22 | 23 | ```NOTE: replace '-' to '_' in keys' name``` 24 | 25 | ### Response 26 | | Code | Body_key | Type | Description | 27 | |--------|--------|--------|--------| 28 | | 200 | result_num | int | number of combinations got from calculator | 29 | | - | output_file | string | output file path | 30 | | 403 | reason | string | IOError: source_data may not exist or output_file is not writable | 31 | | 500 | reason | string | InternalError: traceback info | 32 | 33 | _ _ _ 34 | ## /status GET 35 | 36 | ### Response 37 | | Code | Body_key | Type | Description | 38 | |--------|--------|--------|--------| 39 | | 200 | status | string | value is "running" | 40 | | - | progress(Optional) | float | calculating precent, between 0 to 1 | 41 | | - | current(Optional) | int | calculated combinations | 42 | | - | total(Optional) | int | total combinations | 43 | -------------------------------------------------------------------------------- /example/data_Template.json: -------------------------------------------------------------------------------- 1 | [{"\u66b4\u51fb\u4f24\u5bb3": 0.1, "\u66b4\u51fb": 0.06, "\u5fa1\u9b42\u7c7b\u578b": "\u9488\u5973", "\u653b\u51fb": 100, "\u4f4d\u7f6e": 1, "\u653b\u51fb\u52a0\u6210": 0.03, "\u751f\u547d\u52a0\u6210": 0.03}, {"\u66b4\u51fb\u4f24\u5bb3": 0.16, "\u66b4\u51fb": 0.55, "\u5fa1\u9b42\u7c7b\u578b": "\u7834\u52bf", "\u653b\u51fb": 10, "\u4f4d\u7f6e": 6, "\u653b\u51fb\u52a0\u6210": 0.03, "\u901f\u5ea6": 3}, {"\u66b4\u51fb\u4f24\u5bb3": 0.1, "\u66b4\u51fb": 0.06, "\u5fa1\u9b42\u7c7b\u578b": "\u9488\u5973", "\u4f4d\u7f6e": 4, "\u653b\u51fb\u52a0\u6210": 0.55, "\u751f\u547d\u52a0\u6210": 0.03}, {"\u66b4\u51fb\u4f24\u5bb3": 0.1, "\u66b4\u51fb": 0.06, "\u5fa1\u9b42\u7c7b\u578b": "\u9488\u5973", "\u4f4d\u7f6e": 3, "\u653b\u51fb\u52a0\u6210": 0.03, "\u751f\u547d\u52a0\u6210": 0.03}, {"\u66b4\u51fb\u4f24\u5bb3": 0.1, "\u5fa1\u9b42\u7c7b\u578b": "\u9488\u5973", "\u4f4d\u7f6e": 2, "\u653b\u51fb\u52a0\u6210": 0.6, "\u751f\u547d\u52a0\u6210": 0.03, "\u901f\u5ea6": 5}, {"\u901f\u5ea6": 3, "\u4f4d\u7f6e": 5, "\u66b4\u51fb\u4f24\u5bb3": 0.16, "\u653b\u51fb\u52a0\u6210": 0.03, "\u5fa1\u9b42\u7c7b\u578b": "\u7834\u52bf"}, {"\u66b4\u51fb\u4f24\u5bb3": 0.1, "\u66b4\u51fb": 0.06, "\u5fa1\u9b42\u7c7b\u578b": "\u7834\u52bf", "\u4f4d\u7f6e": 1, "\u653b\u51fb\u52a0\u6210": 0.05, "\u751f\u547d\u52a0\u6210": 0.03}, {"\u901f\u5ea6": 3, "\u4f4d\u7f6e": 5, "\u66b4\u51fb\u4f24\u5bb3": 0.16, "\u653b\u51fb\u52a0\u6210": 0.03, "\u5fa1\u9b42\u7c7b\u578b": "\u9488\u5973"}] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | AUTHORS 28 | ChangeLog 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | 108 | data/ 109 | *.tar.gz 110 | *.xls 111 | *.json 112 | *.csv 113 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/convert_json2xls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | import argparse 5 | import os 6 | import sys 7 | 8 | from calculator_of_Onmyoji import load_data 9 | from calculator_of_Onmyoji import write_data 10 | 11 | 12 | def str2bool(v): 13 | if v.lower() in ('yes', 'true', 't', 'y', '1'): 14 | return True 15 | elif v.lower() in ('no', 'false', 'f', 'n', '0'): 16 | return False 17 | else: 18 | raise argparse.ArgumentTypeError('Boolean value expected.') 19 | 20 | 21 | parser = argparse.ArgumentParser() 22 | 23 | parser.add_argument("-ESPS", "--effective-secondary-prop-show", 24 | type=str2bool, 25 | default=True, 26 | help='是否展示副属性的有效条数,默认为True。如果不想展示输入False即可' 27 | '"-ESPS True"为展示副属性有效条数' 28 | '“输出类”为包含 攻击加成、速度、暴击、暴击伤害的有效条数' 29 | '“奶盾类”为包含 生命加成、速度、暴击、暴击伤害的有效条数' 30 | '“命中类”为包含 效果命中、速度的有效条数' 31 | '“双堆类”为包含 效果命中、效果抵抗、速度的有效条数' 32 | '首领御魂的固有属性也加入计算,首领御魂的满条为12分左右') 33 | 34 | 35 | if __name__ == '__main__': 36 | args = parser.parse_args() 37 | esps_show = args.effective_secondary_prop_show 38 | print('Input args: %s' % args) 39 | json_files = load_data.get_ext_files('.json') 40 | 41 | if not json_files: 42 | print('There is no json file in current directory, exit.') 43 | sys.exit(1) 44 | 45 | for file_path in json_files: 46 | data = load_data.get_mitama_data_json(file_path, []) 47 | 48 | file_name, _ = os.path.splitext(file_path) 49 | file_name_xls = file_name + '.xls' 50 | 51 | if os.path.exists(file_name_xls): 52 | print('%s already exists, skip it.' % file_name_xls) 53 | continue 54 | 55 | write_data.write_original_mitama_data(file_name_xls, data, esps_show) 56 | 57 | print('File %s has been converted' % file_path) 58 | 59 | input('Press any key to exit') 60 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/cm_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | import configparser 5 | import json 6 | import os 7 | import sys 8 | import threading 9 | import time 10 | import traceback 11 | import webbrowser 12 | 13 | import flask 14 | from flask_cors import CORS 15 | from webob import exc 16 | 17 | from calculator_of_Onmyoji import cal_mitama 18 | 19 | 20 | if getattr(sys, 'frozen', False): 21 | if sys.platform == 'darwin': 22 | template_folder = os.path.join(sys._MEIPASS, 'templates') 23 | static_folder = os.path.join(sys._MEIPASS, 'static') 24 | elif sys.platform == 'win32': 25 | template_folder = os.path.join(sys.executable, '..', 'templates') 26 | static_folder = os.path.join(sys.executable, '..', 'static') 27 | app = flask.Flask(__name__, 28 | template_folder=template_folder, 29 | static_folder=static_folder) 30 | else: 31 | app = flask.Flask(__name__) 32 | 33 | CORS(app) 34 | work_path = os.path.dirname(os.path.realpath(__file__)) 35 | calculator = None 36 | 37 | 38 | @app.route('/', methods=['GET']) 39 | def index(): 40 | return flask.render_template('index.html') 41 | 42 | 43 | @app.route('/calculate', methods=['POST']) 44 | def calculate(): 45 | try: 46 | # NOTE: request must be "Content-Type: application/json" 47 | if not flask.request.is_json: 48 | res = {"reason": "Request content-type must be json"} 49 | return flask.make_response((json.dumps(res), 50 | exc.HTTPBadRequest.code)) 51 | 52 | params = flask.request.get_json() 53 | 54 | if 'src_filename' in params: 55 | # NOTE: UI只能获取文件名,限定文件必须在当前目录 56 | src_filename = params['src_filename'] 57 | dst_filename = os.path.splitext(src_filename)[0] + '-result.xls' 58 | params['source_data'] = os.path.join(work_path, src_filename) 59 | params['output_file'] = os.path.join(work_path, dst_filename) 60 | 61 | global calculator 62 | calculator = cal_mitama.Calculator(params) 63 | result_num = calculator.run() 64 | ret = exc.HTTPOk.code 65 | res = {"result_num": result_num, 66 | "output_file": params['output_file']} 67 | except IOError: 68 | ret = exc.HTTPForbidden.code 69 | res = {"reason": "Please check: 1.source_file exists;" 70 | " 2.output_file is writable and not be opened by other process." 71 | " %s" % traceback.format_exc()} 72 | except Exception: 73 | ret = exc.HTTPInternalServerError.code 74 | res = {"reason": traceback.format_exc()} 75 | 76 | print(res) 77 | return flask.make_response((json.dumps(res), ret)) 78 | 79 | 80 | @app.route('/status', methods=['GET']) 81 | def status(): 82 | if calculator: 83 | progress, current, total = calculator.get_progress() 84 | else: 85 | progress, current, total = 0, 0, 0 86 | 87 | res = {"status": "running"} 88 | if progress: 89 | res.update({"progress": progress, 90 | "current": current, 91 | "total": total}) 92 | 93 | return flask.make_response((json.dumps(res)), 200) 94 | 95 | 96 | def open_browser(host, port): 97 | url = 'http://%s:%s' % (host, port) 98 | time.sleep(1) 99 | webbrowser.open(url) 100 | 101 | 102 | if __name__ == '__main__': 103 | conf = configparser.ConfigParser() 104 | conf.read(os.path.join(work_path, 'server.conf')) 105 | host = conf.get('global', 'host') 106 | port = conf.get('global', 'port') 107 | 108 | t = threading.Thread(target=open_browser, args=(host, port)) 109 | t.start() 110 | 111 | app.run(host=host, port=port, threaded=True) 112 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/pull_mitama.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | import argparse 5 | import json 6 | import traceback 7 | 8 | import requests 9 | import xlwt 10 | 11 | from calculator_of_Onmyoji import data_format 12 | 13 | UASTRING = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) " 14 | "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.1.1 " 15 | "Safari/605.1.15") 16 | 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("acc_id", 19 | type=str, 20 | help='藏宝阁id,商品详情页面对应的网址中,' 21 | '格式如201806211901616-3-KJ8J8IQOJTOMD8') 22 | parser.add_argument("-O", "--output-file", 23 | type=str, 24 | default='mitama_data.xls', 25 | help='输出文件位置,格式为pathto/filename.xls') 26 | 27 | 28 | def download_data(acc_id): 29 | server_id = int(acc_id.split('-')[1]) 30 | post_data = {'serverid': server_id, 'ordersn': acc_id} 31 | post_header = {'User Agent': UASTRING} 32 | post_url = 'https://yys.cbg.163.com/cgi/api/get_equip_detail' 33 | 34 | try: 35 | print(post_url, post_data, post_header) 36 | req = requests.post(post_url, data=post_data, headers=post_header, 37 | verify=False) 38 | return json.loads(req.text) 39 | except Exception: 40 | print('Unable to download the data. %s' % traceback.format_exc()) 41 | return None 42 | 43 | 44 | def calAddiAttrs(rattrs): 45 | enAttrNames = ['attackAdditionRate', 46 | 'attackAdditionVal', 47 | 'critPowerAdditionVal', 48 | 'critRateAdditionVal', 49 | 'debuffEnhance', 50 | 'debuffResist', 51 | 'defenseAdditionRate', 52 | 'defenseAdditionVal', 53 | 'maxHpAdditionRate', 54 | 'maxHpAdditionVal', 55 | 'speedAdditionVal'] 56 | 57 | cnAttrNames = ['攻击加成', '攻击', '暴击伤害', '暴击', 58 | '效果命中', '效果抵抗', '防御加成', 59 | '防御', '生命加成', '生命', '速度'] 60 | 61 | basePropValue = {'攻击加成': 3, '攻击': 27, '暴击伤害': 4, '暴击': 3, 62 | '效果抵抗': 4, '效果命中': 4, '防御加成': 3, 63 | '防御': 5, '生命加成': 3, '生命': 114, '速度': 3} 64 | e2cNameMap = dict(list(zip(enAttrNames, cnAttrNames))) 65 | res = {} 66 | for prop, v in rattrs: 67 | prop = e2cNameMap[prop] 68 | if prop not in res: 69 | res[prop] = 0 70 | res[prop] += v 71 | return [[p, res[p]*basePropValue[p]] for p in res] 72 | 73 | 74 | def generate_mitama_list(acc_id, filename, 75 | header_row=data_format.MITAMA_COL_NAME_ZH): 76 | print("Downloading data...") 77 | res = download_data(acc_id) 78 | 79 | print("Dumping mitama data...") 80 | if res is None: 81 | return 82 | 83 | try: 84 | workbook = xlwt.Workbook(encoding='utf-8') 85 | mitama_sheet = workbook.add_sheet('御魂') 86 | acct_info = res['equip'] 87 | acct_detail = json.loads(acct_info['equip_desc']) 88 | 89 | mitama_list = acct_detail['inventory'] 90 | 91 | col_nums = len(header_row) 92 | 93 | # write header row 94 | for c in range(col_nums): 95 | mitama_sheet.write(0, c, label=header_row[c]) 96 | 97 | mitama_num = 1 98 | for mitama_id in mitama_list: 99 | mitama_info = mitama_list[mitama_id] 100 | if int(mitama_info['level']) < 15: 101 | continue 102 | mitama_pos = str(mitama_info['pos']) 103 | mitama_name = mitama_info['name'] 104 | mitama_attrs = dict() 105 | # 获取首领御魂独立属性 106 | single_prop = mitama_info.get('single_attr') 107 | if single_prop: 108 | mitama_attrs[single_prop[0]] = int( 109 | single_prop[1].replace('%', '')) 110 | if 'rattr' in mitama_info: 111 | # 主属性从attrs获取 112 | base_prop = mitama_info['attrs'][0] 113 | mitama_attrs[base_prop[0]] = float(base_prop[1].replace('%', '')) 114 | # 副属性由rattr的强化记录进行推导 115 | for prop, value in calAddiAttrs(mitama_info['rattr']): 116 | if prop not in mitama_attrs: 117 | mitama_attrs[prop] = value 118 | else: 119 | mitama_attrs[prop] += value 120 | else: 121 | for prop, value in mitama_info['attrs']: 122 | value = int(value.replace('%', '')) 123 | if prop not in mitama_attrs: 124 | mitama_attrs[prop] = value 125 | else: 126 | mitama_attrs[prop] += value 127 | 128 | mitama_sheet.write(mitama_num, 0, label=mitama_id) 129 | mitama_sheet.write(mitama_num, 1, label=mitama_name) 130 | mitama_sheet.write(mitama_num, 2, label=mitama_pos) 131 | for i, prop in enumerate(data_format.MITAMA_PROPS): 132 | prop_value = mitama_attrs.get(prop, '') 133 | mitama_sheet.write(mitama_num, 3+i, label=prop_value) 134 | 135 | mitama_num += 1 136 | 137 | workbook.save(filename) 138 | print("write finish, we got %s results" % mitama_num) 139 | except Exception as e: 140 | print(e.message) 141 | raise e 142 | 143 | 144 | def main(): 145 | args = parser.parse_args() 146 | test_acc_id = args.acc_id 147 | output_file = args.output_file 148 | print('Start pulling mitama data, please wait') 149 | generate_mitama_list(test_acc_id, output_file) 150 | 151 | 152 | if __name__ == '__main__': 153 | main() 154 | input('Press any key to exit.') 155 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/css/app.5b01de06.css: -------------------------------------------------------------------------------- 1 | .custom-scheme{margin-top:50px}.custom-scheme .el-button{margin-bottom:5px}.el-select{width:250px}.el-message-box__title{font-size:16px}.el-button.delete{color:#fff;background-color:#f56c6c;border-color:#f56c6c}.el-button.delete:hover{background:#f78989;border-color:#f78989;color:#fff}.calculator{width:940px;height:540px;position:fixed;left:50%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);background-color:#1d2633;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;border-radius:6px}.calculator .header{background-image:url(../img/header.37ff70af.png);width:100%;height:60px;position:relative}.calculator .header p{color:#f1f1f1;position:absolute;top:22px;left:60px}.calculator .main{-webkit-box-flex:1;-ms-flex:1;flex:1;padding:8px 16px;display:-webkit-box;display:-ms-flexbox;display:flex}.calculator .main .control{width:210px;padding:4px 8px}.calculator .main .control p{color:#ccc;line-height:28px;position:relative;top:-6px}.calculator .main .control .el-button{width:100%;margin-bottom:12px}.calculator .main .control .custom-setting{margin-top:50px}.calculator .main .control .custom-setting .el-button{margin-bottom:5px}.calculator .main .filter-options-form{width:300px;margin:0 8px}.calculator .main .filter-options-form .yuhun-package-input .el-form-item__label{line-height:24px}.calculator .main .filter-options-form .yuhun-package-input .el-select{width:200px;margin-bottom:8px}.calculator .main .filter-options-form .yuhun-package-input .el-select .el-select__caret{line-height:34px}.calculator .main .filter-options-form .yuhun-package-input .el-button{margin-left:10px;width:90px;height:34px;-webkit-transform:translateY(2px);transform:translateY(2px)}.calculator .main .filter-options-form .yuhun-package-input .el-button span{position:relative;top:-3px}.calculator .main .filter-options-form .yuhun-package-input .select-yuhun-list{width:100%;min-height:36px}.calculator .main .filter-options-form .yuhun-package-input .select-yuhun-list .el-tag{margin-right:8px;margin-bottom:4px}.calculator .main .expect-options-form{-webkit-box-flex:1;-ms-flex:1;flex:1;margin:0 8px}.calculator .main .expect-options-form label{color:#f1f1f1;margin-right:4px}.calculator .main .expect-options-form label:nth-child(3){margin-left:12px}.calculator .main .expect-options-form .el-select{width:130px}.calculator .main .expect-options-form .el-input-number{width:100px}.calculator .main .expect-options-form .el-button{height:34px;margin-left:12px;-webkit-transform:translateY(2px);transform:translateY(2px)}.calculator .main .expect-options-form .el-button span{position:relative;top:-3px}.calculator .main .expect-options-form .attribute-results{height:66px;color:#409eff}.calculator .main .expect-options-form .attribute-results span{margin-right:12px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.calculator .main .expect-options-form .el-autocomplete{width:100%}.calculator .main .expect-options-form .esp-autocomplete span{text-overflow:ellipsis;overflow:hidden}.calculator .main .el-icon-info{color:#ddd}.calculator .el-checkbox__label,.calculator .el-form-item__label{color:#eee}.calculator .el-input__inner{height:34px}.calculator .el-form-item{margin-bottom:5px}.calculator .el-form-item .preset .el-radio-button.is-active .el-radio-button__inner{color:#fff;background-color:#409eff;border-color:#409eff;-webkit-box-shadow:-1px 0 0 0 #8cc5ff;box-shadow:-1px 0 0 0 #8cc5ff}.calculator .el-form-item .preset .el-radio-button__inner{min-width:50px;padding:7px 5px;color:#fff;background-color:#409eff;border:1px solid #333b47}.calculator .el-form-item .preset .el-form-item__label{padding:0;text-align:center;line-height:22px}.calculator .el-form-item .preset .el-form-item__content{-webkit-box-flex:1;-ms-flex:1;flex:1}.calculator .el-form-item .preset .el-radio-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-transform:translateY(-5px);transform:translateY(-5px)}.calculator .el-form-item .preset .el-radio{margin-left:10px;height:30px}.calculator .el-form-item.multi-checkbox .el-checkbox-button.is-checked .el-checkbox-button__inner{color:#fff;background-color:#409eff;border-color:#409eff;-webkit-box-shadow:-1px 0 0 0 #8cc5ff;box-shadow:-1px 0 0 0 #8cc5ff}.calculator .el-form-item.multi-checkbox .el-checkbox-button__inner{min-width:60px;padding:7px 5px;background-color:#11171f;border:1px solid #333b47;color:#ccc}.calculator .el-form-item.multi-checkbox .el-form-item__label{padding:0;text-align:center;line-height:22px}.calculator .el-form-item.multi-checkbox .el-form-item__content{-webkit-box-flex:1;-ms-flex:1;flex:1}.calculator .el-form-item.multi-checkbox .el-checkbox-group{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-transform:translateY(-5px);transform:translateY(-5px)}.calculator .el-form-item.multi-checkbox .el-checkbox{margin-left:10px;height:30px}.calculator .el-form-item.input-item .el-form-item__label{line-height:24px}.calculator .el-select{width:100%}.calculator .el-checkbox__inner,.calculator input{background-color:#11171f;border:1px solid #333b47;color:#ccc}.calculator .el-checkbox__inner::-webkit-input-placeholder,.calculator input::-webkit-input-placeholder{color:#606266}.calculator .el-checkbox__inner::-ms-input-placeholder,.calculator input::-ms-input-placeholder{color:#606266}.calculator .el-checkbox__inner::placeholder,.calculator input::placeholder{color:#606266}.calculator p.title{color:#eee;line-height:24px;font-size:14px}.calculator p.title .el-tag{background-color:inherit;border:0;-webkit-transform:scale(1.5);transform:scale(1.5);-webkit-transition:-webkit-transform .2s ease-in-out;transition:-webkit-transform .2s ease-in-out;transition:transform .2s ease-in-out;transition:transform .2s ease-in-out,-webkit-transform .2s ease-in-out}.calculator p.title .el-tag:hover{-webkit-transform:scale(2) rotate(45deg);transform:scale(2) rotate(45deg)}.calculator .el-input-number__decrease,.calculator .el-input-number__increase{background-color:#11171f;border-left-color:#333b47!important;color:#ccc}.calculator .el-input-number__increase{border-bottom-color:#333b47!important}#app,body,html{width:100%;height:100%;overflow:hidden}html{font-family:local-file,Arial,Verdana,"\5FAE\8F6F\96C5\9ED1","\9ED1\4F53","serif";color:#111}#app{background-image:url(../img/background.c3af5934.jpg);background-size:cover}body,div,h1,h2,h3,h4,html,p{margin:0} -------------------------------------------------------------------------------- /calculator_of_Onmyoji/data_format.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import string 4 | 5 | 6 | COL_INDEX = string.ascii_uppercase 7 | 8 | MITAMA_COL_NAME_ZH = ['御魂序号', '御魂类型', '位置', '攻击', 9 | '攻击加成', '防御', '防御加成', '暴击', 10 | '暴击伤害', '生命', '生命加成', '效果命中', 11 | '效果抵抗', '速度'] 12 | 13 | RESULT_HEADER = ['组合序号'] + MITAMA_COL_NAME_ZH 14 | 15 | RESULT_HEADER_LEN = len(RESULT_HEADER) 16 | 17 | RESULT_INDEX = {RESULT_HEADER[i]: COL_INDEX[i] 18 | for i in range(RESULT_HEADER_LEN)} 19 | 20 | RESULT_COMB_HEADER = ['组合个数', '组合序号', 21 | '暴击', '总攻击', '攻击x暴伤', '速度'] 22 | 23 | EXTEND_HEADER = ['式神基础攻击', '式神基础生命', '式神基础暴伤', 24 | '总攻击', '总生命', 25 | '攻击x暴伤', '生命×暴伤', '攻击x暴伤(+buff)'] 26 | 27 | EXTEND_INDEX = {EXTEND_HEADER[i]: COL_INDEX[i+RESULT_HEADER_LEN] 28 | for i in range(len(EXTEND_HEADER))} 29 | 30 | MITAMA_PROPS = ['攻击', '攻击加成', '防御', '防御加成', '暴击', 31 | '暴击伤害', '生命', '生命加成', '效果命中', 32 | '效果抵抗', '速度'] 33 | 34 | MITAMA_ENHANCE = {"珍珠": {"加成类型": "防御加成", "加成数值": 30}, 35 | "骰子鬼": {"加成类型": "效果抵抗", "加成数值": 15}, 36 | "蚌精": {"加成类型": "效果命中", "加成数值": 15}, 37 | "魅妖": {"加成类型": "防御加成", "加成数值": 30}, 38 | "针女": {"加成类型": "暴击", "加成数值": 15}, 39 | "返魂香": {"加成类型": "效果抵抗", "加成数值": 15}, 40 | "雪幽魂": {"加成类型": "防御加成", "加成数值": 30}, 41 | "地藏像": {"加成类型": "生命加成", "加成数值": 15}, 42 | "蝠翼": {"加成类型": "攻击加成", "加成数值": 15}, 43 | "涅槃之火": {"加成类型": "生命加成", "加成数值": 15}, 44 | "三味": {"加成类型": "暴击", "加成数值": 15}, 45 | "魍魉之匣": {"加成类型": "效果抵抗", "加成数值": 15}, 46 | "被服": {"加成类型": "生命加成", "加成数值": 15}, 47 | "招财猫": {"加成类型": "防御加成", "加成数值": 30}, 48 | "反枕": {"加成类型": "防御加成", "加成数值": 30}, 49 | "轮入道": {"加成类型": "攻击加成", "加成数值": 15}, 50 | "日女巳时": {"加成类型": "防御加成", "加成数值": 30}, 51 | "镜姬": {"加成类型": "生命加成", "加成数值": 15}, 52 | "钟灵": {"加成类型": "生命加成", "加成数值": 15}, 53 | "狰": {"加成类型": "攻击加成", "加成数值": 15}, 54 | "火灵": {"加成类型": "效果命中", "加成数值": 15}, 55 | "鸣屋": {"加成类型": "攻击加成", "加成数值": 15}, 56 | "薙魂": {"加成类型": "生命加成", "加成数值": 15}, 57 | "心眼": {"加成类型": "攻击加成", "加成数值": 15}, 58 | "木魅": {"加成类型": "防御加成", "加成数值": 30}, 59 | "树妖": {"加成类型": "生命加成", "加成数值": 15}, 60 | "网切": {"加成类型": "暴击", "加成数值": 15}, 61 | "阴摩罗": {"加成类型": "攻击加成", "加成数值": 15}, 62 | "伤魂鸟": {"加成类型": "暴击", "加成数值": 15}, 63 | "破势": {"加成类型": "暴击", "加成数值": 15}, 64 | "镇墓兽": {"加成类型": "暴击", "加成数值": 15}, 65 | "狂骨": {"加成类型": "攻击加成", "加成数值": 15}, 66 | "幽谷响": {"加成类型": "效果抵抗", "加成数值": 15}, 67 | "兵主部": {"加成类型": "攻击加成", "加成数值": 15}, 68 | "青女房": {"加成类型": "暴击", "加成数值": 15}, 69 | "涂佛": {"加成类型": "生命加成", "加成数值": 15}, 70 | "飞缘魔": {"加成类型": "效果命中", "加成数值": 15}, 71 | "土蜘蛛": {"加成类型": "", "加成数值": 0}, 72 | "胧车": {"加成类型": "", "加成数值": 0}, 73 | "荒骷髅": {"加成类型": "", "加成数值": 0}, 74 | "地震鲶": {"加成类型": "", "加成数值": 0}, 75 | "蜃气楼": {"加成类型": "", "加成数值": 0}, 76 | "鬼灵歌伎": {"加成类型": "", "加成数值": 0}, 77 | } 78 | 79 | MITAMA_TYPES = list(MITAMA_ENHANCE.keys()) 80 | 81 | MITAMA_GROWTH = {"攻击加成": {"最小成长值": 2.4, 82 | "副属性最大值": 26, 83 | "主属性": 55}, 84 | "生命加成": {"最小成长值": 2.4, 85 | "副属性最大值": 26, 86 | "主属性": 55}, 87 | "防御加成": {"最小成长值": 2.4, 88 | "副属性最大值": 26, 89 | "主属性": 55}, 90 | "效果命中": {"最小成长值": 3.2, 91 | "副属性最大值": 32, 92 | "主属性": 55}, 93 | "效果抵抗": {"最小成长值": 3.2, 94 | "副属性最大值": 32, 95 | "主属性": 55}, 96 | "暴击": {"最小成长值": 2.4, 97 | "副属性最大值": 26, 98 | "主属性": 55}, 99 | "暴击伤害": {"最小成长值": 3.2, 100 | "副属性最大值": 24, 101 | "主属性": 89}, 102 | "速度": {"最小成长值": 2.4, 103 | "副属性最大值": 18, 104 | "主属性": 57}, 105 | "攻击": {"最小成长值": 21.6, 106 | "副属性最大值": 162, 107 | "主属性": 486}, 108 | "防御": {"最小成长值": 4, 109 | "副属性最大值": 30, 110 | "主属性": 104}, 111 | "生命": {"最小成长值": 91.2, 112 | "副属性最大值": 684, 113 | "主属性": 2052}, 114 | } 115 | 116 | MITAMA_ESP_EXTEND_HEADER = ["输出类有效条数", "奶盾类有效条数", 117 | "命中类有效条数", "双堆类有效条数", 118 | "混合类有效条数"] 119 | 120 | MITAMA_ESP = {"输出类有效条数": ['攻击加成', '暴击', '速度', '暴击伤害'], 121 | "奶盾类有效条数": ['生命加成', '暴击', '速度', '暴击伤害'], 122 | "命中类有效条数": ['速度', '效果命中'], 123 | "双堆类有效条数": ['速度', '效果命中', '效果抵抗'], 124 | "混合类有效条数": ['速度', '效果命中', '效果抵抗', 125 | '攻击加成', '生命加成', 126 | '暴击', '暴击伤害'], 127 | } 128 | 129 | ATTACK_MITAMA_TYPE = [t for t, e in list(MITAMA_ENHANCE.items()) 130 | if e['加成类型'] in ['', '攻击加成', '暴击']] 131 | 132 | SUIT_ID_TO_NAME = {300002: '雪幽魂', 300003: '地藏像', 300004: '蝠翼', 133 | 300006: '涅槃之火', 300007: '三味', 300008: '魍魉之匣', 134 | 300009: '被服', 300010: '招财猫', 300011: '反枕', 135 | 300012: '轮入道', 300013: '日女巳时', 300014: '镜姬', 136 | 300015: '钟灵', 300018: '狰', 300019: '火灵', 300020: '鸣屋', 137 | 300021: '薙魂', 300022: '心眼', 300023: '木魅', 300024: '树妖', 138 | 300026: '网切', 300027: '阴摩罗', 300029: '伤魂鸟', 139 | 300030: '破势', 300031: '镇墓兽', 300032: '珍珠', 140 | 300033: '骰子鬼', 300034: '蚌精', 300035: '魅妖', 141 | 300036: '针女', 300039: '返魂香', 300048: '狂骨', 142 | 300049: '幽谷响', 300050: '土蜘蛛', 300051: '胧车', 143 | 300052: '荒骷髅', 300053: '地震鲶', 300054: '蜃气楼', 144 | 300073: '飞缘魔', 300074: '兵主部', 300075: '青女房', 145 | 300076: '涂佛', 300077: '鬼灵歌伎'} 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # calculator\_of\_Onmyoji 2 | 3 | 御魂搭配计算器 4 | 5 | **Note** This project has been archived and not maitained any more. Thanks for your attention. 6 | 7 | ## Setup 8 | 9 | Python version >= 3.6.* 10 | 11 | ``` 12 | pip install -r requirements.txt 13 | pip install . 14 | ``` 15 | 16 | 17 | ## Usage of calculator 18 | 19 | ``` 20 | usage: cal_mitama.py [-h] [-M MITAMA_SUIT] [-P PROP_LIMIT] 21 | [-UP UPPER_PROP_LIMIT] [-2P SEC_PROP_VALUE] 22 | [-4P FTH_PROP_VALUE] [-6P STH_PROP_VALUE] 23 | [-IG IGNORE_SERIAL] [-AS ALL_SUIT] [-DL DAMAGE_LIMIT] 24 | [-HL HEALTH_LIMIT] [-AO ATTACK_ONLY] 25 | [-ESP EFFECTIVE_SECONDARY_PROP] 26 | [-ESPN EFFECTIVE_SECONDARY_PROP_NUM] 27 | source_data output_file 28 | 29 | positional arguments: 30 | source_data 御魂数据,格式参照example/data_Template.xls 31 | output_file 输出文件位置,格式为pathto/filename.xls 32 | 33 | optional arguments: 34 | -h, --help show this help message and exit 35 | -M MITAMA_SUIT, --mitama-suit MITAMA_SUIT 36 | 期望的x件套御魂类型或者加成类型,多个限制用英文符号#间隔,例如"-M 针女,4"为针女至少4件,"-M 37 | 针女,4#破势,2"为针女4件+破势2件,"-M 生命加成,2#生命加成,2#生命加成,2"为3个生命两件套 38 | -P PROP_LIMIT, --prop-limit PROP_LIMIT 39 | 期望限制的属性下限,多个属性条件用英文符号#间隔, 例如"-P 40 | 暴击,90#暴击伤害,70"为暴击至少90且暴击伤害至少70 41 | -UP UPPER_PROP_LIMIT, --upper-prop-limit UPPER_PROP_LIMIT 42 | 期望限制的属性上限,多个属性条件用英文符号#间隔,例如"-UP 43 | 暴击,95#速度,20"为暴击最多95且速度最多20 44 | -2P SEC_PROP_VALUE, --sec-prop-value SEC_PROP_VALUE 45 | 二号位限制的属性类型和数值,多个属性用英文符号#间隔,例如"-2P 攻击加成,55"为二号位攻击加成至少55 46 | -4P FTH_PROP_VALUE, --fth-prop-value FTH_PROP_VALUE 47 | 四号位限制的属性类型和数值,多个属性用英文符号#间隔,例如"-4P 攻击加成,55"为四号位攻击加成至少55 48 | -6P STH_PROP_VALUE, --sth-prop-value STH_PROP_VALUE 49 | 六号位限制的属性类型和数值,多个属性用英文符号#间隔,例如"-6P 50 | 暴击,55"为六号位暴击至少55,"-6P 51 | 暴击,55#暴击伤害,89"为六号位暴击至少55或暴击伤害至少89 52 | -IG IGNORE_SERIAL, --ignore-serial IGNORE_SERIAL 53 | 忽略的御魂序号关键字,用逗号,间隔例如"-IG 天狗,鸟"为御魂序号包含天狗或鸟则滤除 54 | -AS ALL_SUIT, --all-suit ALL_SUIT 55 | 是否全为套装,默认为True。"-AS False"为允许非套装的组合出现,如5针女1破势 56 | -DL DAMAGE_LIMIT, --damage-limit DAMAGE_LIMIT 57 | 基础攻击,基础暴伤,期望的攻击*暴伤,例如"-DL 58 | 3126,150,20500",当基础攻击为3126,基础暴伤为150,攻击*暴伤>=20500 59 | -HL HEALTH_LIMIT, --health-limit HEALTH_LIMIT 60 | 基础生命,基础暴伤,期望的生命*暴伤,例如"-HL 61 | 8000,150,60000",当基础生命为8000,基础暴伤为150,生命*暴伤>=60000 62 | -AO ATTACK_ONLY, --attack-only ATTACK_ONLY 63 | 是否只计算输出类御魂,默认为False。"-AO 64 | True"为只计算套装属性为攻击加成、暴击和首领御魂的套装组合 65 | -ESP EFFECTIVE_SECONDARY_PROP, --effective-secondary-prop 66 | 设定御魂的有效副属性,用逗号,间隔 67 | 例如"-ESP 暴击,暴击伤害,速度,攻击加成"意味着有效副属性定位为暴击、暴击伤害、速度、攻击加成 68 | -ESPN EFFECTIVE_SECONDARY_PROP_NUM, --effective-secondary-prop-num 69 | 与-ESP配合使用,限定1-6号位御魂的有效副属性加成次数(含初始次数)用逗号,间隔 70 | 例如"-ESP 暴击,暴击伤害 -ESPN 3,3,5,3,5,0" 71 | 意味着1、2、3、4、5、6号位御魂以暴击和暴击伤害为集合的有效属性集合,出现的总次数不低于3、3、5、3、5、0次 72 | 以一号位举例,暴击、暴击伤害的加成次数(含初始次数)为3次的分布可能有如下情况: 73 | 组合一:暴击*3、暴击伤害*0,即暴击+7.2、暴击伤害+0 74 | 组合二:暴击*2、暴击伤害*1,即暴击+4.8、暴击伤害+3.2 75 | 组合三:暴击*1、暴击伤害*2,即暴击+2.4、暴击伤害+6.4 76 | 组合四:暴击*0、暴击伤害*3,即暴击+0 、暴击伤害+9.6 77 | ``` 78 | 79 | ## Usage of cm server 80 | 81 | ``` 82 | python ./calculator_of_Onmyoji/cm_server.py 83 | ``` 84 | 85 | Then open http://127.0.0.1:2019 in web browser, Chrome etc. The ip and port could be modified in `server.conf`. 86 | 87 | Request example, see details in [api_doc](./doc/API.md) 88 | ``` 89 | curl http://127.0.0.1:2019/calculate -X POST -H "Content-Type: application/json" -d '{"src_filename":"data_Template.xls", "mitama_suit":"针女,4", "prop_limit":"暴击,90", "upper_prop_limit":",0", "sec_prop_value":",0", "fth_prop_value":",0", "sth_prop_value":",0", "ignore_serial":"","all_suit":"True","damage_limit":"0,0,0", "health_limit":"0,0,0","attack_only":"False","effective_secondary_prop":"","effective_secondary_prop_num":""}' 90 | 91 | Note: 92 | src_filename: source file must be in current directroy 93 | ``` 94 | 95 | ## Usage of mitama puller 96 | 97 | ``` 98 | usage: pull_mitama.py [-h] [-O OUTPUT_FILE] acc_id 99 | 100 | positional arguments: 101 | acc_id 藏宝阁id,商品详情页面对应的网址中,格式如201806211901616-3-KJ8J8IQOJTOMD8 102 | 103 | optional arguments: 104 | -h, --help show this help message and exit 105 | -O OUTPUT_FILE, --output-file OUTPUT_FILE 106 | 输出文件位置,格式为pathto/filename.xls 107 | ``` 108 | 109 | ## Usage of mitama json converter 110 | 111 | ``` 112 | usage: make sure json files are in the same directory with convert_json2xls.py 113 | 114 | python convert_json2xls.py 115 | 116 | or 117 | 118 | python convert_json2xls.py -ESPS True 119 | ``` 120 | 121 | ``` 122 | 在转换成的excel中的“输出类有效条数”、“奶盾类有效条数”、“命中类有效条数”、“双堆类有效条数”、“混合类有效条数”下面的值为该类包含有效属性的有效条数, 123 | 其中: 124 | “输出类”为包含 攻击加成、速度、暴击、暴击伤害的有效条数' 125 | “奶盾类”为包含 生命加成、速度、暴击、暴击伤害的有效条数' 126 | “命中类”为包含 效果命中、速度的有效条数' 127 | “双堆类”为包含 效果命中、效果抵抗、速度的有效条数' 128 | “混合类”为包含 攻击加成、速度、暴击、暴击伤害、生命加成、效果命中、效果抵抗的有效条数 129 | “说明:首领御魂的固有属性也加入计算,因此最大可能达到12分以上” 130 | ``` 131 | 132 | ## Usage of result\_combination 133 | 134 | ``` 135 | Cal independent combinations of mitama_results 136 | 137 | usage: make sure files(*-result.xls) are in current directory 138 | 139 | python calculator_of_Onmyoji/result_combination.py 140 | ``` 141 | 142 | ## Test Command 143 | ```python calculator_of_Onmyoji/cal_mitama.py example/data_Template.xls result.xls -M 针女,4 -P 暴击,90``` 144 | 145 | ## Calculate examples 146 | ```python calculator_of_Onmyoji/cal_mitama.py example/victor.xls v_result.xls -M 针女,4 -P 暴击,90#暴击伤害,50 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击,55 -IG 天狗``` 147 | 148 | * 超星破势荒骷髅茨木 149 | ```python calculator_of_Onmyoji/cal_mitama.py example/victor.xls v_result.xls -M 破势,4#荒骷髅,2 -P 暴击,90#速度,16 -2P 攻击加成,55 -4P 攻击加成,55 -6P 暴击,55 -DL 3216,150,17120 -AO True -ESP 暴击,暴击伤害,攻击加成,速度 -ESPN 3,3,3,3,3,0``` 150 | 151 | * 190速150命中招财凤凰火 152 | ```python calculator_of_Onmyoji/cal_mitama.py example/victor.xls v_result.xls -M 招财猫,4 -P 速度,84#效果命中,150 -2P 速度,57 -4P 效果命中,55 -AS False -ESP 速度,效果命中 -ESPN 3,3,3,0,3,3``` 153 | 154 | * 辉夜姬蚌精盾 155 | ```python calculator_of_Onmyoji/cal_mitama.py example/victor.xls v_result.xls -M 蚌精,4 -P 暴击,95 -2P 生命加成,55 -4P 生命加成,55 -6P 暴击,55 -HL 13785,150,40000 -AS False -ESP 暴击,暴击伤害,生命加成,速度 -ESPN 5,3,5,3,5,0``` 156 | 157 | * 散件爆伤面灵气 158 | ```python calculator_of_Onmyoji/cal_mitama.py example/victor.xls v_result.xls -M 暴击,2#暴击,2#暴击,2 -P 暴击,92 -2P 攻击加成,54 -4P 攻击加成,54 -6P 暴击伤害,89 -ESP 暴击,暴击伤害,攻击加成,速度 -ESPN 3,3,3,3,3,0``` 159 | 160 | * 散件生生暴蜃气楼书翁 161 | ```python calculator_of_Onmyoji/cal_mitama.py example/victor.xls v_result.xls -M 蜃气楼,2 -P 暴击,92 -2P 生命加成,55 -4P 生命加成,55 -6P 暴击,55 -ESP 暴击,暴击伤害,生命加成,速度 -ESPN 3,3,3,3,3,0``` 162 | 163 | ## Make tar for release 164 | ```tar zcf calculator.tar.gz calculator_of_Onmyoji example dist doc LICENSE README.md requirements.txt setup.* win_compile.txt``` 165 | 166 | ## Related projects 167 | * web-UI https://github.com/yinxin630/yys-yuhun-calculator-ui 168 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/load_data.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import json 4 | import os 5 | import traceback 6 | 7 | import xlrd 8 | 9 | from calculator_of_Onmyoji import data_format 10 | 11 | 12 | def get_ext_files(file_ext): 13 | work_path = os.getcwd() 14 | ext_files = [] 15 | 16 | for f in os.listdir(work_path): 17 | file_path = os.path.join(work_path, f) 18 | if os.path.isfile(file_path): 19 | _, file_extension = os.path.splitext(file_path) 20 | 21 | if file_extension == file_ext: 22 | ext_files.append(file_path) 23 | 24 | return ext_files 25 | 26 | 27 | def _get_sheet_rows(filename, sheet_name, print_trace=True): 28 | if not os.path.exists(filename): 29 | raise IOError("File not exists %s" % filename) 30 | 31 | try: 32 | xls_book = xlrd.open_workbook(filename=filename) 33 | data_sheet = xls_book.sheet_by_name(sheet_name) 34 | return data_sheet.get_rows() 35 | except Exception: 36 | if print_trace: 37 | print(traceback.format_exc()) 38 | raise 39 | 40 | 41 | def get_mitama_data_json(filename, ignore_serial): 42 | with open(filename, encoding='utf-8') as f: 43 | data = json.load(f) 44 | 45 | if isinstance(data, dict) and 'data' in data: 46 | if isinstance(data['data'], list): 47 | return load_json_from_editor(data, ignore_serial) 48 | else: 49 | return load_json_from_yyx(data, ignore_serial) 50 | elif isinstance(data, list): 51 | return load_json_from_ocr_editor(data, ignore_serial) 52 | else: 53 | print('Unsupport format') 54 | raise TypeError 55 | 56 | 57 | def load_json_from_ocr_editor(data, ignore_serial): 58 | '''从OCR录入器读取数据''' 59 | mitama_data = dict() 60 | PERCENT_PROP = ['攻击加成', '防御加成', '暴击', '暴击伤害', 61 | '生命加成', '效果命中', '效果抵抗'] 62 | 63 | data_version = 1 64 | if data[0] == "yuhun_ocr2.0": 65 | data_version = 2 66 | data.pop(0) 67 | 68 | serial = 0 69 | for d in data: 70 | if data_version < 2: 71 | serial += 1 72 | else: 73 | serial = d['御魂ID'] 74 | 75 | if skip_serial(serial, ignore_serial): 76 | continue 77 | if d.get('御魂等级', 15) < 15: 78 | continue 79 | # 百分比类数据乘100 80 | for p in d: 81 | if p in PERCENT_PROP: 82 | d[p] *= 100 83 | mitama_data[serial] = d 84 | 85 | return mitama_data 86 | 87 | 88 | def load_json_from_yyx(data, ignore_serial): 89 | '''从痒痒熊读取数据''' 90 | PROP_NAME_MAP = {'Hp': '生命', 'Defense': '防御', 'Attack': '攻击', 91 | 'HpRate': '生命加成', 'DefenseRate': '防御加成', 92 | 'AttackRate': '攻击加成', 'Speed': '速度', 'CritRate': '暴击', 93 | 'CritPower': '暴击伤害', 'EffectHitRate': '效果命中', 94 | 'EffectResistRate': '效果抵抗'} 95 | 96 | PERCENT_PROP = ['攻击加成', '防御加成', '暴击', '暴击伤害', 97 | '生命加成', '效果命中', '效果抵抗'] 98 | 99 | MITAMA_COL_MAP = {'御魂序号': 'id', '御魂类型': 'suit_id', 100 | '位置': 'pos'} 101 | 102 | def mitama_json_to_dict(json_obj): 103 | serial = json_obj['id'] 104 | if skip_serial(serial, ignore_serial) \ 105 | or json_obj.get('level', 15) < 15: 106 | return None 107 | mitama = {} 108 | for col_name in data_format.MITAMA_COL_NAME_ZH[1:]: 109 | if col_name in MITAMA_COL_MAP: 110 | mitama[col_name] = json_obj[MITAMA_COL_MAP[col_name]] 111 | else: 112 | mitama[col_name] = 0 113 | 114 | mitama['御魂类型'] = data_format.SUIT_ID_TO_NAME.get(mitama['御魂类型']) 115 | mitama['位置'] += 1 116 | 117 | attr_list = [json_obj['base_attr']] 118 | attr_list.extend(json_obj['attrs']) 119 | if 'single_attrs' in json_obj: 120 | attr_list.extend(json_obj['single_attrs']) 121 | 122 | for prop in attr_list: 123 | prop_name = PROP_NAME_MAP.get(prop['type'], None) 124 | if prop_name in data_format.MITAMA_PROPS: 125 | if prop_name in PERCENT_PROP: 126 | mitama[prop_name] += float(prop['value'])*100 127 | else: 128 | mitama[prop_name] += float(prop['value']) 129 | 130 | return (serial, mitama) 131 | 132 | mitama_list = map(mitama_json_to_dict, data['data'].get('hero_equips', [])) 133 | return dict([x for x in mitama_list if x]) 134 | 135 | 136 | def load_json_from_editor(data, ignore_serial): 137 | '''从网页版御魂编辑器读取数据''' 138 | def mitama_json_to_dict(json_obj): 139 | MITAMA_COL_MAP = {'御魂序号': 'id', '御魂类型': 'name', 140 | '位置': 'pos'} 141 | serial = json_obj['id'] 142 | if skip_serial(serial, ignore_serial): 143 | return None 144 | mitama = {} 145 | for col_name in data_format.MITAMA_COL_NAME_ZH[1:]: 146 | if col_name in MITAMA_COL_MAP: 147 | mitama[col_name] = json_obj[MITAMA_COL_MAP[col_name]] 148 | else: 149 | mitama[col_name] = 0 150 | 151 | for props in [json_obj['mainAttr'], 152 | json_obj['addonAttr']] + json_obj['addiAttr']: 153 | if props['attrName'] in data_format.MITAMA_PROPS: 154 | mitama[props['attrName']] += float(props['attrVal']) 155 | 156 | return (serial, mitama) 157 | 158 | mitama_list = map(mitama_json_to_dict, data['data']) 159 | return dict([x for x in mitama_list if x]) 160 | 161 | 162 | def get_ignore_serial_xls(filename): 163 | '''Load ignore serial from data xls''' 164 | ignore_serial = [] 165 | try: 166 | rows_data = _get_sheet_rows(filename, '已使用', 167 | print_trace=False) 168 | except xlrd.biffh.XLRDError: 169 | return ignore_serial 170 | 171 | next(rows_data) 172 | for r_data in rows_data: 173 | serial = str(convert_int(r_data[1].value)) 174 | if serial not in ignore_serial: 175 | ignore_serial.append(serial) 176 | 177 | return ignore_serial 178 | 179 | 180 | def convert_int(s): 181 | '''Convert s to int or just return if failed''' 182 | try: 183 | s = int(s) 184 | except ValueError: 185 | pass 186 | return s 187 | 188 | 189 | def get_mitama_data_xls(filename, ignore_serial): 190 | ignore_serial.extend(get_ignore_serial_xls(filename)) 191 | rows_data = _get_sheet_rows(filename, '御魂') 192 | mitama_data = dict() 193 | data_len = len(data_format.MITAMA_COL_NAME_ZH) 194 | 195 | next(rows_data) # skip first row 196 | for r_data in rows_data: 197 | serial = convert_int(r_data[0].value) 198 | if skip_serial(serial, ignore_serial): 199 | continue 200 | 201 | data = {data_format.MITAMA_COL_NAME_ZH[1]: r_data[1].value} 202 | 203 | for i in range(2, data_len): 204 | prop_name = data_format.MITAMA_COL_NAME_ZH[i] 205 | data[prop_name] = float(r_data[i].value) if r_data[i].value else 0 206 | 207 | if serial in mitama_data: 208 | print('Mitama serial must be unique %s' % serial) 209 | raise ValueError 210 | mitama_data.setdefault(serial, data) 211 | 212 | return mitama_data 213 | 214 | 215 | def get_mitama_data(filename, ignore_serial): 216 | ext_name = os.path.splitext(filename)[1] 217 | if ext_name == '.xls': 218 | return get_mitama_data_xls(filename, ignore_serial) 219 | elif ext_name == '.json': 220 | return get_mitama_data_json(filename, ignore_serial) 221 | else: 222 | print("Unsupported file type!") 223 | 224 | return dict() 225 | 226 | 227 | def skip_serial(serial, ignore_list): 228 | if not ignore_list: 229 | return False 230 | 231 | serial = str(serial) 232 | for ig in ignore_list: 233 | if not ig: 234 | continue 235 | if ig.isdigit(): 236 | if ig == serial: 237 | # pure digit number as serial must be equal 238 | return True 239 | elif ig in serial: 240 | return True 241 | return False 242 | 243 | 244 | def sep_mitama_by_loc(mitama_data): 245 | mitama_loc_data = {1: [], 2: [], 3: [], 246 | 4: [], 5: [], 6: []} 247 | 248 | for d_k, d_v in mitama_data.items(): 249 | loc = d_v['位置'] 250 | if loc not in mitama_loc_data: 251 | print('Mitama location must be 1 to 6, please check your data.') 252 | mitama_loc_data[loc].append({d_k: d_v}) 253 | 254 | return mitama_loc_data 255 | 256 | 257 | if __name__ == '__main__': 258 | # for test 259 | test_file = './example/data_Template.xls' 260 | d = get_mitama_data(test_file, []) 261 | print(d) 262 | l_d = sep_mitama_by_loc(d) 263 | print(l_d) 264 | 265 | from calculator_of_Onmyoji import write_data 266 | write_data.write_original_mitama_data('test.xls', d) 267 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/write_data.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import xlwt 4 | 5 | from calculator_of_Onmyoji import data_format 6 | 7 | 8 | MAX_ROW = 65532 # divided by 6 for each result is a combination of 6 details 9 | 10 | 11 | def write_mitama_result(filename, comb_data_list, es_prop, 12 | base_att=0, base_hp=0, attack_buff=0, base_critdamage=0): 13 | workbook = xlwt.Workbook(encoding='utf-8') 14 | result_num = 0 15 | detail_sheet_num = 0 16 | 17 | result_sheet = workbook.add_sheet('result') 18 | detail_sheet = workbook.add_sheet('detail_%s' % detail_sheet_num) 19 | detail_sheet_num += 1 20 | 21 | write_header_row(result_sheet, 'result') 22 | write_header_row(detail_sheet, 'detail') 23 | 24 | es_col = data_format.RESULT_HEADER_LEN 25 | 26 | if es_prop: 27 | detail_sheet.write(0, es_col, label='极品度') 28 | 29 | result_row = 1 30 | detail_row = 1 31 | serial_num = 1 32 | try: 33 | for comb_data in comb_data_list: 34 | if result_row > MAX_ROW: 35 | print('Too many results, please enhance restrictive' 36 | 'condition.') 37 | break 38 | 39 | result_num += 1 40 | # first row of each comb_data is sum info 41 | sum_data = comb_data.get('sum', {}) 42 | # first colume of a mitama_comb is serial number 43 | result_sheet.write(result_row, 0, label=serial_num) 44 | result_sheet.write(result_row, 2, label='sum') 45 | write_mitama_row(result_sheet, sum_data, result_row, start_col=4) 46 | write_extend_col(result_sheet, result_row, base_att, base_hp, 47 | attack_buff, base_critdamage) 48 | result_row += 1 49 | 50 | # write each mitama data into detail file 51 | mitama_data = comb_data.get('info', set()) 52 | serial_keys = [] 53 | for mitama in mitama_data: 54 | mitama_serial = list(mitama.keys())[0] 55 | serial_keys.append(str(mitama_serial)) 56 | mitama_prop = mitama[mitama_serial] 57 | detail_sheet.write(detail_row, 0, label=serial_num) 58 | detail_sheet.write(detail_row, 1, label=mitama_serial) 59 | write_mitama_row(detail_sheet, mitama_prop, 60 | detail_row, start_col=2) 61 | 62 | if es_prop: 63 | prop_num = cal_es_prop_num(mitama_prop, es_prop) 64 | detail_sheet.write(detail_row, es_col, label=prop_num) 65 | 66 | detail_row += 1 67 | 68 | # mimata serial in result sheet is the comb of mitama_data serials 69 | str_serial_keys = ','.join(serial_keys) 70 | result_sheet.write(result_row-1, 1, label=str_serial_keys) 71 | 72 | if detail_row > MAX_ROW: 73 | detail_sheet = workbook.add_sheet('detail_%s' 74 | % detail_sheet_num) 75 | write_header_row(detail_sheet, 'detail') 76 | detail_sheet_num += 1 77 | detail_row = 1 78 | 79 | serial_num += 1 80 | except KeyboardInterrupt: 81 | print('\nRecieve SIGINT, stop.') 82 | 83 | workbook.save(filename) 84 | print("We got %s results" % result_num) 85 | 86 | return result_num 87 | 88 | 89 | def write_header_row(worksheet, sheet_type): 90 | if sheet_type == 'result': 91 | header_row = data_format.RESULT_HEADER + data_format.EXTEND_HEADER 92 | elif sheet_type == 'detail': 93 | header_row = data_format.RESULT_HEADER 94 | elif sheet_type == 'result_combs': 95 | header_row = data_format.RESULT_COMB_HEADER 96 | elif sheet_type == 'data_with_esp': 97 | header_row = (data_format.MITAMA_COL_NAME_ZH + 98 | data_format.MITAMA_ESP_EXTEND_HEADER) 99 | else: 100 | header_row = data_format.MITAMA_COL_NAME_ZH 101 | 102 | col_nums = len(header_row) 103 | for c in range(col_nums): 104 | worksheet.write(0, c, label=header_row[c]) 105 | 106 | 107 | def write_mitama_row(worksheet, comb_prop, row_num, start_col, 108 | header_key=data_format.RESULT_HEADER): 109 | 110 | for col in range(start_col, len(header_key)): 111 | cell_data = comb_prop.get(header_key[col]) 112 | worksheet.write(row_num, col, label=cell_data) 113 | 114 | 115 | def write_extend_col(worksheet, row_num, base_att, base_hp, attack_buff, base_critdamage): 116 | start_col = data_format.RESULT_HEADER_LEN 117 | str_row_num = str(row_num + 1) # excel行名称编号比行号大1 118 | # EXTEND_COL: u'式神基础攻击', u'式神基础生命', u'式神基础暴伤', 119 | # u'总攻击', u'总生命', 120 | # u'攻击x暴伤', u'生命×暴伤' 121 | worksheet.write(row_num, start_col, label=base_att) 122 | worksheet.write(row_num, start_col+1, label=base_hp) 123 | worksheet.write(row_num, start_col+2, label=base_critdamage) 124 | 125 | # 总攻击 = 基础攻击 * (1 + 攻击加成/100) + 御魂攻击 126 | base_att_col_name = data_format.EXTEND_INDEX['式神基础攻击'] + str_row_num 127 | att_enhance_col_name = data_format.RESULT_INDEX['攻击加成'] + str_row_num 128 | mitama_att_col_name = data_format.RESULT_INDEX['攻击'] + str_row_num 129 | formula_att = '%s*(1+%s/100)+%s' % (base_att_col_name, 130 | att_enhance_col_name, 131 | mitama_att_col_name) 132 | worksheet.write(row_num, start_col+3, xlwt.Formula(formula_att)) 133 | 134 | # 总生命 = 基础生命 * (1 + 生命加成/100) + 御魂生命 135 | base_hp_col_name = data_format.EXTEND_INDEX['式神基础生命'] + str_row_num 136 | hp_enhance_col_name = data_format.RESULT_INDEX['生命加成'] + str_row_num 137 | mitama_hp_col_name = data_format.RESULT_INDEX['生命'] + str_row_num 138 | formula_hp = '%s*(1+%s/100)+%s' % (base_hp_col_name, 139 | hp_enhance_col_name, 140 | mitama_hp_col_name) 141 | worksheet.write(row_num, start_col+4, xlwt.Formula(formula_hp)) 142 | 143 | # 攻击×暴伤 = 总攻击 * (基础暴伤+御魂暴伤)/100 144 | total_att_col_name = data_format.EXTEND_INDEX['总攻击'] + str_row_num 145 | base_crit_damage_col_name = (data_format.EXTEND_INDEX['式神基础暴伤'] + 146 | str_row_num) 147 | mitama_crit_damage_col_name = (data_format.RESULT_INDEX['暴击伤害'] + 148 | str_row_num) 149 | formula_att_crit = '%s*(%s+%s)/100' % ( 150 | total_att_col_name, 151 | base_crit_damage_col_name, 152 | mitama_crit_damage_col_name 153 | ) 154 | worksheet.write(row_num, start_col+5, xlwt.Formula(formula_att_crit)) 155 | 156 | # 生命×暴伤 = 总生命 * (基础暴伤+御魂暴伤)/100 157 | total_hp_col_name = data_format.EXTEND_INDEX['总生命'] + str_row_num 158 | formula_hp_crit = '%s*(%s+%s)/100' % (total_hp_col_name, 159 | base_crit_damage_col_name, 160 | mitama_crit_damage_col_name) 161 | worksheet.write(row_num, start_col+6, xlwt.Formula(formula_hp_crit)) 162 | 163 | # 攻击×暴伤(+buff) = (总攻击 + (基础攻击*攻击buff/100))* (基础暴伤+御魂暴伤)/100 164 | total_att_col_name = data_format.EXTEND_INDEX['总攻击'] + str_row_num 165 | base_crit_damage_col_name = (data_format.EXTEND_INDEX['式神基础暴伤'] + 166 | str_row_num) 167 | mitama_crit_damage_col_name = (data_format.RESULT_INDEX['暴击伤害'] + 168 | str_row_num) 169 | formula_att_crit = '(%s+%s*%s/100)*(%s+%s)/100' % ( 170 | total_att_col_name, 171 | base_att_col_name, 172 | attack_buff, 173 | base_crit_damage_col_name, 174 | mitama_crit_damage_col_name 175 | ) 176 | worksheet.write(row_num, start_col+7, xlwt.Formula(formula_att_crit)) 177 | 178 | 179 | def write_original_mitama_data(filename, data, esps_show=False): 180 | workbook = xlwt.Workbook(encoding='utf-8') 181 | 182 | data_sheet = workbook.add_sheet('御魂') 183 | if esps_show: 184 | write_header_row(data_sheet, 'data_with_esp') 185 | else: 186 | write_header_row(data_sheet, 'data') 187 | 188 | row = 1 189 | 190 | for serial, prop in data.items(): 191 | data_sheet.write(row, 0, label=serial) 192 | write_mitama_row(data_sheet, prop, row, 1, 193 | header_key=data_format.MITAMA_COL_NAME_ZH) 194 | 195 | col_esp_extend = len(data_format.MITAMA_COL_NAME_ZH) 196 | if esps_show: 197 | for esp_main in data_format.MITAMA_ESP_EXTEND_HEADER: 198 | es_prop = data_format.MITAMA_ESP[esp_main] 199 | prop_num = cal_es_prop_num(prop, es_prop) 200 | data_sheet.write(row, col_esp_extend, label=prop_num) 201 | col_esp_extend += 1 202 | 203 | row += 1 204 | 205 | ignore_sheet = workbook.add_sheet('已使用') 206 | write_header_row(ignore_sheet, 'detail') 207 | 208 | workbook.save(filename) 209 | 210 | 211 | def cal_es_prop_num(mitama_prop, effect_second_prop): 212 | mitama_growth = data_format.MITAMA_GROWTH 213 | prop_num = 0 214 | for select_prop in effect_second_prop: 215 | prop_value = mitama_prop.get(select_prop, 0.0) 216 | main_prop_value = mitama_growth[select_prop]["主属性"] 217 | max_prop_value = mitama_growth[select_prop]["副属性最大值"] 218 | if prop_value >= max_prop_value: 219 | prop_value -= main_prop_value 220 | prop_num += prop_value / mitama_growth[select_prop]["最小成长值"] 221 | 222 | if prop_num < 0: 223 | prop_num = 0 224 | 225 | return prop_num 226 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/result_combination.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding:utf-8 -*- 3 | 4 | import codecs 5 | import csv 6 | from itertools import combinations 7 | from math import factorial 8 | import multiprocessing 9 | import os 10 | import traceback 11 | 12 | from tqdm import tqdm 13 | import xlrd 14 | from xlutils.copy import copy 15 | 16 | from calculator_of_Onmyoji import data_format 17 | from calculator_of_Onmyoji import load_data 18 | from calculator_of_Onmyoji import write_data 19 | 20 | 21 | class ResultBook(object): 22 | def __init__(self, filename, postfix): 23 | self.filename = filename 24 | self.postfix = str(postfix) 25 | 26 | read_book = xlrd.open_workbook(filename=self.filename) 27 | self.write_book = copy(read_book) 28 | 29 | self.work_sheet = None 30 | self.work_sheet_num = 0 31 | self.row_num = 0 32 | self.count = 0 33 | 34 | def init_work_sheet(self): 35 | self.work_sheet = self.write_book.add_sheet('independent_combs_%s' 36 | % self.work_sheet_num) 37 | self.work_sheet_num += 1 38 | 39 | write_data.write_header_row(self.work_sheet, 'result_combs') 40 | self.row_num = 1 41 | 42 | def write(self, comb_data): 43 | if not self.work_sheet or self.row_num > write_data.MAX_ROW: 44 | self.init_work_sheet() 45 | 46 | col_num = 0 47 | for col_name in data_format.RESULT_COMB_HEADER: 48 | self.work_sheet.write(self.row_num, col_num, 49 | comb_data.get(col_name, '')) 50 | col_num += 1 51 | 52 | self.row_num += 1 53 | self.count += 1 54 | 55 | def save(self): 56 | file_name, file_extension = os.path.splitext(self.filename) 57 | result_file = ''.join([file_name, '-comb', '_', self.postfix, 58 | file_extension]) 59 | 60 | self.write_book.save(result_file) 61 | 62 | 63 | class ResultBookCSV(object): 64 | """Write comb result into .csv file. 65 | 66 | Attributes: 67 | count (int): comb nums of result book. 68 | filename (str): File name 69 | postfix (str or int): postfix to seperate this file from 70 | original mitama combs. 71 | write_book (_io.TextIOWrapper): file descriptor. 72 | writer (csv.DictWriter): csv.DictWriter 73 | """ 74 | 75 | def __init__(self, filename, postfix): 76 | self.postfix = str(postfix) 77 | self.filename = filename[:-4] + '-comb_' + self.postfix + '.csv' 78 | # Avoid messy code in win platform 79 | with open(self.filename, 'wb') as fd: 80 | fd.write(codecs.BOM_UTF8) 81 | self.write_book = open(self.filename, 'a', 82 | newline='', encoding='utf-8') 83 | self.writer = csv.DictWriter(self.write_book, 84 | data_format.RESULT_COMB_HEADER) 85 | self.writer.writeheader() 86 | self.count = 0 87 | 88 | def write(self, comb_data): 89 | self.writer.writerow(comb_data) 90 | self.count += 1 91 | 92 | def save_and_close(self): 93 | self.write_book.close() 94 | 95 | 96 | class MakeResultInPool(object): 97 | def __init__(self, filename, postfix): 98 | self.filename = filename 99 | self.postfix = str(postfix) 100 | 101 | self.result_book = dict() 102 | 103 | def _get_result_book(self): 104 | pid = os.getpid() 105 | res_book = self.result_book.get(pid) 106 | if not res_book: 107 | res_book = ResultBook(self.filename, 108 | ''.join([str(pid), '_', self.postfix])) 109 | self.result_book[pid] = res_book 110 | return res_book 111 | 112 | def __call__(self, mitama_combs): 113 | comb_data = get_independent_comb_data(mitama_combs) 114 | if comb_data: 115 | res_book = self._get_result_book() 116 | res_book.write(comb_data) 117 | 118 | def save(self): 119 | for res_book in self.result_book.values(): 120 | res_book.save() 121 | 122 | @property 123 | def count(self): 124 | count = 0 125 | for res_book in self.result_book.values(): 126 | count += res_book.count 127 | return count 128 | 129 | 130 | def load_result_sheet(filename): 131 | xls_book = xlrd.open_workbook(filename=filename, on_demand=True) 132 | data_sheet = xls_book.sheet_by_name('result') 133 | rows_data = data_sheet.get_rows() 134 | 135 | comb_dict_keys = [] 136 | r_data = next(rows_data) # first row is key name 137 | for k in r_data: 138 | comb_dict_keys.append(k.value) 139 | 140 | mitama_combs = [] 141 | 142 | for r_data in rows_data: 143 | combs_data = dict() 144 | for i in range(len(comb_dict_keys)): 145 | k = comb_dict_keys[i] 146 | if k == '组合序号': 147 | try: 148 | combs_data[k] = int(r_data[i].value) 149 | except ValueError: 150 | combs_data[k] = r_data[i].value 151 | elif k == '御魂序号': 152 | # 直接转换为set,减少循环内的计算 153 | combs_data[k] = set(r_data[i].value.split(',')) 154 | else: 155 | combs_data[k] = r_data[i].value 156 | 157 | mitama_combs.append(combs_data) 158 | 159 | return mitama_combs 160 | 161 | 162 | def load_comb_result_sheet(filename): 163 | """load .csv comb file into memory. Not used in this version. 164 | 165 | Args: 166 | filename (str): File name to be loaded 167 | 168 | Returns: 169 | combs_data(list of OrderedDict): data of mitama combs. 170 | """ 171 | print("Loading previous result: %s" % filename) 172 | with open(filename, 'r') as fd: 173 | reader = csv.DictReader(fd) 174 | # print(reader.fieldnames) 175 | combs_data = list(reader) 176 | return combs_data 177 | 178 | 179 | def get_independent_comb_data(mitama_combs): 180 | seed_serials = set() 181 | 182 | for combs_data in mitama_combs: 183 | mitama_serials = combs_data.get('御魂序号') 184 | if seed_serials & mitama_serials: 185 | return None 186 | else: 187 | seed_serials |= mitama_serials 188 | 189 | return gen_result_comb_data(mitama_combs) 190 | 191 | 192 | def make_independent_comb(file_name, mitama_combs, sub_comb_length, cores): 193 | '''遍历组合,找出独立组合并触发写数据''' 194 | n = len(mitama_combs) 195 | total = cal_comb_num(n, sub_comb_length) 196 | print('Calculating C(%s, %s) = %s' % (n, sub_comb_length, total)) 197 | 198 | if cores > 1: 199 | p = multiprocessing.Pool(processes=cores) 200 | # TODO(jjs): 这种方式性能太差,改用Queue模式 201 | make_comb_data_parallel = MakeResultInPool(file_name, sub_comb_length) 202 | for _ in tqdm(p.imap_unordered(make_comb_data_parallel, 203 | combinations(mitama_combs, 204 | sub_comb_length)), 205 | desc='Calculating', total=total, unit='comb'): 206 | pass 207 | p.close() 208 | p.join() 209 | del p 210 | make_comb_data_parallel.save() 211 | return make_comb_data_parallel.count 212 | else: 213 | result_book = ResultBookCSV(file_name, sub_comb_length) 214 | for combs in tqdm(combinations(mitama_combs, sub_comb_length), 215 | desc='Calculating', total=total, unit='comb'): 216 | comb_data = get_independent_comb_data(combs) 217 | if comb_data: 218 | result_book.write(comb_data) 219 | 220 | result_book.save_and_close() 221 | return result_book.count 222 | 223 | 224 | def cal_comb_num(n, m): 225 | '''计算组合数 226 | 227 | C(n, m) = n!/((n-m)! * m!) 228 | ''' 229 | x = 1 230 | for i in range(n, m, -1): 231 | x *= i 232 | x /= factorial(n - m) 233 | 234 | return x 235 | 236 | 237 | def make_all_independent_combs(file_name, mitama_combs, expect_counts, 238 | use_multi_process): 239 | if use_multi_process: 240 | cores = multiprocessing.cpu_count() 241 | else: 242 | cores = 1 243 | 244 | print('Use CPU cores number %s' % cores) 245 | if expect_counts == 0: 246 | combs_count = 0 247 | for c in range(2, len(mitama_combs)): 248 | # 从2开始遍历,直至无法再找到独立组合 249 | res_count = make_independent_comb(file_name, 250 | mitama_combs, c, cores) 251 | if res_count == 0: 252 | break 253 | combs_count += res_count 254 | else: 255 | combs_count = make_independent_comb(file_name, 256 | mitama_combs, expect_counts, cores) 257 | 258 | return combs_count 259 | 260 | 261 | def gen_result_comb_data(independent_comb): 262 | result_comb_data = {'组合个数': len(independent_comb)} 263 | 264 | for key in data_format.RESULT_COMB_HEADER[1:]: 265 | result_comb_data[key] = '#'.join([str(d.get(key, 0)) 266 | for d in independent_comb]) 267 | 268 | return result_comb_data 269 | 270 | 271 | def input_expect_combs_counts(): 272 | prompt = '请输入期望的独立套装个数并回车(0为计算所有可能): ' 273 | inputchar = input(prompt) 274 | try: 275 | expect_counts = int(inputchar) 276 | if expect_counts < 2 and expect_counts != 0: 277 | raise ValueError 278 | except Exception: 279 | print('输入必须为0或大于等于2的整数') 280 | os.exit(1) 281 | return expect_counts 282 | 283 | 284 | def input_use_multi_process(): 285 | inputchar = input('是否使用多进程计算(电脑会比较卡) y/n: ') 286 | if inputchar.strip().lower() == 'y': 287 | return True 288 | else: 289 | return False 290 | 291 | 292 | def main(): 293 | xls_files = load_data.get_ext_files('.xls') 294 | result_files = [f for f in xls_files 295 | if '-result' in f and 'comb' not in f] 296 | 297 | if not result_files: 298 | print('No files with postfix "-result.xls" found.') 299 | return 300 | 301 | print('Files below will be calculated:\n%s\n' % result_files) 302 | expect_counts = input_expect_combs_counts() 303 | # TODO(jjs): 将更多步骤放到imap里面去做,然后再打开多进程开关 304 | # use_multi_process = input_use_multi_process() 305 | use_multi_process = False 306 | 307 | for file_name in result_files: 308 | print('Calculating %s' % file_name) 309 | mitama_combs = load_result_sheet(file_name) 310 | combs_count = make_all_independent_combs(file_name, 311 | mitama_combs, expect_counts, 312 | use_multi_process) 313 | 314 | print('Calculating finish, get %s independent combinations' 315 | % combs_count) 316 | 317 | 318 | if __name__ == '__main__': 319 | multiprocessing.freeze_support() # For windows platform 320 | 321 | try: 322 | main() 323 | except Exception: 324 | print(traceback.format_exc()) 325 | finally: 326 | input('\nPress any key to exit') 327 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/cal_mitama.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding: utf-8 3 | 4 | import argparse 5 | 6 | from calculator_of_Onmyoji import algorithm 7 | from calculator_of_Onmyoji import load_data 8 | from calculator_of_Onmyoji import write_data 9 | 10 | 11 | def str2bool(v): 12 | if v.lower() in ('yes', 'true', 't', 'y', '1'): 13 | return True 14 | elif v.lower() in ('no', 'false', 'f', 'n', '0'): 15 | return False 16 | else: 17 | raise argparse.ArgumentTypeError('Boolean value expected.') 18 | 19 | 20 | def sep_utf_str(uni_str): 21 | if ',' in uni_str: 22 | return uni_str.split(',') 23 | else: 24 | return [uni_str] 25 | 26 | 27 | def sep_utf_str_to_list(uni_str): 28 | if not uni_str: 29 | return list() 30 | 31 | if '#' in uni_str: 32 | limit_list = uni_str.split('#') 33 | else: 34 | limit_list = uni_str.split('.') 35 | formated_list = list() 36 | for limit in limit_list: 37 | if ',' not in limit: 38 | continue 39 | key, value = limit.split(',') 40 | if key: 41 | formated_list.append((key, int(value))) 42 | return formated_list 43 | 44 | 45 | def sep_utf_str_to_dict(uni_str): 46 | if not uni_str: 47 | return dict() 48 | 49 | if '#' in uni_str: 50 | limit_list = uni_str.split('#') 51 | else: 52 | limit_list = uni_str.split('.') 53 | formated_dict = dict() 54 | for limit in limit_list: 55 | if ',' not in limit: 56 | continue 57 | key, value = limit.split(',') 58 | if key: 59 | formated_dict[key] = float(value) 60 | return formated_dict 61 | 62 | 63 | class Calculator(object): 64 | def __init__(self, params=None): 65 | if not params: 66 | args = self._get_args() 67 | print('Input args: %s' % args) 68 | else: 69 | print('Input params: %s' % params) 70 | args = self._get_params(params) 71 | self._init_attr(args) 72 | self.combs = None 73 | 74 | def _init_attr(self, args): 75 | self.file_name = args.source_data 76 | self.output_file = args.output_file 77 | 78 | self.mitama_type_limit = sep_utf_str_to_list(args.mitama_suit) 79 | self.prop_limit = sep_utf_str_to_dict(args.prop_limit) 80 | self.upper_prop_limit = sep_utf_str_to_dict(args.upper_prop_limit) 81 | 82 | self.l2_prop_limit = sep_utf_str_to_dict(args.sec_prop_value) 83 | self.l4_prop_limit = sep_utf_str_to_dict(args.fth_prop_value) 84 | self.l6_prop_limit = sep_utf_str_to_dict(args.sth_prop_value) 85 | 86 | self.all_suit = args.all_suit 87 | self.attack_only = args.attack_only 88 | 89 | if args.effective_secondary_prop: 90 | self.es_prop = sep_utf_str(args.effective_secondary_prop) 91 | self.es_prop_num = \ 92 | list(map(int, sep_utf_str(args.effective_secondary_prop_num))) 93 | else: 94 | self.es_prop = None 95 | self.es_prop_num = None 96 | 97 | self.ignore_serial = sep_utf_str(args.ignore_serial) 98 | 99 | self.base_att, self.base_critdamage_att, self.damage_limit = \ 100 | list(map(float, sep_utf_str(args.damage_limit))) 101 | 102 | self.base_hp, self.base_critdamage_hp, self.hp_crit_limit = \ 103 | list(map(float, sep_utf_str(args.health_limit))) 104 | 105 | if (self.base_critdamage_att and self.base_critdamage_hp and 106 | self.base_critdamage_att != self.base_critdamage_hp): 107 | print('WARN: crit_damage between DL and HL is different') 108 | 109 | if self.base_critdamage_att: 110 | self.base_critdamage = self.base_critdamage_att 111 | else: 112 | self.base_critdamage = self.base_critdamage_hp 113 | 114 | self.attack_buff = args.attack_buff 115 | 116 | def _get_args(self): 117 | parser = argparse.ArgumentParser() 118 | parser.add_argument("source_data", 119 | type=str, 120 | help='御魂数据,格式参照example/data_Template.xls') 121 | parser.add_argument("output_file", 122 | type=str, 123 | help='输出文件位置,格式为pathto/filename.xls') 124 | parser.add_argument("-M", "--mitama-suit", 125 | type=str, 126 | default=',0', 127 | help='期望的x件套御魂类型或者加成类型,' 128 | '多个限制用英文句号#间隔,' 129 | '例如"-M 针女,4"为针女至少4件,' 130 | '"-M 针女,4#破势,2"为针女4件+破势2件,' 131 | '"-M 生命加成,2#生命加成,2#生命加成,2"为3个生命两件套') 132 | parser.add_argument("-P", "--prop-limit", 133 | type=str, 134 | default='', 135 | help='期望限制的属性下限,多个属性条件用英文句号#间隔, ' 136 | '例如"-P 暴击,90#暴击伤害,70"为暴击至少90' 137 | '且暴击伤害至少70') 138 | parser.add_argument("-UP", "--upper-prop-limit", 139 | type=str, 140 | default='', 141 | help='期望限制的属性上限,多个属性条件用英文句号#间隔,' 142 | '例如"-UP 暴击,95#速度,20"为暴击最多95' 143 | '且速度最多20') 144 | parser.add_argument("-2P", "--sec-prop-value", 145 | type=str, 146 | default=',0', 147 | help='二号位限制的属性类型和数值,' 148 | '多个属性用英文句号#间隔,' 149 | '例如"-2P 攻击加成,55#速度,57"为二号位攻击加成至少55或速度至少为57') 150 | parser.add_argument("-4P", "--fth-prop-value", 151 | type=str, 152 | default=',0', 153 | help='四号位限制的属性类型和数值,' 154 | '多个属性用英文句号#间隔,' 155 | '例如"-4P 攻击加成,55"为四号位攻击加成至少55') 156 | parser.add_argument("-6P", "--sth-prop-value", 157 | type=str, 158 | default=',0', 159 | help='六号位限制的属性类型和数值,' 160 | '多个属性用英文句号#间隔,' 161 | '例如"-6P 暴击,55"为六号位暴击至少55,' 162 | '"-6P 暴击,55#暴击伤害,89"为六号位暴击至少55' 163 | '或暴击伤害至少89') 164 | parser.add_argument("-IG", "--ignore-serial", 165 | type=str, 166 | default='', 167 | help='忽略的御魂序号关键字,用逗号,间隔' 168 | '例如"-IG 天狗,鸟"为御魂序号包含天狗或鸟则滤除') 169 | parser.add_argument("-AS", "--all-suit", 170 | type=str2bool, 171 | default=True, 172 | help='是否全为套装,默认为True。' 173 | '"-AS False"为允许非套装的组合出现,如5针女1破势') 174 | parser.add_argument("-DL", "--damage-limit", 175 | type=str, 176 | default='0,0,0', 177 | help='基础攻击,基础暴伤,期望的攻击*暴伤,' 178 | '例如"-DL 3126,150,20500",当基础攻击为3126,' 179 | '基础暴伤为150,攻击*暴伤>=20500') 180 | parser.add_argument("-HL", "--health-limit", 181 | type=str, 182 | default='0,0,0', 183 | help='基础生命,基础暴伤,期望的生命*暴伤,' 184 | '例如"-HL 8000,150,60000",当基础生命为8000,' 185 | '基础暴伤为150,生命*暴伤>=60000') 186 | parser.add_argument("-AO", "--attack-only", 187 | type=str2bool, 188 | default=False, 189 | help='是否只计算输出类御魂,默认为False。' 190 | '"-AO True"为只计算套装属性为攻击加成、' 191 | '暴击和首领御魂的套装组合') 192 | parser.add_argument("-ESP", "--effective-secondary-prop", 193 | type=str, 194 | default='', 195 | help='设定御魂的有效副属性,用逗号,间隔' 196 | '例如"-ESP 暴击,暴击伤害,速度,攻击加成"' 197 | '意味着有效副属性定位为暴击、暴击伤害、速度、攻击加成') 198 | parser.add_argument("-ESPN", "--effective-secondary-prop-num", 199 | type=str, 200 | default='', 201 | help='与-ESP配合使用,限定1-6号位御魂的有效副属性加成次数(含初始次数)用逗号,间隔' 202 | '例如"-ESP 暴击,暴击伤害 -ESPN 3,3,5,3,5,0"' 203 | '意味着1、2、3、4、5、6号位御魂以暴击和暴击伤害为集合的有效属性集合,出现的总次数不低于3、3、5、3、5、0次' 204 | '以一号位举例,暴击、暴击伤害的加成次数(含初始次数)为3次的分布可能有如下情况:' 205 | '组合一:暴击*3、暴击伤害*0,即暴击+7.2、暴击伤害+0' 206 | '组合二:暴击*2、暴击伤害*1,即暴击+4.8、暴击伤害+3.2' 207 | '组合三:暴击*1、暴击伤害*2,即暴击+2.4、暴击伤害+6.4' 208 | '组合四:暴击*0、暴击伤害*3,即暴击+0 、暴击伤害+9.6') 209 | parser.add_argument("-AB", "--attack-buff", 210 | type=int, 211 | default='0', 212 | help='攻击加成,在-DL参数中计算伤害时会计入对应的攻击加成' 213 | '例如"-AB 40", 用来计算黑拉面(兔子舞+黑晴明共40%攻击加成)的面板') 214 | 215 | return parser.parse_args() 216 | 217 | def _get_params(self, param_dict): 218 | class Args(object): 219 | def __init__(self, **entries): 220 | self.__dict__.update(entries) 221 | 222 | return Args(**param_dict) 223 | 224 | def run(self): 225 | self.pre_check() 226 | origin_data = load_data.get_mitama_data(self.file_name, 227 | self.ignore_serial) 228 | print('Loading data finish') 229 | 230 | locate_sep_data = load_data.sep_mitama_by_loc(origin_data) 231 | 232 | print('Start calculating') 233 | self.combs = algorithm.MitamaComb(locate_sep_data, 234 | self.l2_prop_limit, 235 | self.l4_prop_limit, 236 | self.l6_prop_limit, 237 | self.mitama_type_limit, 238 | self.all_suit, 239 | self.attack_only, 240 | self.es_prop, self.es_prop_num, 241 | self.prop_limit, 242 | self.upper_prop_limit, 243 | self.base_att, self.base_critdamage, 244 | self.damage_limit, self.attack_buff, 245 | self.base_hp, self.hp_crit_limit) 246 | 247 | filter_result = self.combs.get_comb() 248 | 249 | result_num = write_data.write_mitama_result(self.output_file, 250 | filter_result, 251 | self.es_prop, 252 | self.base_att, 253 | self.base_hp, 254 | self.attack_buff, 255 | self.base_critdamage) 256 | 257 | return result_num 258 | 259 | def pre_check(self): 260 | # check input_file exist 261 | with open(self.file_name, 'r'): 262 | pass 263 | # check output_file is writable 264 | with open(self.output_file, 'w+') as fd: 265 | fd.write('pre_check') 266 | 267 | def get_progress(self): 268 | if not self.combs: 269 | return 0, 0, 0 270 | return self.combs.get_progress() 271 | 272 | 273 | if __name__ == '__main__': 274 | calculator = Calculator() 275 | calculator.run() 276 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | calculator_of_Onmyoji is a calculator for mitama combinations 294 | in game Onmyoji. 295 | Copyright (C) 2018 junsu.jia@gmail.com 296 | 297 | This program is free software; you can redistribute it and/or modify 298 | it under the terms of the GNU General Public License as published by 299 | the Free Software Foundation; either version 2 of the License, or 300 | (at your option) any later version. 301 | 302 | This program is distributed in the hope that it will be useful, 303 | but WITHOUT ANY WARRANTY; without even the implied warranty of 304 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 305 | GNU General Public License for more details. 306 | 307 | You should have received a copy of the GNU General Public License along 308 | with this program; if not, write to the Free Software Foundation, Inc., 309 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Lesser General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/algorithm.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import itertools 4 | import sys 5 | 6 | from calculator_of_Onmyoji import data_format 7 | from functools import reduce 8 | 9 | 10 | class MitamaComb(object): 11 | def __init__(self, locate_sep_data, 12 | l2_prop_limit, l4_prop_limit, l6_prop_limit, 13 | mitama_type_limit, all_suit, attack_only, 14 | es_prop, es_prop_num, 15 | prop_limit, upper_prop_limit, 16 | base_att, base_critdamage, damage_limit, attack_buff, 17 | base_hp, hp_crit_limit): 18 | 19 | self.locate_sep_data = locate_sep_data 20 | self.l2_prop_limit = l2_prop_limit 21 | self.l4_prop_limit = l4_prop_limit 22 | self.l6_prop_limit = l6_prop_limit 23 | self.mitama_type_limit = mitama_type_limit 24 | self.all_suit = all_suit 25 | self.attack_only = attack_only 26 | self.es_prop = es_prop 27 | self.es_prop_num = es_prop_num 28 | self.prop_limit = prop_limit 29 | self.upper_prop_limit = upper_prop_limit 30 | self.base_att = base_att 31 | self.base_critdamage = base_critdamage 32 | self.damage_limit = damage_limit 33 | self.attack_buff = attack_buff 34 | self.base_hp = base_hp 35 | self.hp_crit_limit = hp_crit_limit 36 | 37 | self.total_comb = 0 38 | self.calculated_count = 0 39 | 40 | def filter_loc_and_type(self): 41 | print('mitama nums by loc is %s' 42 | % str([len(d) for d in list(self.locate_sep_data.values())])) 43 | 44 | if self.attack_only: 45 | # 只计算输出类御魂,已选定的御魂套装不过滤 46 | selected_types = [t for t, _ in self.mitama_type_limit] 47 | filter_types = selected_types + data_format.ATTACK_MITAMA_TYPE 48 | for loc, data in self.locate_sep_data.items(): 49 | self.locate_sep_data[loc] = \ 50 | self.filter_mitama_type(data, filter_types) 51 | 52 | # 246号位主属性过滤 53 | if self.l2_prop_limit: 54 | self.locate_sep_data[2] = \ 55 | self.filter_loc_prop(self.locate_sep_data[2], 56 | self.l2_prop_limit) 57 | if self.l4_prop_limit: 58 | self.locate_sep_data[4] = \ 59 | self.filter_loc_prop(self.locate_sep_data[4], 60 | self.l4_prop_limit) 61 | if self.l6_prop_limit: 62 | self.locate_sep_data[6] = \ 63 | self.filter_loc_prop(self.locate_sep_data[6], 64 | self.l6_prop_limit) 65 | 66 | # 全位置副属性过滤 67 | if self.es_prop and self.es_prop_num: 68 | for loc, data_list in self.locate_sep_data.items(): 69 | self.locate_sep_data[loc] = \ 70 | self.filter_effective_secondary_prop( 71 | data_list, self.es_prop_num[loc-1]) 72 | 73 | print('after filter by loc prop and type %s' 74 | % str([len(d) for d in list(self.locate_sep_data.values())])) 75 | 76 | self.locate_sep_data = dict(zip(range(1, 7), 77 | [d for d in 78 | self.locate_sep_data.values()])) 79 | 80 | def find_mtype_candidates(self, mitama_type='ALL'): 81 | candidates = [] 82 | if mitama_type == 'ALL': 83 | candidates = data_format.MITAMA_TYPES 84 | elif mitama_type in data_format.MITAMA_TYPES: 85 | candidates.append(mitama_type) 86 | elif mitama_type in data_format.MITAMA_PROPS: 87 | for m_type in data_format.MITAMA_TYPES: 88 | if (data_format.MITAMA_ENHANCE[m_type]["加成类型"] == 89 | mitama_type or 90 | not data_format.MITAMA_ENHANCE[m_type]["加成类型"]): 91 | candidates.append(m_type) 92 | return candidates 93 | 94 | def gen_mitama_combos(self): 95 | main_type, secondary_type = None, [] 96 | fixed_pos = 0 97 | for m_type, limit_count in self.mitama_type_limit: 98 | if m_type not in (data_format.MITAMA_TYPES + 99 | data_format.MITAMA_PROPS): 100 | continue 101 | if limit_count == 4: 102 | main_type = m_type 103 | fixed_pos += 4 104 | elif limit_count == 2: 105 | secondary_type.append(m_type) 106 | fixed_pos += 2 107 | 108 | if main_type is not None: 109 | main_candidates = self.find_mtype_candidates(main_type) 110 | if fixed_pos == 6 or self.all_suit: # 4+2 111 | if secondary_type: 112 | secondary_candidates = \ 113 | self.find_mtype_candidates(secondary_type[0]) 114 | elif self.all_suit: 115 | secondary_candidates = self.find_mtype_candidates() 116 | for m_type in main_candidates: 117 | for s_type in secondary_candidates: 118 | yield [m_type] * 4 + [s_type] * 2 119 | else: # 4+1+1 120 | yield [m_type] * 4 + ['ALL', 'ALL'] 121 | 122 | elif fixed_pos == 6 or (self.all_suit and fixed_pos >= 2): 123 | # 2+2+2, 2+2+any, 2+any+any 124 | candidates = [] 125 | for m_type in secondary_type: 126 | candidates.append(self.find_mtype_candidates(m_type)) 127 | for i in range((6-fixed_pos)//2): 128 | candidates.append(self.find_mtype_candidates()) 129 | generated = {} 130 | for t1, t2, t3 in itertools.product(*candidates): 131 | if frozenset((t1, t2, t3)) not in generated and\ 132 | t1 != t2 and t2 != t3 and t1 != t3: 133 | generated[frozenset((t1, t2, t3))] = True 134 | yield [t1, t2, t3]*2 135 | elif fixed_pos >= 2: 136 | candidates = [] 137 | for m_type in secondary_type: 138 | candidates.append(self.find_mtype_candidates(m_type)) 139 | 140 | for i in range(6-fixed_pos): 141 | candidates.append(["ALL"]) 142 | generated = {} 143 | for combo in itertools.product(*candidates): 144 | if len(set(combo[:fixed_pos//2])) != fixed_pos//2: 145 | continue 146 | t1, t2, t3, t4, t5, t6 = (combo[:fixed_pos//2]*2 + 147 | combo[fixed_pos//2:]) 148 | if frozenset((t1, t2, t3, t4, t5, t6)) not in generated: 149 | generated[frozenset((t1, t2, t3, t4, t5, t6))] = True 150 | yield [t1, t2, t3, t4, t5, t6] 151 | else: 152 | yield None 153 | 154 | def gen_mitama_permutations(self): 155 | generated = {} 156 | for m_combos in self.gen_mitama_combos(): 157 | if m_combos is None: 158 | yield None 159 | else: 160 | for m_permu in itertools.permutations(m_combos): 161 | if tuple(m_permu) not in generated: 162 | generated[tuple(m_permu)] = True 163 | yield m_permu 164 | 165 | def make_combination(self): 166 | def filter_mitama_by_type(mitama, desired_type): 167 | mitama_info = list(mitama.values())[0] 168 | if mitama_info['御魂类型'] == desired_type: 169 | return True 170 | else: 171 | return False 172 | 173 | def classify_by_type(): 174 | mitama_data_by_type = {} 175 | for m_type in data_format.MITAMA_TYPES: 176 | mitama_data_by_type[m_type] = {} 177 | for i in range(1, 7): 178 | mitama_data_by_type[m_type][i] =\ 179 | [x for x in self.locate_sep_data[i] 180 | if filter_mitama_by_type(x, m_type)] 181 | return mitama_data_by_type 182 | 183 | mitama_data_by_type = None 184 | res = [] 185 | for m_combos in self.gen_mitama_permutations(): 186 | if m_combos is None: 187 | self.total_comb = reduce(lambda x, y: x*y, 188 | map(len, 189 | self.locate_sep_data.values())) 190 | print("Total combinations: {}".format(self.total_comb)) 191 | return itertools.product(*self.locate_sep_data.values()) 192 | else: 193 | if mitama_data_by_type is None: 194 | mitama_data_by_type = classify_by_type() 195 | mitama_grp = {x: self.locate_sep_data[x] 196 | if m_type == 'ALL' else 197 | mitama_data_by_type[m_type][x] 198 | for x, m_type in zip(range(1, 7), m_combos)} 199 | self.total_comb += reduce(lambda x, y: x*y, 200 | map(len, mitama_grp.values())) 201 | res.append(itertools.product(*mitama_grp.values())) 202 | 203 | print("Total combinations: {}".format(self.total_comb)) 204 | return itertools.chain(*res) 205 | 206 | def filter_loc_prop(self, data_list, prop_limit): 207 | def prop_value_le_min(mitama): 208 | mitama_info = list(mitama.values())[0] 209 | for prop_type, prop_min_value in prop_limit.items(): 210 | if (mitama_info.get(prop_type, 0) and 211 | mitama_info[prop_type] >= prop_min_value): 212 | return True 213 | else: 214 | return False 215 | 216 | return list(filter(prop_value_le_min, data_list)) 217 | 218 | def filter_effective_secondary_prop(self, data_list, es_prop_num): 219 | mitama_growth = data_format.MITAMA_GROWTH 220 | 221 | def es_prop_num_le_min(mitama): 222 | mitama_info = list(mitama.values())[0] 223 | prop_num = 0 224 | for prop in self.es_prop: 225 | prop_value = mitama_info.get(prop, 0.0) 226 | main_prop_value = mitama_growth[prop]["主属性"] 227 | if prop_value >= main_prop_value: 228 | prop_value -= main_prop_value 229 | prop_num += prop_value / mitama_growth[prop]["最小成长值"] 230 | 231 | if prop_num >= es_prop_num: 232 | return True 233 | else: 234 | return False 235 | 236 | return list(filter(es_prop_num_le_min, data_list)) 237 | 238 | def filter_mitama_type(self, data_list, mitama_type_list): 239 | def mitama_type_in_list(mitama): 240 | mitama_info = list(mitama.values())[0] 241 | if mitama_info.get('御魂类型', '') in mitama_type_list: 242 | return True 243 | else: 244 | return False 245 | 246 | return list(filter(mitama_type_in_list, data_list)) 247 | 248 | def filter_mitama(self, mitama_comb_list): 249 | mitama_sum_data = self.fit_mitama_type(mitama_comb_list) 250 | 251 | for prop_type, prop_min_value in self.prop_limit.items(): 252 | if prop_type in self.upper_prop_limit: 253 | prop_max_value = self.upper_prop_limit.pop(prop_type) 254 | else: 255 | prop_max_value = sys.maxsize 256 | mitama_sum_data = self.fit_prop_value(mitama_sum_data, prop_type, 257 | prop_min_value, 258 | prop_max_value) 259 | 260 | for prop_type, prop_max_value in self.upper_prop_limit.items(): 261 | mitama_sum_data = self.fit_prop_value(mitama_sum_data, prop_type, 262 | 0, prop_max_value) 263 | 264 | comb_data_list = self.cal_mitama_comb_prop(mitama_sum_data) 265 | 266 | return comb_data_list 267 | 268 | def fit_mitama_type(self, mitama_comb_list): 269 | printed_rate = 0 270 | sys.stdout.flush() 271 | 272 | for mitama_comb in mitama_comb_list: 273 | self.calculated_count += 1 274 | 275 | mitama_type_count = {} 276 | for mitama in mitama_comb: 277 | mitama_info = list(mitama.values())[0] 278 | 279 | mitama_type = mitama_info.get('御魂类型') 280 | if mitama_type not in mitama_type_count: 281 | mitama_type_count[mitama_type] = 1 282 | else: 283 | mitama_type_count[mitama_type] += 1 284 | 285 | if self.all_suit: 286 | is_suit = True 287 | for _, count in mitama_type_count.items(): 288 | if count < 2: 289 | is_suit = False 290 | if not is_suit: 291 | continue 292 | 293 | comb_data = {'sum': {'御魂计数': mitama_type_count}, 294 | 'info': mitama_comb} 295 | 296 | printed_rate = self.print_cal_rate(printed_rate) 297 | 298 | yield comb_data 299 | 300 | def fit_prop_value(self, mitama_sum_data, prop_type, min_value, max_value): 301 | mitama_enhance = data_format.MITAMA_ENHANCE 302 | 303 | for mitama_data in mitama_sum_data: 304 | mitama_type_count = mitama_data['sum'].get('御魂计数') 305 | mitama_comb = mitama_data['info'] 306 | prop_value = 0 307 | 308 | for mitama in mitama_comb: 309 | mitama_info = list(mitama.values())[0] 310 | if mitama_info.get(prop_type, 0): 311 | prop_value += mitama_info[prop_type] 312 | 313 | for m_type, m_count in mitama_type_count.items(): 314 | if m_count < 2: 315 | continue 316 | else: 317 | p_type = mitama_enhance[m_type].get('加成类型') 318 | if p_type == prop_type: 319 | multi_times = 2 if m_count == 6 else 1 # 6个御魂算2次套装 320 | prop_value += ( 321 | multi_times * mitama_enhance[m_type].get('加成数值')) 322 | 323 | if min_value <= prop_value <= max_value: 324 | yield mitama_data 325 | 326 | def cal_total_damage(self, mitama_comb): 327 | """Calculate total damage 328 | 329 | Args: 330 | mitama_comb (dict): Mitama combination 331 | base_att (float): base attack 332 | base_hitdamage (float): base critical damage 333 | 334 | Returns: 335 | float: total_damage 336 | """ 337 | sum_data = mitama_comb['sum'] 338 | m_att = float(sum_data['攻击'] if sum_data['攻击'] else 0) 339 | m_att_en = float(sum_data['攻击加成'] if sum_data['攻击加成'] else 0) 340 | m_critdamage = float(sum_data['暴击伤害'] if sum_data['暴击伤害'] else 0) 341 | total_damage = ((self.base_att * (1 + m_att_en / 100) + m_att 342 | + self.base_att * self.attack_buff / 100) * 343 | (self.base_critdamage + m_critdamage) / 100) 344 | return total_damage 345 | 346 | def cal_hp_crit(self, mitama_comb): 347 | sum_data = mitama_comb['sum'] 348 | m_hp = float(sum_data['生命'] if sum_data['生命'] else 0) 349 | m_hp_en = float(sum_data['生命加成'] if sum_data['生命加成'] else 0) 350 | m_critdamage = float(sum_data['暴击伤害'] if sum_data['暴击伤害'] else 0) 351 | total_hp_crit = ((self.base_hp * (1 + m_hp_en / 100) + m_hp) * 352 | (self.base_critdamage + m_critdamage) / 100) 353 | return total_hp_crit 354 | 355 | def fit_damage_limit(self, mitama_comb_list): 356 | return self.fit_mitama_lambda(mitama_comb_list, 357 | lambda x: self.cal_total_damage(x) >= 358 | self.damage_limit) 359 | 360 | def fit_hp_crit_limit(self, mitama_comb_list): 361 | return self.fit_mitama_lambda(mitama_comb_list, 362 | lambda x: self.cal_hp_crit(x) >= 363 | self.hp_crit_limit) 364 | 365 | def fit_mitama_lambda(self, mitama_comb_list, filter_func): 366 | """Filter the mitama combination by a customized function 367 | 368 | Args: 369 | mitama_comb_list (function): mitama combination list 370 | filter_func (function): customized function 371 | 372 | Returns: 373 | TYPE: return the filterd mitama combination list 374 | """ 375 | for mitama_comb in mitama_comb_list: 376 | if filter_func(mitama_comb): 377 | yield mitama_comb 378 | 379 | def cal_mitama_comb_prop(self, mitama_sum_data): 380 | for mitama_data in mitama_sum_data: 381 | mitama_type_count = mitama_data['sum'].get('御魂计数') 382 | mitama_comb = mitama_data['info'] 383 | 384 | comb_sum = self.sum_prop(mitama_comb, mitama_type_count) 385 | 386 | comb_data = {'sum': comb_sum, 387 | 'info': mitama_comb} 388 | yield comb_data 389 | 390 | def sum_prop(self, mitama_comb, mitama_type_count): 391 | prop_type_list = data_format.MITAMA_COL_NAME_ZH[3::] 392 | sum_result = {k: 0 for k in prop_type_list} 393 | mitama_enhance = data_format.MITAMA_ENHANCE 394 | 395 | for mitama in mitama_comb: 396 | mitama_info = list(mitama.values())[0] 397 | 398 | # 计算除套装外的总属性 399 | for prop_type in prop_type_list: 400 | if mitama_info.get(prop_type, 0): 401 | sum_result[prop_type] += mitama_info[prop_type] 402 | 403 | for m_type, m_count in mitama_type_count.items(): 404 | if m_count < 2: # 忽略套装效果 405 | continue 406 | else: 407 | multi_times = 2 if m_count == 6 else 1 # 6个同类御魂算2次套装效果 408 | prop_type = mitama_enhance[m_type].get('加成类型') 409 | if prop_type: 410 | sum_result[prop_type] += ( 411 | multi_times * mitama_enhance[m_type].get('加成数值')) 412 | 413 | return sum_result 414 | 415 | def print_cal_rate(self, printed_rate, rate=5): 416 | '''print cal rate in real time''' 417 | cal_rate = int(self.calculated_count * 100.0 / self.total_comb) 418 | if cal_rate > printed_rate and cal_rate % rate == 0: 419 | print('Calculating rate %s%%' % cal_rate) 420 | sys.stdout.flush() 421 | return cal_rate 422 | 423 | return printed_rate 424 | 425 | def get_comb(self): 426 | # 按御魂限制条件筛选剪枝 427 | self.filter_loc_and_type() 428 | # 生成全组合 429 | mitama_comb = self.make_combination() 430 | # 按组合属性上下限过滤 431 | filter_result = self.filter_mitama(mitama_comb) 432 | # 按攻击*暴伤过滤 433 | if self.damage_limit > 0: 434 | filter_result = self.fit_damage_limit(filter_result) 435 | # 按生命*暴伤过滤 436 | if self.hp_crit_limit > 0: 437 | filter_result = self.fit_hp_crit_limit(filter_result) 438 | 439 | return filter_result 440 | 441 | def get_progress(self): 442 | """Return calculating progress 443 | 444 | return value: percent, current, total 445 | """ 446 | if self.total_comb <= 0: 447 | return 0, 0, 0 448 | return (float(self.calculated_count) / self.total_comb, 449 | self.calculated_count, 450 | self.total_comb) 451 | -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/js/app.31814493.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(e){for(var i,s,r=e[0],c=e[1],o=e[2],h=0,m=[];h")])]}}]),model:{value:t.effectiveAttributes,callback:function(e){t.effectiveAttributes=e},expression:"effectiveAttributes"}}),a("Autocomplete",{attrs:{placeholder:"各位置加成次数(以,分隔) 例如: 3,3,3,3,3,0","fetch-suggestions":t.queryEffectiveAttributesBonusCount},model:{value:t.effectiveAttributesBonusCount,callback:function(e){t.effectiveAttributesBonusCount=e},expression:"effectiveAttributesBonusCount"}})],1),a("FormItem",[a("p",{staticClass:"title"},[t._v("目标属性限制")]),a("label",[t._v("属性:")]),a("Select",{model:{value:t.targetAttribute,callback:function(e){t.targetAttribute=e},expression:"targetAttribute"}},t._l(t.attributes,function(t){return a("Option",{key:t,attrs:{label:t,value:t}})}),1),a("br"),a("div",{directives:[{name:"show",rawName:"v-show",value:""!==t.targetAttribute,expression:"targetAttribute !== ''"}]},[a("label",[t._v("下限:")]),a("InputNumber",{attrs:{min:0,max:999,size:"small","controls-position":"right"},model:{value:t.lowerValue,callback:function(e){t.lowerValue=e},expression:"lowerValue"}}),a("label",[t._v("上限:")]),a("InputNumber",{attrs:{min:0,max:999,size:"small","controls-position":"right"},model:{value:t.upperValue,callback:function(e){t.upperValue=e},expression:"upperValue"}})],1)],1),a("FormItem",{staticClass:"attribute-results"},t._l(this.targetAttributeList,function(e){return a("Tag",{key:e.split(" ")[0],attrs:{closable:""},on:{close:function(a){t.removeTargetAttribute(e)}}},[t._v(t._s(e))])}),1)],1),a("Form",{staticClass:"control"},[a("FormItem",{attrs:{label:"输入文件"}},[a("br"),t.filename?a("p",[t._v(t._s(t.filename))]):a("p",[t._v("尚未选择御魂数据文件")]),a("Button",{attrs:{type:"primary"},on:{click:t.selectFile}},[t._v("选择文件")])],1),a("FormItem",[100===t.calculateProgress?a("Button",{attrs:{type:"primary",disabled:!t.serverOnline},on:{click:t.run}},[t._v("开始计算")]):a("Button",{attrs:{type:"primary",loading:""}},[t._v("计算中... "+t._s(t.calculateProgress)+"%")])],1),a("CustomScheme",{attrs:{currentScheme:t.currentScheme},on:{selectScheme:t.handleSelectScheme}})],1)],1)])},p=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("header",{staticClass:"header"},[a("p",[t._v("《阴阳师》御魂计算器")])])}],f=(a("06f1"),a("450d"),a("6ac9")),b=a.n(f),v=(a("3db2"),a("58b8")),g=a.n(v),d=(a("a7cc"),a("df33")),k=a.n(d),A=(a("cbb5"),a("8bbc")),y=a.n(A),L=(a("9d4c"),a("e450")),x=a.n(L),S=(a("10cb"),a("f3ad")),C=a.n(S),P=(a("fe07"),a("6ac5")),_=a.n(P),O=(a("3c52"),a("0d7b")),B=a.n(O),E=(a("b5d8"),a("f494")),j=a.n(E),w=(a("c526"),a("1599")),I=a.n(w),V=(a("560b"),a("dcdc")),F=a.n(V),N=(a("d4df"),a("7fc1")),Y=a.n(N),T=(a("016f"),a("486c")),$=a.n(T),G=(a("6611"),a("e772")),z=a.n(G),R=(a("1f1a"),a("4e4b")),M=a.n(R),q=(a("ae26"),a("845f")),J=a.n(q),D=(a("1951"),a("eedf")),U=a.n(D),H=(a("eca7"),a("3787")),K=a.n(H),Q=(a("425f"),a("4105")),W=a.n(Q),X=(a("46a1"),a("e5f2")),Z=a.n(X),tt=a("768b"),et=(a("0fb7"),a("f529")),at=a.n(et),it=(a("7f7f"),a("28a5"),a("20d6"),a("b0b4")),ut=a("bc3a"),nt=a.n(ut),st=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"custom-scheme"},[a("FormItem",[a("Button",{attrs:{type:"primary"},on:{click:t.saveScheme}},[t._v("保存当前方案")])],1),a("FormItem",[a("Button",{attrs:{type:"primary"},on:{click:t.selectCustomScheme}},[t._v("选择自定义方案")])],1),a("FormItem",[a("Button",{attrs:{type:"primary"},on:{click:t.selectPresetScheme}},[t._v("选择预设方案")])],1)],1)},rt=[],ct=a("5d73"),ot=a.n(ct),lt=(a("7514"),a("5176")),ht=a.n(lt),mt=(a("9e1f"),a("6ed5")),pt=a.n(mt),ft=a("f499"),bt=a.n(ft),vt=[{name:"吃星暴伤针女大天狗",yuhunPackageList:["针女,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3135.6,150,0",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 0 - 18"]},{name:"超星暴伤针女大天狗",yuhunPackageList:["针女,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3135.6,150,0",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 18 - 999"]},{name:"超星荒骷髅茨木",yuhunPackageList:["破势,4","荒骷髅,2"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,17120",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 16 - 999"]},{name:"超星普通茨木",yuhunPackageList:["破势,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,20510",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 16 - 999"]},{name:"吃星荒骷髅茨木",yuhunPackageList:["破势,4","荒骷髅,2"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,13170",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 0 - 16"]},{name:"吃星普通茨木",yuhunPackageList:["破势,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,15780",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 0 - 16"]},{name:"暴伤玉藻前",yuhunPackageList:["破势,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3350,160,0",healthExpect:"",targetAttributeList:["暴击 88 - 95"]},{name:"正堆+反堆暴伤针女",yuhunPackageList:["针女,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害","暴击"],ignoreSerial:"",damageExpect:"",healthExpect:"",targetAttributeList:["暴击 90 - 95","暴击伤害 80 - 999"]},{name:"散件蜃气楼生生暴书翁",yuhunPackageList:["蜃气楼,2"],usePackage:!1,useAttack:!1,secondAttributeList:["生命加成"],fourthAttributeList:["生命加成"],sixthAttributeList:["暴击"],ignoreSerial:"",damageExpect:"",healthExpect:"11393,150,0",targetAttributeList:["暴击 92 - 95"]}],gt=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("Select",{model:{value:t.selectedPresetSchemeName,callback:function(e){t.selectedPresetSchemeName=e},expression:"selectedPresetSchemeName"}},t._l(t.schemeList,function(t){return a("Option",{key:t.name,attrs:{label:t.name,value:t.name}})}),1)},dt=[],kt=function(t){function e(){var t;return Object(s["a"])(this,e),t=Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments)),t.selectedPresetSchemeName="",t}return Object(o["a"])(e,t),Object(it["a"])(e,[{key:"onSelectSchemeChange",value:function(){this.$emit("SelectSchemeChange",this.selectedPresetSchemeName)}}]),e}(h["c"]);l["a"]([Object(h["b"])({default:[]})],kt.prototype,"schemeList",void 0),l["a"]([Object(h["d"])("selectedPresetSchemeName")],kt.prototype,"onSelectSchemeChange",null),kt=l["a"]([Object(h["a"])({components:{Select:M.a,Option:z.a}})],kt);var At=kt,yt=At,Lt=a("2877"),xt=Object(Lt["a"])(yt,gt,dt,!1,null,null,null);xt.options.__file="SchemeSelect.vue";var St=xt.exports,Ct="customSchemeList",Pt=window.localStorage.getItem(Ct)||"[]",_t=function(t){function e(){var t;return Object(s["a"])(this,e),t=Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments)),t.schemeList=JSON.parse(Pt),t.selectedCustomSchemeName="",t.selectedPresetSchemeName="",t}return Object(o["a"])(e,t),Object(it["a"])(e,[{key:"saveStorage",value:function(){window.localStorage.setItem(Ct,bt()(this.schemeList))}},{key:"saveScheme",value:function(){var t=this;pt.a.prompt("","请输入自定义方案名称").then(function(e){var a=e.value,i=ht()({name:a},t.currentScheme);t.schemeList.push(i),t.saveStorage(),at.a.success("保存自定义方案「".concat(i.name,"」成功"))})}},{key:"selectCustomScheme",value:function(){var t=this,e=this.$createElement;pt()({title:"自定义方案列表",message:e("SchemeSelect",{key:"CustomSchemeSelct",props:{schemeList:this.schemeList},on:{SelectSchemeChange:function(e){t.selectedCustomSchemeName=e}}}),showCancelButton:!0,confirmButtonText:"选择",cancelButtonText:"删除",cancelButtonClass:"delete",distinguishCancelAndClose:!0}).then(function(){var e=t.schemeList.find(function(e){return e.name===t.selectedCustomSchemeName});e&&(t.$emit("selectScheme",e),at.a.success("应用自定义方案「".concat(e.name,"」成功")))}).catch(function(e){if("close"!==e){var a=t.schemeList.findIndex(function(e){return e.name===t.selectedCustomSchemeName});-1!==a&&(at.a.success("删除自定义方案「".concat(t.schemeList[a].name,"」成功")),t.schemeList.splice(a,1),t.saveStorage(),t.selectedCustomSchemeName="")}})}},{key:"selectPresetScheme",value:function(){var t=this,e=this.$createElement;pt()({title:"预设方案列表",message:e("SchemeSelect",{key:"PresetSchemeSelct",props:{schemeList:vt},on:{SelectSchemeChange:function(e){t.selectedCustomSchemeName=e}}}),confirmButtonText:"选择",distinguishCancelAndClose:!0}).then(function(){var e=!0,a=!1,i=void 0;try{for(var u,n=ot()(vt);!(e=(u=n.next()).done);e=!0){var s=u.value;if(s.name===t.selectedCustomSchemeName){t.$emit("selectScheme",s),at.a.success("应用预设方案「".concat(t.selectedCustomSchemeName,"」成功"));break}}}catch(r){a=!0,i=r}finally{try{e||null==n.return||n.return()}finally{if(a)throw i}}})}}]),e}(h["c"]);l["a"]([Object(h["b"])({default:{}})],_t.prototype,"currentScheme",void 0),_t=l["a"]([Object(h["a"])({components:{FormItem:K.a,Button:U.a,Select:M.a,Option:z.a,Dialog:k.a,SchemeSelect:St}})],_t);var Ot=_t,Bt=Ot,Et=(a("1642"),Object(Lt["a"])(Bt,st,rt,!1,null,null,null));Et.options.__file="CustomScheme.vue";var jt=Et.exports,wt="//localhost:2019",It={validateStatus:function(t){return t>=200&&t<=500}},Vt=function(t){function e(){var t;return Object(s["a"])(this,e),t=Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments)),t.serverOnline=!0,t.yuhunPackage="",t.yuhunPackageList=[],t.presetYuhunPackageList=[],t.usePackage=!0,t.useAttack=!1,t.secondAttributeList=[],t.fourthAttributeList=[],t.sixthAttributeList=[],t.presetAttribute="",t.ignoreSerial="",t.damageExpect="",t.attackBuff="",t.healthExpect="",t.effectiveAttributes="",t.effectiveAttributesBonusCount="",t.targetAttribute="",t.lowerValue=0,t.upperValue=999,t.targetAttributeList=[],t.filename="",t.calculateProgress=100,t}return Object(o["a"])(e,t),Object(it["a"])(e,[{key:"addYuhunPackageLimit",value:function(){this.disableAddYuhunPackageButton||this.yuhunPackageList.push(this.yuhunPackage)}},{key:"removeYuhunPackageLimit",value:function(t){var e=this.yuhunPackageList.findIndex(function(e){return e===t});this.yuhunPackageList.splice(e,1)}},{key:"addTargetAttribute",value:function(){var t=this,e="".concat(this.targetAttribute," ").concat(this.lowerValue||0," - ").concat(this.upperValue||0),a=this.targetAttributeList.findIndex(function(e){return e.split(" ")[0]===t.targetAttribute});-1===a?this.targetAttributeList.push(e):this.targetAttributeList.splice(a,1,e)}},{key:"removeTargetAttribute",value:function(t){var e=this.targetAttributeList.findIndex(function(e){return e===t});-1!==e&&this.targetAttributeList.splice(e,1)}},{key:"ontargetAttributeChange",value:function(t){t&&(this.lowerValue=0,this.upperValue=0,this.addTargetAttribute())}},{key:"onLowerValueChange",value:function(t,e){0===e&&(this.upperValue=t+10),this.upperValuet&&(this.lowerValue=t),this.addTargetAttribute()}},{key:"selectFile",value:function(){var t=this,e=document.createElement("input");e.style.display="none",e.setAttribute("type","file"),e.setAttribute("accept",".json,.xls"),e.onclick=function(){e.value="",document.body.onfocus=function(){setTimeout(function(){e.value.length,document.body.onfocus=null},500)}},e.onchange=function(e){var a=e.target.files[0];a&&(t.filename=a.name)},e.click()}},{key:"verifyInputValue",value:function(){return this.damageExpect&&!/^[0-9]+,[0-9]+,[0-9]+(?:,[0-9]+)?$/.test(this.damageExpect)?(at.a.warning('"伤害期望" 格式错误'),!1):this.healthExpect&&!/^[0-9]+,[0-9]+,[0-9]+(?:,[0-9]+)?$/.test(this.healthExpect)?(at.a.warning('"治疗期望" 格式错误'),!1):this.effectiveAttributes&&!/^(攻击加成|生命加成|防御加成|速度|效果命中|效果抵抗|暴击|暴击伤害)(,(攻击加成|生命加成|防御加成|速度|效果命中|效果抵抗|暴击|暴击伤害))*$/.test(this.effectiveAttributes)?(at.a.warning('"有效副属性 => 属性列表" 格式错误'),!1):!(this.effectiveAttributesBonusCount&&!/^[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+$/.test(this.effectiveAttributesBonusCount))||(at.a.warning('"有效副属性 => 各位置加成次数" 格式错误'),!1)}},{key:"run",value:function(){this.filename?this.verifyInputValue()&&(nt.a.post(wt+"/calculate",{src_filename:this.filename,mitama_suit:this.yuhunPackageList.join("."),prop_limit:this.targetAttributeList.map(function(t){var e=t.split(/ |-/),a=Object(tt["a"])(e,2),i=a[0],u=a[1];return i+","+u}).join("."),upper_prop_limit:this.targetAttributeList.map(function(t){var e=t.split(/ |-/),a=Object(tt["a"])(e,5),i=a[0],u=a[4];return i+","+u}).join("."),sec_prop_value:this.getPropValue(this.secondAttributeList),fth_prop_value:this.getPropValue(this.fourthAttributeList),sth_prop_value:this.getPropValue(this.sixthAttributeList),ignore_serial:this.ignoreSerial,all_suit:this.usePackage,damage_limit:this.damageExpect||"0,0,0",health_limit:this.healthExpect||"0,0,0",attack_only:this.useAttack,effective_secondary_prop:this.effectiveAttributes||"",effective_secondary_prop_num:this.effectiveAttributesBonusCount||"",attack_buff:this.attackBuff||0},It).then(function(t){switch(t.status){case 500:at()({type:"error",message:"计算失败, 服务端错误: ".concat(t.data.reason),duration:5e3});break;case 200:at.a.success("计算完毕, 组合数量:".concat(t.data.result_num));break;default:at.a.error("计算失败, 服务端返回状态码:".concat(t.status));break}}).catch(function(t){at.a.error("计算失败")}),setTimeout(this.getCalculateStatus.bind(this),500)):at.a.warning("请先选择御魂数据文件")}},{key:"mounted",value:function(){var t=this;nt.a.get(wt+"/status").catch(function(){Z.a.error({title:"提示",message:"未检测到服务端, 请先启动服务端后再试",duration:0}),t.serverOnline=!1})}},{key:"handleSelectScheme",value:function(t){this.yuhunPackageList=t.yuhunPackageList,this.usePackage=t.usePackage,this.useAttack=t.useAttack,this.secondAttributeList=t.secondAttributeList,this.fourthAttributeList=t.fourthAttributeList,this.sixthAttributeList=t.sixthAttributeList,this.ignoreSerial=t.ignoreSerial,this.damageExpect=t.damageExpect,this.healthExpect=t.healthExpect,this.targetAttributeList=t.targetAttributeList,this.targetAttribute="",this.attackBuff=""}},{key:"getCalculateStatus",value:function(){var t=this;nt.a.get(wt+"/status").then(function(e){var a=e.data.progress;void 0!==a&&(t.calculateProgress=Math.floor(100*a),1!==a&&setTimeout(t.getCalculateStatus.bind(t),100))}).catch(function(){console.warn("获取计算进度失败")})}},{key:"getPropValue",value:function(t){return t.map(function(t){switch(t){case"速度":return"速度,57";case"暴击伤害":return"暴击伤害,89";default:return t+",55"}}).join(".")}},{key:"queryEffectiveAttributes",value:function(t,e){var a=this.effectiveAttributesChoices;return t&&(a=a.filter(function(e){return 0===e.name.toLowerCase().indexOf(t.toLowerCase())||0===e.value.toLowerCase().indexOf(t.toLowerCase())})),e(a)}},{key:"queryEffectiveAttributesBonusCount",value:function(t,e){var a=this.effectiveAttributesBonusCountChoices;return t&&(a=a.filter(function(e){return-1!==e.value.indexOf(t.toLowerCase())})),e(a)}},{key:"onSelectPresetAttribute",value:function(t){var e={"生":["生命加成"],"攻":["攻击加成"],"暴":["暴击","暴击伤害"],"命":["效果命中"],"抗":["效果抵抗"],"速":["速度"]};if(3===t.length){var a=t.split("").map(function(t){return e[t]});this.secondAttributeList=a[0],this.fourthAttributeList=a[1],this.sixthAttributeList=a[2]}}},{key:"onSelectPresetYuhun",value:function(t){this.yuhunPackageList=this.presetYuhunPackageList.slice()}},{key:"yuhunOptions",get:function(){return[{name:"散件",list:["攻击加成,2","暴击,2","生命加成,2","效果命中,2","效果抵抗,2"]},{name:"首领御魂",list:["荒骷髅,2","土蜘蛛,2","地震鲶,2","蜃气楼,2","胧车,2"]},{name:"暴击",list:["针女,4","破势,4","网切,4","三味,4","伤魂鸟,4","镇魂兽,4"]},{name:"攻击加成",list:["蝠翼,4","鸣屋,4","心眼,4","狰,4","轮入道,4","狂骨,4","阴摩罗,4"]},{name:"生命加成",list:["树妖,4","地藏像,4","薙魂,4","镜姬,4","钟灵,4","涅槃之火,4","被服,4"]},{name:"防御加成",list:["珍珠,4","魅妖,4","雪幽魂,4","招财猫,4","反枕,4","日女巳时,4","木魅,4"]},{name:"效果命中",list:["蚌精,4","火灵,4"]},{name:"效果抵抗",list:["骰子鬼,4","返魂香,4","幽谷响,4","魍魉之匣,4"]}]}},{key:"attributes",get:function(){return["暴击","暴击伤害","效果命中","效果抵抗","速度","攻击加成","生命加成","防御加成"]}},{key:"attackBuffs",get:function(){return[{name:"无buff",value:0},{name:"1级兔子舞",value:10},{name:"满级兔子舞",value:20},{name:"满级兄弟之绊",value:25},{name:"黑晴明+1级兔子舞",value:30},{name:"黑晴明+满级兔子舞",value:40},{name:"黑晴明+满级兄弟之绊",value:45}]}},{key:"presetAttributes",get:function(){return[{name:"攻攻暴",value:"攻攻暴"},{name:"速攻暴",value:"速攻暴"},{name:"生生暴",value:"生生暴"},{name:"速生暴",value:"速生暴"},{name:"速命生",value:"速命生"},{name:"速抗生",value:"速抗生"}]}},{key:"presetYuhunPackages",get:function(){return[{name:"破荒",value:["破势,4","荒骷髅,2"]},{name:"心荒",value:["心眼,4","荒骷髅,2"]},{name:"狂荒",value:["狂骨,4","荒骷髅,2"]},{name:"针荒",value:["针女,4","荒骷髅,2"]},{name:"散件生命",value:["生命加成,2","生命加成,2","生命加成,2"]}]}},{key:"effectiveAttributesChoices",get:function(){return[{name:"输出",value:"暴击,暴击伤害,攻击加成,速度"},{name:"奶盾",value:"暴击,暴击伤害,生命加成,速度"},{name:"命中",value:"效果命中,速度"},{name:"抵抗",value:"效果抵抗,速度"},{name:"双堆",value:"速度,效果命中,效果抵抗"}]}},{key:"effectiveAttributesBonusCountChoices",get:function(){return[{name:"1",value:"1,1,1,1,1,0"},{name:"2",value:"3,3,3,3,3,1"},{name:"3",value:"3,2,3,2,3,0"},{name:"4",value:"5,3,5,3,5,1"}]}},{key:"disableAddYuhunPackageButton",get:function(){if(!this.yuhunPackage)return!0;var t=0;if(this.yuhunPackage){var e=this.yuhunPackage.split(","),a=Object(tt["a"])(e,2),i=a[1];t=+i}return t+this.selectedYuhunPackageCount>6}},{key:"selectedYuhunPackageCount",get:function(){return this.yuhunPackageList.map(function(t){var e=t.split(","),a=Object(tt["a"])(e,2),i=a[1];return+i}).reduce(function(t,e){return t+e},0)}},{key:"currentScheme",get:function(){return{yuhunPackageList:this.yuhunPackageList,usePackage:this.usePackage,useAttack:this.useAttack,secondAttributeList:this.secondAttributeList,fourthAttributeList:this.fourthAttributeList,sixthAttributeList:this.sixthAttributeList,ignoreSerial:this.ignoreSerial,damageExpect:this.damageExpect,healthExpect:this.healthExpect,targetAttributeList:this.targetAttributeList}}}]),e}(h["c"]);l["a"]([Object(h["d"])("yuhunPackage")],Vt.prototype,"addYuhunPackageLimit",null),l["a"]([Object(h["d"])("targetAttribute")],Vt.prototype,"ontargetAttributeChange",null),l["a"]([Object(h["d"])("lowerValue")],Vt.prototype,"onLowerValueChange",null),l["a"]([Object(h["d"])("upperValue")],Vt.prototype,"onUpperValueChange",null),Vt=l["a"]([Object(h["a"])({components:{Form:W.a,FormItem:K.a,Button:U.a,ButtonGroup:J.a,Select:M.a,Option:z.a,OptionGroup:$.a,CheckboxGroup:Y.a,Checkbox:F.a,CheckboxButton:I.a,Radio:j.a,RadioButton:B.a,RadioGroup:_.a,Input:C.a,InputNumber:x.a,Tag:y.a,Dialog:k.a,CustomScheme:jt,Autocomplete:g.a,Popover:b.a}})],Vt);var Ft=Vt,Nt=Ft,Yt=(a("b8cc"),Object(Lt["a"])(Nt,m,p,!1,null,null,null));Yt.options.__file="Calculator.vue";var Tt=Yt.exports,$t=function(t){function e(){return Object(s["a"])(this,e),Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments))}return Object(o["a"])(e,t),e}(h["c"]);$t=l["a"]([Object(h["a"])({components:{Calculator:Tt}})],$t);var Gt=$t,zt=Gt,Rt=(a("7c55"),Object(Lt["a"])(zt,u,n,!1,null,null,null));Rt.options.__file="App.vue";var Mt=Rt.exports;i["default"].config.productionTip=!1,new i["default"]({render:function(t){return t(Mt)}}).$mount("#app")}}); 2 | //# sourceMappingURL=app.31814493.js.map -------------------------------------------------------------------------------- /calculator_of_Onmyoji/static/js/app.5c0c5dc2.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(e){for(var i,s,r=e[0],c=e[1],o=e[2],h=0,m=[];h")])]}}]),model:{value:t.effectiveAttributes,callback:function(e){t.effectiveAttributes=e},expression:"effectiveAttributes"}}),a("Autocomplete",{attrs:{placeholder:"各位置加成次数(以,分隔) 例如: 3,3,3,3,3,0","fetch-suggestions":t.queryEffectiveAttributesBonusCount},model:{value:t.effectiveAttributesBonusCount,callback:function(e){t.effectiveAttributesBonusCount=e},expression:"effectiveAttributesBonusCount"}})],1),a("FormItem",[a("p",{staticClass:"title"},[t._v("目标属性限制")]),a("label",[t._v("属性:")]),a("Select",{model:{value:t.targetAttribute,callback:function(e){t.targetAttribute=e},expression:"targetAttribute"}},t._l(t.attributes,function(t){return a("Option",{key:t,attrs:{label:t,value:t}})}),1),a("br"),a("div",{directives:[{name:"show",rawName:"v-show",value:""!==t.targetAttribute,expression:"targetAttribute !== ''"}]},[a("label",[t._v("下限:")]),a("InputNumber",{attrs:{min:0,max:999,size:"small","controls-position":"right"},model:{value:t.lowerValue,callback:function(e){t.lowerValue=e},expression:"lowerValue"}}),a("label",[t._v("上限:")]),a("InputNumber",{attrs:{min:0,max:999,size:"small","controls-position":"right"},model:{value:t.upperValue,callback:function(e){t.upperValue=e},expression:"upperValue"}})],1)],1),a("FormItem",{staticClass:"attribute-results"},t._l(this.targetAttributeList,function(e){return a("Tag",{key:e.split(" ")[0],attrs:{closable:""},on:{close:function(a){t.removeTargetAttribute(e)}}},[t._v(t._s(e))])}),1)],1),a("Form",{staticClass:"control"},[a("FormItem",{attrs:{label:"输入文件"}},[a("br"),t.filename?a("p",[t._v(t._s(t.filename))]):a("p",[t._v("尚未选择御魂数据文件")]),a("Button",{attrs:{type:"primary"},on:{click:t.selectFile}},[t._v("选择文件")])],1),a("FormItem",[100===t.calculateProgress?a("Button",{attrs:{type:"primary",disabled:!t.serverOnline},on:{click:t.run}},[t._v("开始计算")]):a("Button",{attrs:{type:"primary",loading:""}},[t._v("计算中... "+t._s(t.calculateProgress)+"%")])],1),a("CustomScheme",{attrs:{currentScheme:t.currentScheme},on:{selectScheme:t.handleSelectScheme}})],1)],1)])},p=[function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("header",{staticClass:"header"},[a("p",[t._v("《阴阳师》御魂计算器")])])}],f=(a("06f1"),a("450d"),a("6ac9")),b=a.n(f),v=(a("3db2"),a("58b8")),g=a.n(v),d=(a("a7cc"),a("df33")),k=a.n(d),A=(a("cbb5"),a("8bbc")),y=a.n(A),L=(a("9d4c"),a("e450")),x=a.n(L),S=(a("10cb"),a("f3ad")),C=a.n(S),P=(a("fe07"),a("6ac5")),_=a.n(P),O=(a("3c52"),a("0d7b")),B=a.n(O),E=(a("b5d8"),a("f494")),j=a.n(E),w=(a("c526"),a("1599")),I=a.n(w),V=(a("560b"),a("dcdc")),F=a.n(V),N=(a("d4df"),a("7fc1")),Y=a.n(N),T=(a("016f"),a("486c")),$=a.n(T),G=(a("6611"),a("e772")),z=a.n(G),R=(a("1f1a"),a("4e4b")),M=a.n(R),q=(a("ae26"),a("845f")),J=a.n(q),D=(a("1951"),a("eedf")),U=a.n(D),H=(a("eca7"),a("3787")),K=a.n(H),Q=(a("425f"),a("4105")),W=a.n(Q),X=(a("46a1"),a("e5f2")),Z=a.n(X),tt=a("768b"),et=(a("0fb7"),a("f529")),at=a.n(et),it=(a("7f7f"),a("28a5"),a("20d6"),a("b0b4")),ut=a("bc3a"),nt=a.n(ut),st=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"custom-scheme"},[a("FormItem",[a("Button",{attrs:{type:"primary"},on:{click:t.saveScheme}},[t._v("保存当前方案")])],1),a("FormItem",[a("Button",{attrs:{type:"primary"},on:{click:t.selectCustomScheme}},[t._v("选择自定义方案")])],1),a("FormItem",[a("Button",{attrs:{type:"primary"},on:{click:t.selectPresetScheme}},[t._v("选择预设方案")])],1)],1)},rt=[],ct=a("5d73"),ot=a.n(ct),lt=(a("7514"),a("5176")),ht=a.n(lt),mt=(a("9e1f"),a("6ed5")),pt=a.n(mt),ft=a("f499"),bt=a.n(ft),vt=[{name:"吃星暴伤针女大天狗",yuhunPackageList:["针女,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3135.6,150,0",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 0 - 18"]},{name:"超星暴伤针女大天狗",yuhunPackageList:["针女,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3135.6,150,0",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 18 - 999"]},{name:"超星荒骷髅茨木",yuhunPackageList:["破势,4","荒骷髅,2"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,17120",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 16 - 999"]},{name:"超星普通茨木",yuhunPackageList:["破势,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,20510",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 16 - 999"]},{name:"吃星荒骷髅茨木",yuhunPackageList:["破势,4","荒骷髅,2"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,13170",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 0 - 16"]},{name:"吃星普通茨木",yuhunPackageList:["破势,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3216,150,15780",healthExpect:"",targetAttributeList:["暴击 90 - 95","速度 0 - 16"]},{name:"暴伤玉藻前",yuhunPackageList:["破势,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害"],ignoreSerial:"",damageExpect:"3350,160,0",healthExpect:"",targetAttributeList:["暴击 88 - 95"]},{name:"正堆+反堆暴伤针女",yuhunPackageList:["针女,4"],usePackage:!0,useAttack:!0,secondAttributeList:["攻击加成"],fourthAttributeList:["攻击加成"],sixthAttributeList:["暴击伤害","暴击"],ignoreSerial:"",damageExpect:"",healthExpect:"",targetAttributeList:["暴击 90 - 95","暴击伤害 80 - 999"]},{name:"散件蜃气楼生生暴书翁",yuhunPackageList:["蜃气楼,2"],usePackage:!1,useAttack:!1,secondAttributeList:["生命加成"],fourthAttributeList:["生命加成"],sixthAttributeList:["暴击"],ignoreSerial:"",damageExpect:"",healthExpect:"11393,150,0",targetAttributeList:["暴击 92 - 95"]}],gt=function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("Select",{model:{value:t.selectedPresetSchemeName,callback:function(e){t.selectedPresetSchemeName=e},expression:"selectedPresetSchemeName"}},t._l(t.schemeList,function(t){return a("Option",{key:t.name,attrs:{label:t.name,value:t.name}})}),1)},dt=[],kt=function(t){function e(){var t;return Object(s["a"])(this,e),t=Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments)),t.selectedPresetSchemeName="",t}return Object(o["a"])(e,t),Object(it["a"])(e,[{key:"onSelectSchemeChange",value:function(){this.$emit("SelectSchemeChange",this.selectedPresetSchemeName)}}]),e}(h["c"]);l["a"]([Object(h["b"])({default:[]})],kt.prototype,"schemeList",void 0),l["a"]([Object(h["d"])("selectedPresetSchemeName")],kt.prototype,"onSelectSchemeChange",null),kt=l["a"]([Object(h["a"])({components:{Select:M.a,Option:z.a}})],kt);var At=kt,yt=At,Lt=a("2877"),xt=Object(Lt["a"])(yt,gt,dt,!1,null,null,null);xt.options.__file="SchemeSelect.vue";var St=xt.exports,Ct="customSchemeList",Pt=window.localStorage.getItem(Ct)||"[]",_t=function(t){function e(){var t;return Object(s["a"])(this,e),t=Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments)),t.schemeList=JSON.parse(Pt),t.selectedCustomSchemeName="",t.selectedPresetSchemeName="",t}return Object(o["a"])(e,t),Object(it["a"])(e,[{key:"saveStorage",value:function(){window.localStorage.setItem(Ct,bt()(this.schemeList))}},{key:"saveScheme",value:function(){var t=this;pt.a.prompt("","请输入自定义方案名称").then(function(e){var a=e.value,i=ht()({name:a},t.currentScheme);t.schemeList.push(i),t.saveStorage(),at.a.success("保存自定义方案「".concat(i.name,"」成功"))})}},{key:"selectCustomScheme",value:function(){var t=this,e=this.$createElement;pt()({title:"自定义方案列表",message:e("SchemeSelect",{key:"CustomSchemeSelct",props:{schemeList:this.schemeList},on:{SelectSchemeChange:function(e){t.selectedCustomSchemeName=e}}}),showCancelButton:!0,confirmButtonText:"选择",cancelButtonText:"删除",cancelButtonClass:"delete",distinguishCancelAndClose:!0}).then(function(){var e=t.schemeList.find(function(e){return e.name===t.selectedCustomSchemeName});e&&(t.$emit("selectScheme",e),at.a.success("应用自定义方案「".concat(e.name,"」成功")))}).catch(function(e){if("close"!==e){var a=t.schemeList.findIndex(function(e){return e.name===t.selectedCustomSchemeName});-1!==a&&(at.a.success("删除自定义方案「".concat(t.schemeList[a].name,"」成功")),t.schemeList.splice(a,1),t.saveStorage(),t.selectedCustomSchemeName="")}})}},{key:"selectPresetScheme",value:function(){var t=this,e=this.$createElement;pt()({title:"预设方案列表",message:e("SchemeSelect",{key:"PresetSchemeSelct",props:{schemeList:vt},on:{SelectSchemeChange:function(e){t.selectedCustomSchemeName=e}}}),confirmButtonText:"选择",distinguishCancelAndClose:!0}).then(function(){var e=!0,a=!1,i=void 0;try{for(var u,n=ot()(vt);!(e=(u=n.next()).done);e=!0){var s=u.value;if(s.name===t.selectedCustomSchemeName){t.$emit("selectScheme",s),at.a.success("应用预设方案「".concat(t.selectedCustomSchemeName,"」成功"));break}}}catch(r){a=!0,i=r}finally{try{e||null==n.return||n.return()}finally{if(a)throw i}}})}}]),e}(h["c"]);l["a"]([Object(h["b"])({default:{}})],_t.prototype,"currentScheme",void 0),_t=l["a"]([Object(h["a"])({components:{FormItem:K.a,Button:U.a,Select:M.a,Option:z.a,Dialog:k.a,SchemeSelect:St}})],_t);var Ot=_t,Bt=Ot,Et=(a("1642"),Object(Lt["a"])(Bt,st,rt,!1,null,null,null));Et.options.__file="CustomScheme.vue";var jt=Et.exports,wt="//localhost:2019",It={validateStatus:function(t){return t>=200&&t<=500}},Vt=function(t){function e(){var t;return Object(s["a"])(this,e),t=Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments)),t.serverOnline=!0,t.yuhunPackage="",t.yuhunPackageList=[],t.presetYuhunPackageList=[],t.usePackage=!0,t.useAttack=!1,t.secondAttributeList=[],t.fourthAttributeList=[],t.sixthAttributeList=[],t.presetAttribute="",t.ignoreSerial="",t.damageExpect="",t.attackBuff="",t.healthExpect="",t.effectiveAttributes="",t.effectiveAttributesBonusCount="",t.targetAttribute="",t.lowerValue=0,t.upperValue=999,t.targetAttributeList=[],t.filename="",t.calculateProgress=100,t}return Object(o["a"])(e,t),Object(it["a"])(e,[{key:"addYuhunPackageLimit",value:function(){this.disableAddYuhunPackageButton||this.yuhunPackageList.push(this.yuhunPackage)}},{key:"removeYuhunPackageLimit",value:function(t){var e=this.yuhunPackageList.findIndex(function(e){return e===t});this.yuhunPackageList.splice(e,1)}},{key:"addTargetAttribute",value:function(){var t=this,e="".concat(this.targetAttribute," ").concat(this.lowerValue||0," - ").concat(this.upperValue||0),a=this.targetAttributeList.findIndex(function(e){return e.split(" ")[0]===t.targetAttribute});-1===a?this.targetAttributeList.push(e):this.targetAttributeList.splice(a,1,e)}},{key:"removeTargetAttribute",value:function(t){var e=this.targetAttributeList.findIndex(function(e){return e===t});-1!==e&&this.targetAttributeList.splice(e,1)}},{key:"ontargetAttributeChange",value:function(t){t&&(this.lowerValue=0,this.upperValue=0,this.addTargetAttribute())}},{key:"onLowerValueChange",value:function(t,e){0===e&&(this.upperValue=t+10),this.upperValuet&&(this.lowerValue=t),this.addTargetAttribute()}},{key:"selectFile",value:function(){var t=this,e=document.createElement("input");e.style.display="none",e.setAttribute("type","file"),e.setAttribute("accept",".json,.xls"),e.onclick=function(){e.value="",document.body.onfocus=function(){setTimeout(function(){e.value.length,document.body.onfocus=null},500)}},e.onchange=function(e){var a=e.target.files[0];a&&(t.filename=a.name)},e.click()}},{key:"verifyInputValue",value:function(){return this.damageExpect&&!/^[0-9]+,[0-9]+,[0-9]+(?:,[0-9]+)?$/.test(this.damageExpect)?(at.a.warning('"伤害期望" 格式错误'),!1):this.healthExpect&&!/^[0-9]+,[0-9]+,[0-9]+(?:,[0-9]+)?$/.test(this.healthExpect)?(at.a.warning('"治疗期望" 格式错误'),!1):this.effectiveAttributes&&!/^(攻击加成|生命加成|防御加成|速度|效果命中|效果抵抗|暴击|暴击伤害)(,(攻击加成|生命加成|防御加成|速度|效果命中|效果抵抗|暴击|暴击伤害))*$/.test(this.effectiveAttributes)?(at.a.warning('"有效副属性 => 属性列表" 格式错误'),!1):!(this.effectiveAttributesBonusCount&&!/^[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+,[0-9]+$/.test(this.effectiveAttributesBonusCount))||(at.a.warning('"有效副属性 => 各位置加成次数" 格式错误'),!1)}},{key:"run",value:function(){this.filename?this.verifyInputValue()&&(nt.a.post(wt+"/calculate",{src_filename:this.filename,mitama_suit:this.yuhunPackageList.join("."),prop_limit:this.targetAttributeList.map(function(t){var e=t.split(/ |-/),a=Object(tt["a"])(e,2),i=a[0],u=a[1];return i+","+u}).join("."),upper_prop_limit:this.targetAttributeList.map(function(t){var e=t.split(/ |-/),a=Object(tt["a"])(e,5),i=a[0],u=a[4];return i+","+u}).join("."),sec_prop_value:this.getPropValue(this.secondAttributeList),fth_prop_value:this.getPropValue(this.fourthAttributeList),sth_prop_value:this.getPropValue(this.sixthAttributeList),ignore_serial:this.ignoreSerial,all_suit:this.usePackage,damage_limit:this.damageExpect||"0,0,0",health_limit:this.healthExpect||"0,0,0",attack_only:this.useAttack,effective_secondary_prop:this.effectiveAttributes||"",effective_secondary_prop_num:this.effectiveAttributesBonusCount||"",attack_buff:this.attackBuff||0},It).then(function(t){switch(t.status){case 500:at()({type:"error",message:"计算失败, 服务端错误: ".concat(t.data.reason),duration:5e3});break;case 200:at.a.success("计算完毕, 组合数量:".concat(t.data.result_num));break;default:at.a.error("计算失败, 服务端返回状态码:".concat(t.status));break}}).catch(function(t){at.a.error("计算失败")}),setTimeout(this.getCalculateStatus.bind(this),500)):at.a.warning("请先选择御魂数据文件")}},{key:"mounted",value:function(){var t=this;nt.a.get(wt+"/status").catch(function(){Z.a.error({title:"提示",message:"未检测到服务端, 请先启动服务端后再试",duration:0}),t.serverOnline=!1})}},{key:"handleSelectScheme",value:function(t){this.yuhunPackageList=t.yuhunPackageList,this.usePackage=t.usePackage,this.useAttack=t.useAttack,this.secondAttributeList=t.secondAttributeList,this.fourthAttributeList=t.fourthAttributeList,this.sixthAttributeList=t.sixthAttributeList,this.ignoreSerial=t.ignoreSerial,this.damageExpect=t.damageExpect,this.healthExpect=t.healthExpect,this.targetAttributeList=t.targetAttributeList,this.targetAttribute="",this.attackBuff=""}},{key:"getCalculateStatus",value:function(){var t=this;nt.a.get(wt+"/status").then(function(e){var a=e.data.progress;void 0!==a&&(t.calculateProgress=Math.floor(100*a),1!==a&&setTimeout(t.getCalculateStatus.bind(t),100))}).catch(function(){console.warn("获取计算进度失败")})}},{key:"getPropValue",value:function(t){return t.map(function(t){switch(t){case"速度":return"速度,57";case"暴击伤害":return"暴击伤害,89";default:return t+",55"}}).join(".")}},{key:"queryEffectiveAttributes",value:function(t,e){var a=this.effectiveAttributesChoices;return t&&(a=a.filter(function(e){return 0===e.name.toLowerCase().indexOf(t.toLowerCase())||0===e.value.toLowerCase().indexOf(t.toLowerCase())})),e(a)}},{key:"queryEffectiveAttributesBonusCount",value:function(t,e){var a=this.effectiveAttributesBonusCountChoices;return t&&(a=a.filter(function(e){return-1!==e.value.indexOf(t.toLowerCase())})),e(a)}},{key:"onSelectPresetAttribute",value:function(t){var e={"生":["生命加成"],"攻":["攻击加成"],"暴":["暴击","暴击伤害"],"命":["效果命中"],"抗":["效果抵抗"],"速":["速度"]};if(3===t.length){var a=t.split("").map(function(t){return e[t]});this.secondAttributeList=a[0],this.fourthAttributeList=a[1],this.sixthAttributeList=a[2]}}},{key:"onSelectPresetYuhun",value:function(t){this.yuhunPackageList=this.presetYuhunPackageList.slice()}},{key:"yuhunOptions",get:function(){return[{name:"散件",list:["攻击加成,2","暴击,2","生命加成,2","效果命中,2","效果抵抗,2"]},{name:"首领御魂",list:["荒骷髅,2","土蜘蛛,2","地震鲶,2","蜃气楼,2","胧车,2","鬼灵歌伎,2"]},{name:"暴击",list:["针女,4","破势,4","网切,4","三味,4","伤魂鸟,4","镇魂兽,4","青女房,4"]},{name:"攻击加成",list:["蝠翼,4","鸣屋,4","心眼,4","狰,4","轮入道,4","狂骨,4","阴摩罗,4","兵主部,4"]},{name:"生命加成",list:["树妖,4","地藏像,4","薙魂,4","镜姬,4","钟灵,4","涅槃之火,4","被服,4","涂佛,4"]},{name:"防御加成",list:["珍珠,4","魅妖,4","雪幽魂,4","招财猫,4","反枕,4","日女巳时,4","木魅,4"]},{name:"效果命中",list:["蚌精,4","火灵,4","飞缘魔,4"]},{name:"效果抵抗",list:["骰子鬼,4","返魂香,4","幽谷响,4","魍魉之匣,4"]}]}},{key:"attributes",get:function(){return["暴击","暴击伤害","效果命中","效果抵抗","速度","攻击加成","生命加成","防御加成"]}},{key:"attackBuffs",get:function(){return[{name:"无buff",value:0},{name:"1级兔子舞",value:10},{name:"满级兔子舞",value:20},{name:"满级兄弟之绊",value:25},{name:"黑晴明+1级兔子舞",value:30},{name:"黑晴明+满级兔子舞",value:40},{name:"黑晴明+满级兄弟之绊",value:45}]}},{key:"presetAttributes",get:function(){return[{name:"攻攻暴",value:"攻攻暴"},{name:"速攻暴",value:"速攻暴"},{name:"生生暴",value:"生生暴"},{name:"速生暴",value:"速生暴"},{name:"速命生",value:"速命生"},{name:"速抗生",value:"速抗生"}]}},{key:"presetYuhunPackages",get:function(){return[{name:"破荒",value:["破势,4","荒骷髅,2"]},{name:"心荒",value:["心眼,4","荒骷髅,2"]},{name:"狂荒",value:["狂骨,4","荒骷髅,2"]},{name:"针荒",value:["针女,4","荒骷髅,2"]},{name:"散件生命",value:["生命加成,2","生命加成,2","生命加成,2"]}]}},{key:"effectiveAttributesChoices",get:function(){return[{name:"输出",value:"暴击,暴击伤害,攻击加成,速度"},{name:"奶盾",value:"暴击,暴击伤害,生命加成,速度"},{name:"命中",value:"效果命中,速度"},{name:"抵抗",value:"效果抵抗,速度"},{name:"双堆",value:"速度,效果命中,效果抵抗"}]}},{key:"effectiveAttributesBonusCountChoices",get:function(){return[{name:"1",value:"1,1,1,1,1,0"},{name:"2",value:"3,3,3,3,3,1"},{name:"3",value:"3,2,3,2,3,0"},{name:"4",value:"5,3,5,3,5,1"}]}},{key:"disableAddYuhunPackageButton",get:function(){if(!this.yuhunPackage)return!0;var t=0;if(this.yuhunPackage){var e=this.yuhunPackage.split(","),a=Object(tt["a"])(e,2),i=a[1];t=+i}return t+this.selectedYuhunPackageCount>6}},{key:"selectedYuhunPackageCount",get:function(){return this.yuhunPackageList.map(function(t){var e=t.split(","),a=Object(tt["a"])(e,2),i=a[1];return+i}).reduce(function(t,e){return t+e},0)}},{key:"currentScheme",get:function(){return{yuhunPackageList:this.yuhunPackageList,usePackage:this.usePackage,useAttack:this.useAttack,secondAttributeList:this.secondAttributeList,fourthAttributeList:this.fourthAttributeList,sixthAttributeList:this.sixthAttributeList,ignoreSerial:this.ignoreSerial,damageExpect:this.damageExpect,healthExpect:this.healthExpect,targetAttributeList:this.targetAttributeList}}}]),e}(h["c"]);l["a"]([Object(h["d"])("yuhunPackage")],Vt.prototype,"addYuhunPackageLimit",null),l["a"]([Object(h["d"])("targetAttribute")],Vt.prototype,"ontargetAttributeChange",null),l["a"]([Object(h["d"])("lowerValue")],Vt.prototype,"onLowerValueChange",null),l["a"]([Object(h["d"])("upperValue")],Vt.prototype,"onUpperValueChange",null),Vt=l["a"]([Object(h["a"])({components:{Form:W.a,FormItem:K.a,Button:U.a,ButtonGroup:J.a,Select:M.a,Option:z.a,OptionGroup:$.a,CheckboxGroup:Y.a,Checkbox:F.a,CheckboxButton:I.a,Radio:j.a,RadioButton:B.a,RadioGroup:_.a,Input:C.a,InputNumber:x.a,Tag:y.a,Dialog:k.a,CustomScheme:jt,Autocomplete:g.a,Popover:b.a}})],Vt);var Ft=Vt,Nt=Ft,Yt=(a("b8cc"),Object(Lt["a"])(Nt,m,p,!1,null,null,null));Yt.options.__file="Calculator.vue";var Tt=Yt.exports,$t=function(t){function e(){return Object(s["a"])(this,e),Object(r["a"])(this,Object(c["a"])(e).apply(this,arguments))}return Object(o["a"])(e,t),e}(h["c"]);$t=l["a"]([Object(h["a"])({components:{Calculator:Tt}})],$t);var Gt=$t,zt=Gt,Rt=(a("7c55"),Object(Lt["a"])(zt,u,n,!1,null,null,null));Rt.options.__file="App.vue";var Mt=Rt.exports;i["default"].config.productionTip=!1,new i["default"]({render:function(t){return t(Mt)}}).$mount("#app")}}); 2 | //# sourceMappingURL=app.5c0c5dc2.js.map --------------------------------------------------------------------------------