├── 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
--------------------------------------------------------------------------------