Compare commits

...

12 Commits

Author SHA1 Message Date
dependabot[bot]
2bedf4aaa1
build(deps): bump the pip group across 4 directories with 9 updates
Bumps the pip group with 4 updates in the / directory: [pip](https://github.com/pypa/pip), [python-multipart](https://github.com/Kludex/python-multipart), [setuptools](https://github.com/pypa/setuptools) and [urllib3](https://github.com/urllib3/urllib3).
Bumps the pip group with 3 updates in the /deploy/AidLux/0.92 directory: [pycryptodome](https://github.com/Legrandin/pycryptodome), [starlette](https://github.com/Kludex/starlette) and [mxnet](https://github.com/apache/incubator-mxnet).
Bumps the pip group with 4 updates in the /deploy/docker directory: [numpy](https://github.com/numpy/numpy), [pycryptodome](https://github.com/Legrandin/pycryptodome), [starlette](https://github.com/Kludex/starlette) and [mxnet](https://github.com/apache/incubator-mxnet).
Bumps the pip group with 5 updates in the /deploy/headless directory:

| Package | From | To |
| --- | --- | --- |
| [numpy](https://github.com/numpy/numpy) | `1.17.4` | `1.22.0` |
| [pillow](https://github.com/python-pillow/Pillow) | `9.5.0` | `12.2.0` |
| [pycryptodome](https://github.com/Legrandin/pycryptodome) | `3.9.9` | `3.19.1` |
| [starlette](https://github.com/Kludex/starlette) | `0.14.2` | `0.49.1` |
| [mxnet](https://github.com/apache/incubator-mxnet) | `1.6.0` | `1.9.1` |



Updates `pip` from 26.0.1 to 26.1
- [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/pip/compare/26.0.1...26.1)

Updates `python-multipart` from 0.0.26 to 0.0.27
- [Release notes](https://github.com/Kludex/python-multipart/releases)
- [Changelog](https://github.com/Kludex/python-multipart/blob/main/CHANGELOG.md)
- [Commits](https://github.com/Kludex/python-multipart/compare/0.0.26...0.0.27)

Updates `setuptools` from 75.8.2 to 78.1.1
- [Release notes](https://github.com/pypa/setuptools/releases)
- [Changelog](https://github.com/pypa/setuptools/blob/main/NEWS.rst)
- [Commits](https://github.com/pypa/setuptools/compare/v75.8.2...v78.1.1)

Updates `urllib3` from 2.6.3 to 2.7.0
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](https://github.com/urllib3/urllib3/compare/2.6.3...2.7.0)

Updates `pycryptodome` from 3.10.4 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `pycryptodome` from 3.10.4 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `pycryptodome` from 3.10.4 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `pycryptodome` from 3.10.4 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `numpy` from 1.16.6 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `numpy` from 1.16.6 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `numpy` from 1.16.6 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `numpy` from 1.16.6 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `numpy` from 1.17.4 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pillow` from 9.5.0 to 12.2.0
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.5.0...12.2.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `numpy` from 1.17.4 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pillow` from 9.5.0 to 12.2.0
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.5.0...12.2.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `numpy` from 1.17.4 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pillow` from 9.5.0 to 12.2.0
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.5.0...12.2.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

Updates `numpy` from 1.17.4 to 1.22.0
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v1.16.6...v1.22.0)

Updates `pillow` from 9.5.0 to 12.2.0
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/9.5.0...12.2.0)

Updates `pycryptodome` from 3.9.9 to 3.19.1
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](https://github.com/Legrandin/pycryptodome/compare/v3.10.4...v3.19.1)

Updates `mxnet` from 1.6.0 to 1.9.1
- [Release notes](https://github.com/apache/incubator-mxnet/releases)
- [Changelog](https://github.com/apache/mxnet/blob/master/NEWS.md)
- [Commits](https://github.com/apache/incubator-mxnet/compare/1.6.0...1.9.1)

Updates `starlette` from 0.14.2 to 0.49.1
- [Release notes](https://github.com/Kludex/starlette/releases)
- [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md)
- [Commits](https://github.com/Kludex/starlette/compare/0.14.2...0.49.1)

---
updated-dependencies:
- dependency-name: mxnet
  dependency-version: 1.9.1
  dependency-type: direct:production
- dependency-name: mxnet
  dependency-version: 1.9.1
  dependency-type: direct:production
- dependency-name: mxnet
  dependency-version: 1.9.1
  dependency-type: direct:production
- dependency-name: numpy
  dependency-version: 1.22.0
  dependency-type: direct:production
- dependency-name: numpy
  dependency-version: 1.22.0
  dependency-type: direct:production
- dependency-name: pillow
  dependency-version: 12.2.0
  dependency-type: direct:production
- dependency-name: pip
  dependency-version: '26.1'
  dependency-type: direct:production
- dependency-name: pycryptodome
  dependency-version: 3.19.1
  dependency-type: direct:production
- dependency-name: pycryptodome
  dependency-version: 3.19.1
  dependency-type: direct:production
- dependency-name: pycryptodome
  dependency-version: 3.19.1
  dependency-type: direct:production
- dependency-name: python-multipart
  dependency-version: 0.0.27
  dependency-type: direct:production
- dependency-name: setuptools
  dependency-version: 78.1.1
  dependency-type: direct:production
- dependency-name: starlette
  dependency-version: 0.49.1
  dependency-type: direct:production
- dependency-name: starlette
  dependency-version: 0.49.1
  dependency-type: direct:production
- dependency-name: starlette
  dependency-version: 0.49.1
  dependency-type: direct:production
- dependency-name: urllib3
  dependency-version: 2.7.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-12 14:02:13 +00:00
7f17c1c006
Merge pull request #464 from wess09/dev
Implement configuration system, i18n support, and OCR benchmarking
2026-05-12 21:56:52 +08:00
wess09
b95ef734ef fix 2026-05-12 21:55:27 +08:00
wess09
73123f9027 feat: implement new configuration system, i18n support, OCR benchmarking, and update dependencies 2026-05-12 21:49:18 +08:00
a4d0945e1e
Merge pull request #463 from wess09/dev
Add support for alternate team use after battle failure and mood cost calculation
2026-05-12 19:25:29 +08:00
wess09
506a1d12cf fix Bug 2026-05-12 19:23:00 +08:00
ad1e560e4b
Merge pull request #462 from wess09/master
Consolidate fixes and improvements
2026-05-12 18:56:21 +08:00
wess09
644181076a 分时视图支持拖动操作 2026-05-12 18:55:06 +08:00
wess09
f7100a2568 Merge branch 'dev' of https://github.com/wess09/AzurPilot into dev 2026-05-12 18:42:41 +08:00
wess09
71cb6f4c98 new 2026-05-12 18:42:39 +08:00
2de7cb7d44
Merge pull request #451 from moon-dim/master
feat: 新增道中战斗失败时可以使用另一队继续战斗,新增沉船时心情消耗计算
2026-05-12 17:53:18 +08:00
moon-dim
982697e18f feat: 新增道中战斗失败时可以使用另一队继续战斗,新增沉船时心情消耗计算 2026-05-11 22:39:22 +08:00
28 changed files with 370 additions and 222 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -198,7 +198,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -276,7 +277,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -354,7 +356,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -447,7 +450,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -527,7 +531,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -607,7 +612,8 @@
"UseAutoSearch": false,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -686,7 +692,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -764,7 +771,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -842,7 +850,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -924,7 +933,8 @@
"UseAutoSearch": false,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -979,7 +989,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1084,7 +1095,8 @@
"UseAutoSearch": false,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"Coalition": {
"Mode": "hard",
@ -1171,7 +1183,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1253,7 +1266,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1335,7 +1349,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1417,7 +1432,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1499,7 +1515,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1577,7 +1594,8 @@
"UseAutoSearch": true,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1658,7 +1676,8 @@
"UseAutoSearch": false,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"StopCondition": {
"OilLimit": 1000,
@ -1706,7 +1725,8 @@
"UseAutoSearch": false,
"Use2xBook": false,
"AmbushEvade": true,
"UseRecommendFleet": false
"UseRecommendFleet": false,
"DefeatWithdraw": true
},
"Coalition": {
"Mode": "hard",

View File

@ -7,13 +7,13 @@ imageio==2.27.0
inflection
jellyfish==0.11.2
lz4
mxnet==1.6.0
mxnet==1.9.1
numpy
onepush==1.4.0
pillow
prettytable==2.2.1
psutil==5.9.3
pycryptodome==3.10.4
pycryptodome==3.19.1
pydantic
pypresence==4.2.1
pywebio==1.6.2
@ -22,7 +22,7 @@ pyzmq==22.3.0
retrying
rich==11.2.0
scipy==1.7.1
starlette==0.14.2
starlette==0.49.1
tqdm
uiautomator2==2.16.17
uiautomator2cache==0.3.0.1

View File

@ -1,5 +1,5 @@
# Image processing
numpy==1.16.6
numpy==1.22.0
scipy==1.4.1
pillow
opencv-python-headless
@ -28,16 +28,16 @@ anyio==1.3.1
# Pushing
onepush==1.4.0
pycryptodome==3.9.9
pycryptodome==3.19.1
pypresence==4.2.1
# Ocr
cnocr==1.2.2
mxnet==1.6.0
mxnet==1.9.1
# Webui
pywebio==1.6.2
starlette==0.14.2
starlette==0.49.1
uvicorn[standard]==0.17.6
zerorpc==0.6.3
pyzmq==22.3.0

View File

@ -1,7 +1,7 @@
# Image processing
numpy==1.17.4
numpy==1.22.0
scipy==1.4.1
pillow==9.5.0
pillow==12.2.0
opencv-python-headless==4.7.0.72
imageio==2.27.0
@ -28,16 +28,16 @@ anyio==1.3.1
# Pushing
onepush==1.4.0
pycryptodome==3.9.9
pycryptodome==3.19.1
pypresence==4.2.1
# Ocr
cnocr==1.2.2
mxnet==1.6.0
mxnet==1.9.1
# Webui
pywebio==1.6.2
starlette==0.14.2
starlette==0.49.1
uvicorn[standard]==0.17.6
zerorpc==0.6.3
pyzmq==22.3.0

View File

@ -133,6 +133,7 @@ class CampaignBase(CampaignUI, Map, AutoSearchCombat):
else:
self.map = self.MAP
self.battle_count = 0
self.fleet_alive_multiple = self.config.Fleet_Fleet2 != 0
self.lv_reset()
self.lv_get()

View File

@ -5,7 +5,7 @@ from module.combat.combat import Combat
from module.exception import CampaignEnd
from module.handler.assets import AUTO_SEARCH_MAP_OPTION_ON, GET_MISSION
from module.logger import logger
from module.map.assets import WITHDRAW
from module.map.assets import WITHDRAW, SWITCH_OVER, FLEET_WITHDRAW, FLEET_SWITCH_CONFIRM
from module.map.map_operation import MapOperation
@ -285,15 +285,23 @@ class AutoSearchCombat(MapOperation, Combat, CampaignStatus):
if self.handle_get_ship():
continue
if self.appear_then_click(OPTS_INFO_D, offset=(30, 30), interval=2):
if emotion_reduce:
self.emotion.reduce(fleet_index, shipwreck=True)
self._withdraw = True
continue
break
if confirm_timer.reached():
self._withdraw = True
self.device.click(OPTS_INFO_D)
if emotion_reduce:
self.emotion.reduce(fleet_index, shipwreck=True)
confirm_timer.reset()
continue
if self.appear(BATTLE_STATUS_S) or self.appear(BATTLE_STATUS_A) or self.appear(BATTLE_STATUS_B) \
or self.appear(EXP_INFO_S) or self.appear(EXP_INFO_A) or self.appear(EXP_INFO_B) \
break
if self.appear(BATTLE_STATUS_A) or self.appear(BATTLE_STATUS_B) \
or self.appear(EXP_INFO_A) or self.appear(EXP_INFO_B):
if emotion_reduce:
self.emotion.reduce(fleet_index, shipwreck=True)
break
if self.appear(BATTLE_STATUS_S) or self.appear(EXP_INFO_S) \
or self.appear(GET_MISSION) or self.is_auto_search_running():
self.device.screenshot_interval_set()
break
@ -325,10 +333,29 @@ class AutoSearchCombat(MapOperation, Combat, CampaignStatus):
raise CampaignEnd
# Withdraw
if self._withdraw and get_urgent_commission and self.appear(WITHDRAW, offset=(30, 30)):
if self._withdraw:
if self.appear_then_click(FLEET_SWITCH_CONFIRM, offset=(30, 30)):
continue
if not self.appear(WITHDRAW, offset=(30, 30)):
continue
logger.info(f'fleet_alive_multiple: {self.fleet_alive_multiple}')
self._withdraw = False
self.withdraw()
break
if self.config.Campaign_DefeatWithdraw or not self.fleet_alive_multiple:
self.withdraw()
break
elif get_urgent_commission:
self.fleet_alive_multiple = False
continue
else:
while True:
self.device.screenshot()
if self.appear_then_click(FLEET_WITHDRAW, offset=(30, 30)):
break
if self.appear_then_click(SWITCH_OVER, interval=2):
continue
self.fleet_alive_multiple = False
continue
# Combat status
if self.handle_get_ship():

View File

@ -248,6 +248,10 @@ class Emotion:
return 4
else:
return 2
@property
def reduce_shipwreck(self):
return 10
def _check_reduce(self, battle):
"""
@ -339,7 +343,7 @@ class Emotion:
logger.attr('Wait until', recovered)
sleep(60)
def reduce(self, fleet_index):
def reduce(self, fleet_index, shipwreck=False):
"""
Reduce emotion of specific fleet.
Should be called after battle executing.
@ -347,6 +351,7 @@ class Emotion:
Args:
fleet_index (int): 1 or 2.
shipwreck (bool): Whether the fleet is at shipwreck.
"""
logger.hr('Emotion reduce')
self.update()
@ -356,8 +361,12 @@ class Emotion:
else:
fleet = self.fleets[fleet_index - 1]
fleet.current -= self.reduce_per_battle
self.total_reduced += self.reduce_per_battle
if not shipwreck:
fleet.current -= self.reduce_per_battle
self.total_reduced += self.reduce_per_battle
else:
fleet.current -= self.reduce_shipwreck
self.total_reduced += self.reduce_shipwreck
self.record()
self.show()

View File

@ -468,7 +468,8 @@
"option": [
"auto",
"cpu",
"gpu"
"gpu",
"ane"
]
},
"ScreenshotInterval": {
@ -878,6 +879,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -1274,6 +1279,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -1670,6 +1679,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -2198,6 +2211,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -2693,6 +2710,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -3153,6 +3174,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -3589,6 +3614,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -4024,6 +4053,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -4458,6 +4491,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -4896,6 +4933,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -5167,6 +5208,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -5727,6 +5772,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"Coalition": {
@ -6378,6 +6427,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -6825,6 +6878,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -7277,6 +7334,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -7729,6 +7790,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -8181,6 +8246,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -8623,6 +8692,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -9057,6 +9130,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"StopCondition": {
@ -9302,6 +9379,10 @@
"UseRecommendFleet": {
"type": "checkbox",
"value": false
},
"DefeatWithdraw": {
"type": "checkbox",
"value": true
}
},
"Coalition": {

View File

@ -163,7 +163,7 @@ Error:
Optimization:
OcrDevice:
value: auto
option: [auto, cpu, gpu]
option: [auto, cpu, gpu, ane]
ScreenshotInterval: 0.3
CombatScreenshotInterval: 1.0
TaskHoardingDuration: 0
@ -292,6 +292,7 @@ Campaign:
Use2xBook: false
AmbushEvade: true
UseRecommendFleet: false
DefeatWithdraw: true
StopCondition:
OilLimit: 1000
CoinLimit: 0

View File

@ -1,5 +1,7 @@
import copy
import operator
import platform
import sys
import threading
from datetime import datetime, timedelta
@ -186,7 +188,12 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher
def ocr_device(self) -> str:
val = self.Optimization_OcrDevice
if val == 'auto':
if sys.platform == 'darwin' and platform.machine() == 'arm64':
return 'ane'
return 'gpu' if is_good_gpu() else 'cpu'
if val == 'ane' and sys.platform != 'darwin':
logger.warning("当前系统非 macOS不使用 Apple Neural Engine")
return 'cpu'
return val
@property

View File

@ -113,7 +113,7 @@ class GeneratedConfig:
Error_LlmModel = 'Nvidia/qwen/qwen2.5-coder-32b-instruct'
# Group `Optimization`
Optimization_OcrDevice = 'auto' # auto, cpu, gpu
Optimization_OcrDevice = 'auto' # auto, cpu, gpu, ane
Optimization_ScreenshotInterval = 0.3
Optimization_CombatScreenshotInterval = 1.0
Optimization_TaskHoardingDuration = 0
@ -178,6 +178,7 @@ class GeneratedConfig:
Campaign_Use2xBook = False
Campaign_AmbushEvade = True
Campaign_UseRecommendFleet = False
Campaign_DefeatWithdraw = True
# Group `StopCondition`
StopCondition_OilLimit = 1000

View File

@ -609,9 +609,10 @@
"OcrDevice": {
"name": "Optimization.OcrDevice.name",
"help": "Optimization.OcrDevice.help",
"auto": "auto",
"cpu": "cpu",
"gpu": "gpu"
"auto": "Auto",
"cpu": "CPU",
"gpu": "GPU",
"ane": "ANE (Apple)"
},
"ScreenshotInterval": {
"name": "Slow Down Screenshot Speed to X Seconds per Frame",
@ -1046,6 +1047,10 @@
"UseRecommendFleet": {
"name": "Auto Fleet Composition",
"help": "Use the game's built-in recommended fleet to automatically compose hard mode fleets"
},
"DefeatWithdraw": {
"name": "Retreat after defeat in Mob",
"help": "The handling method for defeat in Mob during the journey. Upon initiation, exit the sea area directly; upon termination, attempt to have another team take over the subsequent battle."
}
},
"StopCondition": {

View File

@ -607,11 +607,12 @@
"help": ""
},
"OcrDevice": {
"name": "Optimization.OcrDevice.name",
"help": "Optimization.OcrDevice.help",
"auto": "auto",
"cpu": "cpu",
"gpu": "gpu"
"name": "OCR设备选择",
"help": "",
"auto": "自動",
"cpu": "CPU",
"gpu": "GPU",
"ane": "ANE (Apple)"
},
"ScreenshotInterval": {
"name": "スクリーンショット速度をX秒に1枚に減速",
@ -1046,6 +1047,10 @@
"UseRecommendFleet": {
"name": "自動編成",
"help": "ゲーム内蔵の推奨編成を使用してハードステージの編成を自動的に完了します"
},
"DefeatWithdraw": {
"name": "道中での戦闘で敗北し撤退",
"help": "道中戦闘で敗北した際の処理方法。オンの場合は海域から直接退出し、オフの場合は別のチームが引き継いで後続の戦闘を試みる。"
}
},
"StopCondition": {

View File

@ -608,10 +608,11 @@
},
"OcrDevice": {
"name": "OCR设备",
"help": "选择OCR设备不推荐任何没有独立显卡的用户使用GPU除非你知道自己在做什么\n非Windows系统目前不支持GPU\n自动选择显存超过1G时使用GPU否则使用CPU",
"help": "选择OCR设备不推荐任何没有独立显卡的用户使用GPU除非你知道自己在做什么\nGPU 仅用于 Windows DirectMLANE 仅用于 macOS Apple Neural Engine\n自动选择Apple Silicon Mac 使用 ANEWindows 显存超过1G时使用GPU否则使用CPU",
"auto": "自动选择",
"cpu": "CPU",
"gpu": "GPU"
"gpu": "GPU",
"ane": "ANE (Apple)"
},
"ScreenshotInterval": {
"name": "放慢截图速度至 X 秒一张",
@ -1046,6 +1047,10 @@
"UseRecommendFleet": {
"name": "自动配队",
"help": "使用游戏内置的推荐阵容自动完成困难图配队"
},
"DefeatWithdraw": {
"name": "道中战斗失败撤退",
"help": "道中战斗战败时的处理方式。开启时直接退出海域,关闭时尝试由另一队接管后续战斗。"
}
},
"StopCondition": {

View File

@ -608,10 +608,11 @@
},
"OcrDevice": {
"name": "OCR设备选择 (´・ω・`)",
"help": "OCR跑在CPU或GPU上。没独显别选GPU喵。自动显存>1G用GPU否则CPU。非Windows无GPU。",
"help": "OCR跑在CPU/GPU/ANE上。GPU仅用于Windows DirectMLANE仅用于macOS Apple Neural Engine。自动Apple Silicon Mac用ANEWindows显存>1G用GPU否则CPU。",
"auto": "自动判断 (`・ω・´)",
"cpu": "CPU喵",
"gpu": "GPU喵"
"gpu": "GPU喵",
"ane": "ANE (Apple)喵"
},
"ScreenshotInterval": {
"name": "放慢截图至 X 秒一张 (´-ω-`)zzz",
@ -1046,6 +1047,10 @@
"UseRecommendFleet": {
"name": "自动配队困难图 (`・ω・´)ゞ",
"help": "使用游戏推荐阵容自动完成困难配队喵~"
},
"DefeatWithdraw": {
"name": "道中战斗失败撤退 (`・ω・´)",
"help": "道中战斗战败时的处理方式喵。开启时直接退出海域,关闭时尝试由另一队接管后续战斗喵。"
}
},
"StopCondition": {

View File

@ -610,8 +610,9 @@
"name": "Optimization.OcrDevice.name",
"help": "Optimization.OcrDevice.help",
"auto": "auto",
"cpu": "cpu",
"gpu": "gpu"
"cpu": "CPU",
"gpu": "GPU",
"ane": "ANE (Apple)"
},
"ScreenshotInterval": {
"name": "放慢截圖速度至 X 秒一張",
@ -1046,6 +1047,10 @@
"UseRecommendFleet": {
"name": "自動配隊",
"help": "使用遊戲內建的推薦陣容自動完成困難圖配隊"
},
"DefeatWithdraw": {
"name": "道中戰鬥失敗撤退",
"help": "道中戰鬥戰敗時的處理方式。開啟時直接退出海域,關閉時嘗試由另一隊接管後續戰鬥。"
}
},
"StopCondition": {

View File

@ -1,23 +1,33 @@
import os
import platform
import shutil
import sys
import time
import cv2
from rich.table import Table
from rich.text import Text
from module.daemon.daemon_base import DaemonBase
from module.config.config import AzurLaneConfig
from module.exception import RequestHumanTakeover
from module.logger import logger
from module.ocr.al_ocr import AlOcr
class OcrBenchmark(DaemonBase):
class OcrBenchmark:
# Each entry: (model_name, dataset_prefix, subfolder_name)
BENCHMARKS = [
('en', 'sets_num', 'sets_num'),
('cn', 'sets_zhcn', 'sets_zhcn'),
]
def __init__(self, config, device=None, task=None):
if isinstance(config, AzurLaneConfig):
self.config = config
if task is not None:
self.config.init_task(task)
else:
self.config = AzurLaneConfig(config, task=task)
def _find_archive(self, prefix):
for ext in ['.zip', '.tar', '.tar.xz', '.tar.gz']:
path = f'module/daemon/{prefix}{ext}'
@ -54,12 +64,14 @@ class OcrBenchmark(DaemonBase):
if avg_ms < 300.0: return 'Very Slow', 'red'
return 'Ultra Slow', 'bold red'
def _run_single(self, model_name, dataset_prefix, subfolder, use_gpu=None):
def _run_single(self, model_name, dataset_prefix, subfolder, use_gpu=None, ocr_device=None):
logger.hr(f'Benchmark: {model_name.upper()} model | dataset: {dataset_prefix}', level=2)
# --- Dynamic GPU config ---
if use_gpu is not None:
self.config.override(Optimization_OcrDevice='gpu' if use_gpu else 'cpu')
# --- Dynamic OCR device config ---
if ocr_device is None and use_gpu is not None:
ocr_device = 'gpu' if use_gpu else 'cpu'
if ocr_device is not None:
self.config.override(Optimization_OcrDevice=ocr_device)
from module.ocr.al_ocr import reset_ocr_model
reset_ocr_model()
@ -215,17 +227,22 @@ class OcrBenchmark(DaemonBase):
def run_simple_ocr_benchmark(self):
"""
Returns:
str: 'gpu' if accuracy is 100% on a simple test set, else 'cpu'.
str: Best OCR device for this machine.
"""
logger.hr('Simple OCR Benchmark', level=1)
logger.info('Testing OCR with GPU...')
res = self._run_single('en', 'sets_num', 'sets_num', use_gpu=True)
if sys.platform == 'darwin' and platform.machine() == 'arm64':
logger.info('Testing OCR with ANE...')
device = 'ane'
else:
logger.info('Testing OCR with GPU...')
device = 'gpu'
res = self._run_single('en', 'sets_num', 'sets_num', ocr_device=device)
if res and res['accuracy'] >= 100.0:
logger.info('OCR accuracy is 100% with GPU, use GPU.')
return 'gpu'
logger.info(f'OCR accuracy is 100% with {device.upper()}, use {device.upper()}.')
return device
else:
logger.info('OCR accuracy is not 100% with GPU or test failed, fallback to CPU.')
logger.info(f'OCR accuracy is not 100% with {device.upper()} or test failed, fallback to CPU.')
return 'cpu'
@ -235,4 +252,4 @@ def run_ocr_benchmark(config):
return True
except RequestHumanTakeover:
logger.critical('错误 请求人类接管')
return False
return False

View File

@ -22,6 +22,8 @@ FLEET_NUM_2 = Button(area={'cn': (212, 75, 226, 101), 'en': (212, 75, 226, 101),
FLEET_PREPARATION = Button(area={'cn': (1013, 558, 1141, 588), 'en': (1048, 569, 1086, 595), 'jp': (1046, 558, 1107, 587), 'tw': (1014, 557, 1142, 588)}, color={'cn': (242, 211, 160), 'en': (241, 201, 148), 'jp': (241, 205, 151), 'tw': (242, 208, 157)}, button={'cn': (980, 549, 1181, 612), 'en': (988, 556, 1145, 606), 'jp': (983, 549, 1185, 612), 'tw': (980, 548, 1180, 612)}, file={'cn': './assets/cn/map/FLEET_PREPARATION.png', 'en': './assets/en/map/FLEET_PREPARATION.png', 'jp': './assets/jp/map/FLEET_PREPARATION.png', 'tw': './assets/tw/map/FLEET_PREPARATION.png'})
FLEET_PREPARATION_CHECK = Button(area={'cn': (1146, 107, 1174, 136), 'en': (1129, 111, 1158, 140), 'jp': (1146, 107, 1174, 136), 'tw': (1145, 106, 1175, 136)}, color={'cn': (180, 98, 111), 'en': (189, 105, 109), 'jp': (180, 98, 111), 'tw': (180, 90, 92)}, button={'cn': (1146, 107, 1174, 136), 'en': (1129, 111, 1158, 140), 'jp': (1146, 107, 1174, 136), 'tw': (1145, 106, 1175, 136)}, file={'cn': './assets/cn/map/FLEET_PREPARATION_CHECK.png', 'en': './assets/en/map/FLEET_PREPARATION_CHECK.png', 'jp': './assets/jp/map/FLEET_PREPARATION_CHECK.png', 'tw': './assets/tw/map/FLEET_PREPARATION_CHECK.png'})
FLEET_SUPPORT_EMPTY = Button(area={'cn': (454, 470, 538, 540), 'en': (454, 470, 538, 540), 'jp': (454, 470, 538, 540), 'tw': (454, 470, 538, 540)}, color={'cn': (47, 54, 77), 'en': (47, 54, 77), 'jp': (47, 54, 77), 'tw': (47, 54, 77)}, button={'cn': (454, 470, 538, 540), 'en': (454, 470, 538, 540), 'jp': (454, 470, 538, 540), 'tw': (454, 470, 538, 540)}, file={'cn': './assets/cn/map/FLEET_SUPPORT_EMPTY.png', 'en': './assets/cn/map/FLEET_SUPPORT_EMPTY.png', 'jp': './assets/cn/map/FLEET_SUPPORT_EMPTY.png', 'tw': './assets/cn/map/FLEET_SUPPORT_EMPTY.png'})
FLEET_SWITCH_CONFIRM = Button(area={'cn': (553, 481, 728, 541), 'en': (553, 481, 728, 541), 'jp': (553, 481, 728, 541), 'tw': (553, 481, 728, 541)}, color={'cn': (98, 144, 201), 'en': (98, 144, 201), 'jp': (98, 144, 201), 'tw': (98, 144, 201)}, button={'cn': (553, 481, 728, 541), 'en': (553, 481, 728, 541), 'jp': (553, 481, 728, 541), 'tw': (553, 481, 728, 541)}, file={'cn': './assets/cn/map/FLEET_SWITCH_CONFIRM.png', 'en': './assets/cn/map/FLEET_SWITCH_CONFIRM.png', 'jp': './assets/cn/map/FLEET_SWITCH_CONFIRM.png', 'tw': './assets/cn/map/FLEET_SWITCH_CONFIRM.png'})
FLEET_WITHDRAW = Button(area={'cn': (714, 356, 773, 380), 'en': (714, 356, 773, 380), 'jp': (714, 356, 773, 380), 'tw': (714, 356, 773, 380)}, color={'cn': (175, 188, 201), 'en': (175, 188, 201), 'jp': (175, 188, 201), 'tw': (175, 188, 201)}, button={'cn': (714, 356, 773, 380), 'en': (714, 356, 773, 380), 'jp': (714, 356, 773, 380), 'tw': (714, 356, 773, 380)}, file={'cn': './assets/cn/map/FLEET_WITHDRAW.png', 'en': './assets/cn/map/FLEET_WITHDRAW.png', 'jp': './assets/cn/map/FLEET_WITHDRAW.png', 'tw': './assets/cn/map/FLEET_WITHDRAW.png'})
MAP_CAT_ATTACK = Button(area={'cn': (1237, 103, 1252, 153), 'en': (1237, 103, 1252, 153), 'jp': (1237, 103, 1252, 153), 'tw': (1237, 103, 1252, 153)}, color={'cn': (43, 45, 52), 'en': (43, 45, 52), 'jp': (43, 45, 52), 'tw': (43, 45, 52)}, button={'cn': (1148, 653, 1262, 705), 'en': (1147, 651, 1263, 701), 'jp': (1149, 653, 1261, 704), 'tw': (1148, 653, 1262, 705)}, file={'cn': './assets/cn/map/MAP_CAT_ATTACK.png', 'en': './assets/en/map/MAP_CAT_ATTACK.png', 'jp': './assets/jp/map/MAP_CAT_ATTACK.png', 'tw': './assets/tw/map/MAP_CAT_ATTACK.png'})
MAP_CAT_ATTACK_MIRROR = Button(area={'cn': (147, 145, 187, 157), 'en': (147, 145, 187, 157), 'jp': (147, 145, 187, 157), 'tw': (147, 145, 187, 157)}, color={'cn': (214, 191, 99), 'en': (214, 191, 99), 'jp': (214, 191, 99), 'tw': (214, 191, 99)}, button={'cn': (147, 145, 187, 157), 'en': (147, 145, 187, 157), 'jp': (147, 145, 187, 157), 'tw': (147, 145, 187, 157)}, file={'cn': './assets/cn/map/MAP_CAT_ATTACK_MIRROR.png', 'en': './assets/en/map/MAP_CAT_ATTACK_MIRROR.png', 'jp': './assets/jp/map/MAP_CAT_ATTACK_MIRROR.png', 'tw': './assets/tw/map/MAP_CAT_ATTACK_MIRROR.png'})
MAP_MODE_SWITCH_HARD = Button(area={'cn': (341, 580, 374, 617), 'en': (341, 580, 374, 617), 'jp': (341, 580, 374, 617), 'tw': (341, 580, 374, 617)}, color={'cn': (234, 179, 179), 'en': (234, 179, 179), 'jp': (234, 179, 179), 'tw': (234, 179, 179)}, button={'cn': (341, 580, 374, 617), 'en': (341, 580, 374, 617), 'jp': (341, 580, 374, 617), 'tw': (341, 580, 374, 617)}, file={'cn': './assets/cn/map/MAP_MODE_SWITCH_HARD.png', 'en': './assets/cn/map/MAP_MODE_SWITCH_HARD.png', 'jp': './assets/cn/map/MAP_MODE_SWITCH_HARD.png', 'tw': './assets/cn/map/MAP_MODE_SWITCH_HARD.png'})

View File

@ -378,6 +378,8 @@ class MapOperation(MysteryHandler, FleetPreparation, Retirement, FastForwardHand
else:
self.device.screenshot()
if self.appear_then_click(FLEET_SWITCH_CONFIRM, offset=(30, 30)):
continue
if self.handle_popup_confirm('WITHDRAW'):
continue
if self.appear_then_click(WITHDRAW, interval=5):

View File

@ -111,6 +111,7 @@ class RecOnlyOCR(RapidOCR):
self.use_rec = cfg.Global.use_rec
cfg.Rec.engine_cfg = cfg.EngineConfig[cfg.Rec.engine_type.value]
cfg.Rec.font_path = cfg.Global.font_path
cfg.Rec.model_root_dir = cfg.Global.get("model_root_dir", os.getcwd())
self.text_rec = TextRecognizer(cfg.Rec)
self.load_img = LoadImage()
@ -124,7 +125,9 @@ class RecOnlyOCR(RapidOCR):
def _create_ocr(model_path, rec_keys_path, ocr_version):
use_gpu = config.ocr_device == 'gpu'
ocr_device = config.ocr_device
use_dml = os.name == 'nt' and ocr_device == 'gpu'
use_coreml = ocr_device == 'ane'
params = {
"Global.use_det": False,
"Global.use_cls": False,
@ -133,7 +136,9 @@ def _create_ocr(model_path, rec_keys_path, ocr_version):
"Rec.ocr_version": ocr_version,
"Rec.model_path": model_path,
"Rec.rec_keys_path": rec_keys_path,
"EngineConfig.onnxruntime.use_dml": use_gpu,
"EngineConfig.onnxruntime.use_dml": use_dml,
"EngineConfig.onnxruntime.use_coreml": use_coreml,
"EngineConfig.onnxruntime.coreml_ep_cfg.MLComputeUnits": "CPUAndNeuralEngine",
}
return RecOnlyOCR(params=params)

View File

@ -623,7 +623,7 @@ class AlasGUI(Frame):
coins_legend_html += '<span style="display:flex; align-items:center; gap:4px;"><span style="width:12px; height:2px; background:#ce93d8; border-radius:1px; border-top:1px dashed #ce93d8;"></span>紫币</span>'
chart_id = f"ap_cv_{id(self)}"
detail_controls_display = 'display:flex;' if is_detail_mode else 'display:none;'
detail_controls_display = 'display:flex;' if current_view in ('line', 'detail') else 'display:none;'
html_tpl = read_webapp_template('ap_chart_panel.html')
html = html_tpl.format(

View File

@ -13,7 +13,7 @@ jellyfish
pyyaml
inflection
pywebio
starlette==0.49.1
starlette==1.0.0
anyio
uvicorn[standard]
aiofiles
@ -31,13 +31,13 @@ typing_extensions>=4.8.0
pydantic>=2.5.0
rapidocr
onnxruntime-directml>=1.24.4; sys_platform == "win32"
onnxruntime>=1.24.4; sys_platform == "linux"
onnxruntime @ https://files.pythonhosted.org/packages/fb/aa/04530bd38e31e26970fa1212346d76cf81705dc16a8ee5e6f4fb24634c11/onnxruntime-1.25.1-cp314-cp314-macosx_14_0_arm64.whl; sys_platform == "darwin" and platform_machine == "arm64"
onnxruntime==1.26.0; sys_platform == "linux"
onnxruntime @ https://files.pythonhosted.org/packages/40/89/17546c1c20f6bfc3ae41c22152378a26edfea918af3129e2139dcd7c99f3/onnxruntime-1.26.0-cp314-cp314-macosx_14_0_arm64.whl; sys_platform == "darwin" and platform_machine == "arm64"
watchdog>=2.0.0
numba
pip
lz4
setuptools==75.8.2
setuptools==78.1.1
importlib_resources>=6.0.0
importlib_metadata>=8.0.0
openai

View File

@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile requirements-in.txt --python-platform linux --python-version 3.14 --annotation-style=line --no-emit-index-url --output-file requirements-linux.txt
# uv pip compile requirements-in.txt --annotation-style=line --python-platform linux --python-version 3.14 --output-file requirements-linux.txt
adbutils==0.11.0 # via uiautomator2, -r requirements-in.txt
aiofiles==23.1.0 # via -r requirements-in.txt
annotated-types==0.7.0 # via pydantic
@ -52,18 +52,17 @@ lxml==6.1.0 # via uiautomator2
lz4==4.4.5 # via -r requirements-in.txt
matplotlib==3.10.8 # via -r requirements-in.txt
mcp==1.23.0 # via -r requirements-in.txt
mpmath==1.3.0 # via sympy
msgpack==1.0.4 # via zerorpc
numba==0.64.0 # via -r requirements-in.txt
numpy==2.4.3 # via contourpy, imageio, matplotlib, numba, onnxruntime, opencv-python, rapidocr, scipy, shapely, -r requirements-in.txt
numpy==2.4.4 # via contourpy, imageio, matplotlib, numba, onnxruntime, opencv-python, rapidocr, scipy, shapely, -r requirements-in.txt
omegaconf==2.3.0 # via rapidocr
onepush==1.2.0 # via -r requirements-in.txt
onnxruntime==1.24.4 # via -r requirements-in.txt
onnxruntime==1.26.0 # via -r requirements-in.txt
openai==2.30.0 # via -r requirements-in.txt
opencv-python==4.13.0.92 # via rapidocr, -r requirements-in.txt
packaging==20.9 # via deprecation, matplotlib, onnxruntime, uiautomator2
pillow==12.2.0 # via imageio, matplotlib, rapidocr, uiautomator2, -r requirements-in.txt
pip==26.0.1 # via -r requirements-in.txt
pip==26.1 # via -r requirements-in.txt
progress==1.6 # via uiautomator2
protobuf==6.33.5 # via onnxruntime
psutil==5.9.4 # via -r requirements-in.txt
@ -81,7 +80,7 @@ pyparsing==3.0.9 # via matplotlib, packaging
pypresence==4.2.1 # via -r requirements-in.txt
python-dateutil==2.9.0.post0 # via matplotlib
python-dotenv==1.2.2 # via pydantic-settings, uvicorn
python-multipart==0.0.26 # via mcp
python-multipart==0.0.27 # via mcp
pywebio==1.7.1 # via -r requirements-in.txt
pyyaml==6.0.3 # via omegaconf, rapidocr, uvicorn, -r requirements-in.txt
pyzmq==27.1.0 # via zerorpc, -r requirements-in.txt
@ -93,13 +92,12 @@ retrying==1.3.4 # via -r requirements-in.txt
rich==11.2.0 # via -r requirements-in.txt
rpds-py==0.30.0 # via jsonschema, referencing
scipy==1.17.1 # via -r requirements-in.txt
setuptools==75.8.2 # via zope-event, zope-interface, -r requirements-in.txt
setuptools==78.1.1 # via zope-event, zope-interface, -r requirements-in.txt
shapely==2.1.2 # via rapidocr
six==1.16.0 # via adbutils, python-dateutil, rapidocr, retrying, uiautomator2
sniffio==1.3.0 # via openai
sse-starlette==3.0.3 # via mcp, -r requirements-in.txt
starlette==0.49.1 # via mcp, -r requirements-in.txt
sympy==1.14.0 # via onnxruntime
starlette==1.0.0 # via mcp, -r requirements-in.txt
tornado==6.5.5 # via pywebio
tqdm==4.67.3 # via openai, rapidocr
typing-extensions==4.15.0 # via mcp, openai, pydantic, pydantic-core, typing-inspection, -r requirements-in.txt
@ -107,7 +105,7 @@ typing-inspection==0.4.2 # via mcp, pydantic, pydantic-settings
ua-parser==0.16.1 # via user-agents
uiautomator2==2.16.17 # via -r requirements-in.txt
uiautomator2cache==0.3.0.1 # via -r requirements-in.txt
urllib3==2.6.3 # via requests
urllib3==2.7.0 # via requests
user-agents==2.2.0 # via pywebio
uvicorn==0.44.0 # via mcp, -r requirements-in.txt
uvloop==0.22.1 # via uvicorn

View File

@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile requirements-in.txt --annotation-style=line --python-platform aarch64-apple-darwin --python-version 3.14 --output-file requirements-macos.txt
# uv pip compile requirements-in.txt --annotation-style=line --python-platform macos --python-version 3.14 --output-file requirements-macos.txt
adbutils==0.16.2 # via uiautomator2, -r requirements-in.txt
aiofiles==25.1.0 # via -r requirements-in.txt
annotated-types==0.7.0 # via pydantic
@ -56,12 +56,12 @@ numba==0.65.0 # via -r requirements-in.txt
numpy==2.4.4 # via contourpy, imageio, matplotlib, numba, onnxruntime, opencv-python, rapidocr, scipy, shapely, -r requirements-in.txt
omegaconf==2.3.0 # via rapidocr
onepush==1.8.0 # via -r requirements-in.txt
onnxruntime @ https://files.pythonhosted.org/packages/fb/aa/04530bd38e31e26970fa1212346d76cf81705dc16a8ee5e6f4fb24634c11/onnxruntime-1.25.1-cp314-cp314-macosx_14_0_arm64.whl # via -r requirements-in.txt
onnxruntime @ https://files.pythonhosted.org/packages/40/89/17546c1c20f6bfc3ae41c22152378a26edfea918af3129e2139dcd7c99f3/onnxruntime-1.26.0-cp314-cp314-macosx_14_0_arm64.whl # via -r requirements-in.txt
openai==2.31.0 # via -r requirements-in.txt
opencv-python==4.13.0.92 # via rapidocr, -r requirements-in.txt
packaging==20.9 # via deprecation, matplotlib, onnxruntime, uiautomator2
pillow==12.2.0 # via adbutils, imageio, matplotlib, rapidocr, uiautomator2, -r requirements-in.txt
pip==26.0.1 # via -r requirements-in.txt
pip==26.1 # via -r requirements-in.txt
progress==1.6.1 # via uiautomator2
protobuf==7.34.1 # via onnxruntime
psutil==7.2.2 # via -r requirements-in.txt
@ -80,7 +80,7 @@ pypresence==4.6.1 # via -r requirements-in.txt
pysocks==1.7.1 # via requests
python-dateutil==2.9.0.post0 # via matplotlib
python-dotenv==1.2.2 # via pydantic-settings, uvicorn
python-multipart==0.0.26 # via mcp
python-multipart==0.0.27 # via mcp
pywebio==1.8.4 # via -r requirements-in.txt
pyyaml==6.0.3 # via omegaconf, rapidocr, uvicorn, -r requirements-in.txt
pyzmq==27.1.0 # via zerorpc, -r requirements-in.txt
@ -92,12 +92,12 @@ retrying==1.4.2 # via -r requirements-in.txt
rich==15.0.0 # via -r requirements-in.txt
rpds-py==0.30.0 # via jsonschema, referencing
scipy==1.17.1 # via -r requirements-in.txt
setuptools==75.8.2 # via -r requirements-in.txt
setuptools==78.1.1 # via -r requirements-in.txt
shapely==2.1.2 # via rapidocr
six==1.17.0 # via python-dateutil, rapidocr, uiautomator2
sniffio==1.3.1 # via openai
sse-starlette==3.0.3 # via mcp, -r requirements-in.txt
starlette==0.49.1 # via mcp, -r requirements-in.txt
starlette==1.0.0 # via mcp, -r requirements-in.txt
tornado==6.5.5 # via pywebio
tqdm==4.67.3 # via openai, rapidocr
typing-extensions==4.15.0 # via mcp, openai, pydantic, pydantic-core, typing-inspection, -r requirements-in.txt
@ -106,7 +106,7 @@ ua-parser==1.0.2 # via user-agents
ua-parser-builtins==202603 # via ua-parser
uiautomator2==2.12.2 # via -r requirements-in.txt
uiautomator2cache==0.3.1 # via -r requirements-in.txt
urllib3==2.6.3 # via requests
urllib3==2.7.0 # via requests
user-agents==2.2.0 # via pywebio
uvicorn==0.44.0 # via mcp, -r requirements-in.txt
uvloop==0.22.1 # via uvicorn

View File

@ -1,5 +1,5 @@
# This file was autogenerated by uv via the following command:
# uv pip compile --annotation-style=line --no-emit-index-url --output-file=requirements.txt --resolver=backtracking requirements-in.txt
# uv pip compile requirements-in.txt --annotation-style=line --python-platform windows --python-version 3.14 --output-file requirements.txt
adbutils==0.11.0 # via uiautomator2, -r requirements-in.txt
aiofiles==23.1.0 # via -r requirements-in.txt
annotated-types==0.7.0 # via pydantic
@ -55,7 +55,7 @@ mcp==1.23.0 # via -r requirements-in.txt
mpmath==1.3.0 # via sympy
msgpack==1.0.4 # via zerorpc
numba==0.64.0 # via -r requirements-in.txt
numpy==2.4.3 # via contourpy, imageio, matplotlib, numba, onnxruntime-directml, opencv-python, rapidocr, scipy, shapely, -r requirements-in.txt
numpy==2.4.4 # via contourpy, imageio, matplotlib, numba, onnxruntime-directml, opencv-python, rapidocr, scipy, shapely, -r requirements-in.txt
omegaconf==2.3.0 # via rapidocr
onepush==1.2.0 # via -r requirements-in.txt
onnxruntime-directml==1.24.4 # via -r requirements-in.txt
@ -63,7 +63,7 @@ openai==2.30.0 # via -r requirements-in.txt
opencv-python==4.13.0.92 # via rapidocr, -r requirements-in.txt
packaging==20.9 # via deprecation, matplotlib, onnxruntime-directml, uiautomator2
pillow==12.2.0 # via imageio, matplotlib, rapidocr, uiautomator2, -r requirements-in.txt
pip==26.0.1 # via -r requirements-in.txt
pip==26.1 # via -r requirements-in.txt
progress==1.6 # via uiautomator2
protobuf==6.33.5 # via onnxruntime-directml
psutil==5.9.4 # via -r requirements-in.txt
@ -81,7 +81,7 @@ pyparsing==3.0.9 # via matplotlib, packaging
pypresence==4.2.1 # via -r requirements-in.txt
python-dateutil==2.9.0.post0 # via matplotlib
python-dotenv==1.2.2 # via pydantic-settings, uvicorn
python-multipart==0.0.26 # via mcp
python-multipart==0.0.27 # via mcp
pywebio==1.7.1 # via -r requirements-in.txt
pywin32==311 # via mcp
pyyaml==6.0.3 # via omegaconf, rapidocr, uvicorn, -r requirements-in.txt
@ -94,12 +94,12 @@ retrying==1.3.4 # via -r requirements-in.txt
rich==11.2.0 # via -r requirements-in.txt
rpds-py==0.30.0 # via jsonschema, referencing
scipy==1.17.1 # via -r requirements-in.txt
setuptools==75.8.2 # via zope-event, zope-interface, -r requirements-in.txt
setuptools==78.1.1 # via zope-event, zope-interface, -r requirements-in.txt
shapely==2.1.2 # via rapidocr
six==1.16.0 # via adbutils, python-dateutil, rapidocr, retrying, uiautomator2
sniffio==1.3.0 # via openai
sse-starlette==3.0.3 # via mcp, -r requirements-in.txt
starlette==0.49.1 # via mcp, -r requirements-in.txt
starlette==1.0.0 # via mcp, -r requirements-in.txt
sympy==1.14.0 # via onnxruntime-directml
tornado==6.5.5 # via pywebio
tqdm==4.67.3 # via openai, rapidocr
@ -108,7 +108,7 @@ typing-inspection==0.4.2 # via mcp, pydantic, pydantic-settings
ua-parser==0.16.1 # via user-agents
uiautomator2==2.16.17 # via -r requirements-in.txt
uiautomator2cache==0.3.0.1 # via -r requirements-in.txt
urllib3==2.6.3 # via requests
urllib3==2.7.0 # via requests
user-agents==2.2.0 # via pywebio
uvicorn==0.44.0 # via mcp, -r requirements-in.txt
watchdog==6.0.0 # via -r requirements-in.txt

View File

@ -340,123 +340,75 @@
oc.scale(dpr, dpr);
if (chartType === 'line') {
var visibleStart = Math.max(0, Math.floor(panOffset));
var visibleCount = Math.ceil(nn / zoomLevel);
var visibleEnd = Math.min(nn, visibleStart + visibleCount);
var visibleNn = visibleEnd - visibleStart;
var dMin = Infinity, dMax = -Infinity;
for (var i = visibleStart; i < visibleEnd; i++) {
if (ap[i] < dMin) dMin = ap[i];
if (ap[i] > dMax) dMax = ap[i];
}
if (dMin === Infinity) dMin = 0;
if (dMax === -Infinity) dMax = 100;
var drng = dMax - dMin || 1;
dMin -= drng * 0.1;
dMax += drng * 0.1;
var xScale = gW / Math.max(visibleNn - 1, 1);
var idx = Math.round(visibleStart + (mx_ - pad.l) / xScale);
idx = Math.max(0, Math.min(nn - 1, idx));
var px = pad.l + (idx - visibleStart) * xScale;
var py = pad.t + gH - (ap[idx] - dMin) / (dMax - dMin) * gH;
oc.strokeStyle = "rgba(255,255,255,0.18)";
oc.lineWidth = 1;
oc.setLineDash([4, 3]);
oc.beginPath(); oc.moveTo(px, pad.t); oc.lineTo(px, pad.t + gH); oc.stroke();
oc.beginPath(); oc.moveTo(pad.l, py); oc.lineTo(W - pad.r, py); oc.stroke();
oc.setLineDash([]);
oc.beginPath(); oc.arc(px, py, 6, 0, Math.PI * 2);
oc.fillStyle = "rgba(100,181,246,0.3)"; oc.fill();
oc.beginPath(); oc.arc(px, py, 4, 0, Math.PI * 2);
oc.fillStyle = "#64b5f6"; oc.fill();
oc.strokeStyle = "#fff"; oc.lineWidth = 2; oc.stroke();
oc.setTransform(1, 0, 0, 1, 0, 0);
var diff = idx > 0 ? (ap[idx] - ap[idx - 1]) : 0;
var isUp = diff >= 0;
var dc = isUp ? "#ef5350" : "#26a69a";
var ds = (isUp ? "+" : "") + diff;
var tooltipRows = [
{ style: { color: "#888", marginBottom: "4px", fontWeight: "600" }, parts: [{ type: 'text', value: labels[idx] }] },
{ parts: [{ type: 'text', value: "体力: " }, { type: 'bold', value: String(ap[idx]), style: { color: "#64b5f6" } }] },
{ parts: [{ type: 'text', value: "单次变化: " }, { type: 'bold', value: ds, style: { color: dc } }] }
];
if (isDetailMode) {
var visibleStart = Math.max(0, Math.floor(panOffset));
var visibleCount = Math.ceil(nn / zoomLevel);
var visibleEnd = Math.min(nn, visibleStart + visibleCount);
var visibleNn = visibleEnd - visibleStart;
var dMin = Infinity, dMax = -Infinity;
for (var i = visibleStart; i < visibleEnd; i++) {
if (ap[i] < dMin) dMin = ap[i];
if (ap[i] > dMax) dMax = ap[i];
}
if (dMin === Infinity) dMin = 0;
if (dMax === -Infinity) dMax = 100;
var drng = dMax - dMin || 1;
dMin -= drng * 0.1;
dMax += drng * 0.1;
var xScale = gW / visibleNn;
var idx = Math.floor(panOffset + (mx_ - pad.l) / xScale);
idx = Math.max(0, Math.min(nn - 1, idx));
var px = pad.l + (idx - visibleStart) * xScale;
var py = pad.t + gH - (ap[idx] - dMin) / (dMax - dMin) * gH;
oc.strokeStyle = "rgba(255,255,255,0.18)";
oc.lineWidth = 1;
oc.setLineDash([4, 3]);
oc.beginPath(); oc.moveTo(px, pad.t); oc.lineTo(px, pad.t + gH); oc.stroke();
oc.beginPath(); oc.moveTo(pad.l, py); oc.lineTo(W - pad.r, py); oc.stroke();
oc.setLineDash([]);
oc.beginPath(); oc.arc(px, py, 6, 0, Math.PI * 2);
oc.fillStyle = "rgba(100,181,246,0.3)"; oc.fill();
oc.beginPath(); oc.arc(px, py, 4, 0, Math.PI * 2);
oc.fillStyle = "#64b5f6"; oc.fill();
oc.strokeStyle = "#fff"; oc.lineWidth = 2; oc.stroke();
oc.setTransform(1, 0, 0, 1, 0, 0);
var diff = idx > 0 ? (ap[idx] - ap[idx - 1]) : 0;
var isUp = diff >= 0;
var dc = isUp ? "#ef5350" : "#26a69a";
var ds = (isUp ? "+" : "") + diff;
var source = sources && sources[idx] ? sources[idx] : '-';
var sourceColor = source === 'cl1' ? '#64b5f6' : (source === 'meow' ? '#ff9800' : '#888');
var tooltipRows = [
{ style: { color: "#888", marginBottom: "4px", fontWeight: "600" }, parts: [{ type: 'text', value: labels[idx] }] },
{ parts: [{ type: 'text', value: "体力: " }, { type: 'bold', value: String(ap[idx]), style: { color: "#64b5f6" } }] },
{ parts: [{ type: 'text', value: "单次变化: " }, { type: 'bold', value: ds, style: { color: dc } }] },
{ parts: [{ type: 'text', value: "来源: " }, { type: 'bold', value: source, style: { color: sourceColor } }] }
];
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen && yellowCoins[idx] !== null && yellowCoins[idx] !== undefined) {
var yc = yellowCoins[idx];
var ycDiff = idx > 0 && yellowCoins[idx - 1] !== null && yellowCoins[idx - 1] !== undefined ? (yc - yellowCoins[idx - 1]) : 0;
var ycColor = ycDiff >= 0 ? "#ef5350" : "#26a69a";
var ycDiffStr = (ycDiff >= 0 ? "+" : "") + ycDiff;
tooltipRows.push({ parts: [{ type: 'text', value: "黄币: " }, { type: 'bold', value: String(yc), style: { color: "#ffd54f" } }, { type: 'text', value: " (" + ycDiffStr + ")", style: { color: ycColor } }] });
}
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen && purpleCoins[idx] !== null && purpleCoins[idx] !== undefined) {
var pc = purpleCoins[idx];
var pcDiff = idx > 0 && purpleCoins[idx - 1] !== null && purpleCoins[idx - 1] !== undefined ? (pc - purpleCoins[idx - 1]) : 0;
var pcColor = pcDiff >= 0 ? "#ef5350" : "#26a69a";
var pcDiffStr = (pcDiff >= 0 ? "+" : "") + pcDiff;
tooltipRows.push({ parts: [{ type: 'text', value: "紫币: " }, { type: 'bold', value: String(pc), style: { color: "#ce93d8" } }, { type: 'text', value: " (" + pcDiffStr + ")", style: { color: pcColor } }] });
}
setTooltipContent(tipEl, tooltipRows);
} else {
var ratio = (mx_ - pad.l) / gW;
var idx = Math.round(ratio * (nn - 1));
idx = Math.max(0, Math.min(nn - 1, idx));
var px = xOfLine(idx), py = yOf(ap[idx]);
oc.strokeStyle = "rgba(255,255,255,0.18)";
oc.lineWidth = 1;
oc.setLineDash([4, 3]);
oc.beginPath(); oc.moveTo(px, pad.t); oc.lineTo(px, pad.t + gH); oc.stroke();
oc.beginPath(); oc.moveTo(pad.l, py); oc.lineTo(W - pad.r, py); oc.stroke();
oc.setLineDash([]);
oc.beginPath(); oc.arc(px, py, 6, 0, Math.PI * 2);
oc.fillStyle = "rgba(100,181,246,0.3)"; oc.fill();
oc.beginPath(); oc.arc(px, py, 4, 0, Math.PI * 2);
oc.fillStyle = "#64b5f6"; oc.fill();
oc.strokeStyle = "#fff"; oc.lineWidth = 2; oc.stroke();
oc.setTransform(1, 0, 0, 1, 0, 0);
var diff = idx > 0 ? (ap[idx] - ap[idx - 1]) : 0;
var isUp = diff >= 0;
var dc = isUp ? "#ef5350" : "#26a69a";
var ds = (isUp ? "+" : "") + diff;
var tooltipRows = [
{ style: { color: "#888", marginBottom: "4px", fontWeight: "600" }, parts: [{ type: 'text', value: labels[idx] }] },
{ parts: [{ type: 'text', value: "体力: " }, { type: 'bold', value: String(ap[idx]), style: { color: "#64b5f6" } }] },
{ parts: [{ type: 'text', value: "单次变化: " }, { type: 'bold', value: ds, style: { color: dc } }] }
];
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen && yellowCoins[idx] !== null && yellowCoins[idx] !== undefined) {
var yc = yellowCoins[idx];
var ycDiff = idx > 0 && yellowCoins[idx - 1] !== null && yellowCoins[idx - 1] !== undefined ? (yc - yellowCoins[idx - 1]) : 0;
var ycColor = ycDiff >= 0 ? "#ef5350" : "#26a69a";
var ycDiffStr = (ycDiff >= 0 ? "+" : "") + ycDiff;
tooltipRows.push({ parts: [{ type: 'text', value: "黄币: " }, { type: 'bold', value: String(yc), style: { color: "#ffd54f" } }, { type: 'text', value: " (" + ycDiffStr + ")", style: { color: ycColor } }] });
}
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen && purpleCoins[idx] !== null && purpleCoins[idx] !== undefined) {
var pc = purpleCoins[idx];
var pcDiff = idx > 0 && purpleCoins[idx - 1] !== null && purpleCoins[idx - 1] !== undefined ? (pc - purpleCoins[idx - 1]) : 0;
var pcColor = pcDiff >= 0 ? "#ef5350" : "#26a69a";
var pcDiffStr = (pcDiff >= 0 ? "+" : "") + pcDiff;
tooltipRows.push({ parts: [{ type: 'text', value: "紫币: " }, { type: 'bold', value: String(pc), style: { color: "#ce93d8" } }, { type: 'text', value: " (" + pcDiffStr + ")", style: { color: pcColor } }] });
}
setTooltipContent(tipEl, tooltipRows);
tooltipRows.push({ parts: [{ type: 'text', value: "来源: " }, { type: 'bold', value: source, style: { color: sourceColor } }] });
}
if (showCoins && yellowCoinsLen > 0 && idx < yellowCoinsLen && yellowCoins[idx] !== null && yellowCoins[idx] !== undefined) {
var yc = yellowCoins[idx];
var ycDiff = idx > 0 && yellowCoins[idx - 1] !== null && yellowCoins[idx - 1] !== undefined ? (yc - yellowCoins[idx - 1]) : 0;
var ycColor = ycDiff >= 0 ? "#ef5350" : "#26a69a";
var ycDiffStr = (ycDiff >= 0 ? "+" : "") + ycDiff;
tooltipRows.push({ parts: [{ type: 'text', value: "黄币: " }, { type: 'bold', value: String(yc), style: { color: "#ffd54f" } }, { type: 'text', value: " (" + ycDiffStr + ")", style: { color: ycColor } }] });
}
if (showCoins && purpleCoinsLen > 0 && idx < purpleCoinsLen && purpleCoins[idx] !== null && purpleCoins[idx] !== undefined) {
var pc = purpleCoins[idx];
var pcDiff = idx > 0 && purpleCoins[idx - 1] !== null && purpleCoins[idx - 1] !== undefined ? (pc - purpleCoins[idx - 1]) : 0;
var pcColor = pcDiff >= 0 ? "#ef5350" : "#26a69a";
var pcDiffStr = (pcDiff >= 0 ? "+" : "") + pcDiff;
tooltipRows.push({ parts: [{ type: 'text', value: "紫币: " }, { type: 'bold', value: String(pc), style: { color: "#ce93d8" } }, { type: 'text', value: " (" + pcDiffStr + ")", style: { color: pcColor } }] });
}
setTooltipContent(tipEl, tooltipRows);
} else {
var idx = Math.floor((mx_ - pad.l) / candleSpace);
idx = Math.max(0, Math.min(nn - 1, idx));
@ -529,7 +481,7 @@
oc.clearRect(0, 0, ovCv.width, ovCv.height);
});
if (isDetailMode) {
if (chartType === 'line') {
var zoomLevel = 1.0;
var panOffset = 0;
var maxZoom = 5.0;
@ -583,7 +535,7 @@
ctx.textAlign = "center";
ctx.textBaseline = "top";
var xScale = gW / visibleNn;
var xScale = gW / Math.max(visibleNn - 1, 1);
function dxOf(i) { return pad.l + (i - visibleStart) * xScale; }
function dyOf(v) { return pad.t + gH - (v - dMin) / (dMax - dMin) * gH; }
@ -650,7 +602,7 @@
if (!isDragging) return;
var dx = e.clientX - dragStartX;
var visibleCount = Math.ceil(nn / zoomLevel);
var xScale = gW / visibleCount;
var xScale = gW / Math.max(visibleCount - 1, 1);
var newPan = dragStartPan - dx / xScale;
var maxPan = Math.max(0, nn - visibleCount);
panOffset = Math.max(0, Math.min(maxPan, newPan));
@ -673,10 +625,10 @@
if (newZoom !== zoomLevel) {
var visibleCountBefore = Math.ceil(nn / zoomLevel);
var visibleCountAfter = Math.ceil(nn / newZoom);
var xScaleBefore = gW / visibleCountBefore;
var xScaleBefore = gW / Math.max(visibleCountBefore - 1, 1);
var mouseIdx = panOffset + (mx - pad.l) / xScaleBefore;
zoomLevel = newZoom;
var xScaleAfter = gW / visibleCountAfter;
var xScaleAfter = gW / Math.max(visibleCountAfter - 1, 1);
panOffset = Math.max(0, mouseIdx - (mx - pad.l) / xScaleAfter);
var maxPan = Math.max(0, nn - visibleCountAfter);
panOffset = Math.max(0, Math.min(maxPan, panOffset));