diff --git a/pyproject.toml b/pyproject.toml index 93e2c3f9..9339f9fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,11 +74,9 @@ zsim = "zsim.run:main" [dependency-groups] dev = [ "ipykernel>=6.29.5", - "openpyxl>=3.1.5", "pyinstaller>=6.13.0", "pytest>=8.3.5", "viztracer>=1.0.3", - "cx-freeze>=6.15.0", "objprint>=0.3.0", "pytest-cov>=5.0.0", "pytest-asyncio>=1.1.0", diff --git a/tests/api/test_apl_database.py b/tests/api/test_apl_database.py index 7fa5ec46..67fc6aab 100644 --- a/tests/api/test_apl_database.py +++ b/tests/api/test_apl_database.py @@ -4,7 +4,6 @@ """ import os -import json import pytest import tempfile import shutil diff --git a/uv.lock b/uv.lock index 4431f566..e1b1a305 100644 --- a/uv.lock +++ b/uv.lock @@ -125,15 +125,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7b/b1/a6d4ef30ebfa0f62aedd437d81130075afc32266f7f6baf2cddf4b41ac2a/bottle-0.13.3-py2.py3-none-any.whl", hash = "sha256:551709073d750d2e88ade05915f0d5dbbbcb8e1d82573fbe85a0635eb842ab07", size = 104151, upload-time = "2025-04-21T11:48:34.257Z" }, ] -[[package]] -name = "cabarchive" -version = "0.2.4" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/28/d3/a544aed878edc269ce4427bc937310b73624e1d595de7f4e5bcab413a639/cabarchive-0.2.4.tar.gz", hash = "sha256:04f60089473114cf26eab2b7e1d09611c5bfaf8edd3202dacef66bb5c71e48cf", size = 21064, upload-time = "2022-02-23T09:28:10.911Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0f/fb/713421f46c68f4bf9cd26f05bda0c233446108997b6b4d83d7ef07f20009/cabarchive-0.2.4-py3-none-any.whl", hash = "sha256:4afabd224eb2e40af8e907379fb8eec6b0adfb71c2aef4457ec3a4d77383c059", size = 25729, upload-time = "2022-02-23T09:28:09.571Z" }, -] - [[package]] name = "cachetools" version = "5.5.2" @@ -307,55 +298,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" }, ] -[[package]] -name = "cx-freeze" -version = "8.2.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -dependencies = [ - { name = "cabarchive", marker = "sys_platform == 'win32'" }, - { name = "cx-logging", marker = "sys_platform == 'win32'" }, - { name = "dmgbuild", marker = "sys_platform == 'darwin'" }, - { name = "filelock" }, - { name = "lief", marker = "sys_platform == 'win32'" }, - { name = "packaging" }, - { name = "patchelf", marker = "(platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'armv7l' and sys_platform == 'linux') or (platform_machine == 'i686' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and sys_platform == 'linux') or (platform_machine == 's390x' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, - { name = "setuptools" }, - { name = "striprtf", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/18/90/9abcc037fc514ce1c1c43f0725137e878fe61fd87a98eb7ee9a0cd590b99/cx_freeze-8.2.0.tar.gz", hash = "sha256:bfaac976daf96cff94463e992006305afb520af1c9efabd6dfd6c746449f1c96", size = 3177536, upload-time = "2025-04-22T23:36:26.666Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0e/72/b2c97d85e0e58557a8eba36897448e396caa2208db9b329b9bc1e66cb6e4/cx_freeze-8.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f5ac85f06c0fed4b17aac948069db868cf423d9d3cec49d7572d0400d80c2121", size = 21974093, upload-time = "2025-04-22T23:35:28.574Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/fb/88737b6a9bd042dafda0a51121adcb0b877d86841378137f18cfcd7265cf/cx_freeze-8.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076ea51c9bf8151193b041a22646ce9dca08a13d1d9536e284da97769ca0c43d", size = 14418404, upload-time = "2025-04-22T23:35:31.332Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/06/d5/84923e405dc647de6a6d8fd573e71c023d308999e82783d7a8ac636c83b4/cx_freeze-8.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:397fc1dd812e8e844dabb8336ec15287c63167eddd61d5164422e4c4111cceb9", size = 15923717, upload-time = "2025-04-22T23:35:34.093Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/19/83/fb844c81482aa0c9ccea0a1851655908fbaa1dc05e85d4c25aecc8edbde1/cx_freeze-8.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d1ad7241eda9e22bf595344c4cf6b2b24d3203aa6aed201554a5ddf587df28", size = 14558849, upload-time = "2025-04-22T23:35:36.551Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b2/d8/e23edcfeddc73cf0decf0017a7acef0f9aaf00f3031a7216d5dc5b6287da/cx_freeze-8.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6853b3da774c9efeb1d60bfa061e1c6a614f43f7a03e857a0093f18c25c67527", size = 15582433, upload-time = "2025-04-22T23:35:38.834Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/f9/20319d17c687b33fd698b083e8f902b4cf39378a1d034982a20f38c4e793/cx_freeze-8.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5f8dd2ae032bb6293b8dab6658a73abf3f6ab9845ae5bdfaf39f105c31b72fdc", size = 15363056, upload-time = "2025-04-22T23:35:41.337Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9e/06/9844bc97553e12808bf3b7db50910760738d514a275abdb6e0772949de73/cx_freeze-8.2.0-cp312-cp312-win32.whl", hash = "sha256:402f5cc62d72e2d01732d1c8abe7c9441c4fd259be6ab52d3d86678cfd37073e", size = 2324419, upload-time = "2025-04-22T23:35:43.858Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/00/4f/9058938b8d191af20f1a6a77f8ce625351d24d0d212272167774fc04337f/cx_freeze-8.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:35815ce2628ffebbcec163823473eebcd841b3ab3904e860b64e85c46765fad7", size = 2329266, upload-time = "2025-04-22T23:35:45.686Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a3/55/a3798307256871fd050389717c9641b6bdbfb3384796fc40839dda6d7dc9/cx_freeze-8.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0763ee2fa81d2f3a280f365a29405061189ed9067317df31aa241cc512a599dc", size = 21395061, upload-time = "2025-04-22T23:35:47.385Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/f0/b75f1e95c700575e7704438665750d6c171aeae4baabcdc16ac4ffa2adff/cx_freeze-8.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:515ef3a74df3efdfebf3ee5730cf942fab48717c1219caf1f0d64dbaf0281346", size = 12425004, upload-time = "2025-04-22T23:35:52.945Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/63/6f/4bb4a9e1d94623cfd84c361f1a1a71d7557c418c3d0e717a4bd8808ebdc3/cx_freeze-8.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce2e246e4efc349220748b609e5884d1188eb3fe953199b38e509a70326f9ea5", size = 12540513, upload-time = "2025-04-22T23:35:55.523Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2c/a3/2d846e1516dc5ffd26b78f5e5cc8f583468a884b5172c65ba6dd8b73ac5a/cx_freeze-8.2.0-cp313-cp313-win32.whl", hash = "sha256:33fe02416942f1b85555f0fef6517bfd9b4354692d804ddea79b420819c806d0", size = 2320887, upload-time = "2025-04-22T23:35:58.26Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/12/09/a3046f07aa9415fc9b23a56e4b03af4f1ec309054d09003d20520069d24d/cx_freeze-8.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:b06a2d404d28ce90c33b0a2fd4651bedc072a2b835dc7f1ff1337c722b7296bc", size = 2325237, upload-time = "2025-04-22T23:35:59.905Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/40/11/5ad70b6f1cea0c22b4e64406b56d6384a34889a6a8e0c5fad7526c810f7e/cx_freeze-8.2.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c1f1ab26ad549709cdc56db6f733395894a1d3c28944f6256b4806f10c74a908", size = 21036359, upload-time = "2025-04-22T23:36:02.378Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/30/63/237917ae00e33a8a31052a3c8218860eda6570abb5ceaf7012ce04c2767b/cx_freeze-8.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eb3d69a4b91bd136c245399015442e92d4f94ebb3ad5021da8022cfd393c58d", size = 12640766, upload-time = "2025-04-22T23:36:05.073Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/23/0c/b8fd2a50265ff40acd479ef630955e3ac169e2ead0a74a1a6d781962b23d/cx_freeze-8.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b500df5eb4fef8aeb19e61237daa67069f4e7b208f2bc21b130a77db8e3b494", size = 12719402, upload-time = "2025-04-22T23:36:07.591Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/bb/afb8df0b49cc039cb6ce75193f3bceb401ec4dc9a5549941ddcb6e28b791/cx_freeze-8.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a9f080eb264eff17fc99f6f8ecf4c472bd3cb375adf11337a89ee190faace4f5", size = 13623748, upload-time = "2025-04-22T23:36:10.229Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/4b/2e25ff083c09635164ac82cd3a1e62cc32035ac77f90df16b3b716062b23/cx_freeze-8.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de1ffc5c05bff95b69bc6158e995cb32cabdbd4726b0f47cfeba1b532c04d5af", size = 13302507, upload-time = "2025-04-22T23:36:12.99Z" }, -] - -[[package]] -name = "cx-logging" -version = "3.2.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/9d/69/50b0c38e26658072b0221f1ea243c47dd56a9f3f50e5754aa5a39189145c/cx_logging-3.2.1.tar.gz", hash = "sha256:812665ae5012680a6fe47095c3772bce638e47cf05b2c3483db3bdbe6b06da44", size = 26966, upload-time = "2024-10-13T03:13:10.561Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/9b/d8babcfafa7233b862b310a6fe630fc5e6ced02453ca4e60b0c819afbaff/cx_Logging-3.2.1-cp312-cp312-win32.whl", hash = "sha256:3f3de06cf09d5986b39e930c213567c340b3237dfce03d8d3bf6099475eaa02e", size = 22869, upload-time = "2024-10-13T03:13:28.258Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/52/b6bd4f4d51eb4f3523da182cdf5969a560e35f4ef178f34841ba6795addc/cx_Logging-3.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:3452add0544db6ff29116b72a4c48761aaffa9b638728330433853c0c4ad2ea1", size = 26911, upload-time = "2024-10-13T03:13:29.521Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/78/0ce28b89aedf369b02bb5cb763324e799844144386fba75c03128ea9e2ff/cx_Logging-3.2.1-cp313-cp313-win32.whl", hash = "sha256:330a29030bdca8795c99b678b4f6d87a75fb606eed1da206fdd9fa579a33dc21", size = 22874, upload-time = "2024-10-13T03:13:32.99Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/cb/23/dab5f561888951ec02843f087f34a59c791e8ac6423c25a412eb49300633/cx_Logging-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:e14748b031522a95aa2db4adfc5f2be5f96f4d0fe687da591114f73a09e66926", size = 26916, upload-time = "2024-10-13T03:13:34.085Z" }, -] - [[package]] name = "dash" version = "2.18.2" @@ -432,40 +374,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] -[[package]] -name = "dmgbuild" -version = "1.6.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -dependencies = [ - { name = "ds-store", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" }, - { name = "mac-alias", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" }, -] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/16/93/b9702c68d5dedfd6b91c76268a89091ff681b8e3b9a026e7919b6ab730a4/dmgbuild-1.6.5.tar.gz", hash = "sha256:c5cbeec574bad84a324348aa7c36d4aada04568c99fb104dec18d22ba3259f45", size = 36848, upload-time = "2025-03-21T01:04:10.093Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/48/4a/b16f1081f69592c6dba92baa4d3ca7a5685091a0f840f4b5e01be41aaf84/dmgbuild-1.6.5-py3-none-any.whl", hash = "sha256:e19ab8c5e8238e6455d9ccb9175817be7fd62b9cdd1eef20f63dd88e0ec469ab", size = 34906, upload-time = "2025-03-21T01:04:08.044Z" }, -] - -[[package]] -name = "ds-store" -version = "1.3.1" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -dependencies = [ - { name = "mac-alias", marker = "(platform_machine != 'aarch64' and platform_machine != 'armv7l' and platform_machine != 'i686' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_machine != 'x86_64') or sys_platform != 'linux'" }, -] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7c/36/902259bf7ddb142dd91cf7a9794aa15e1a8ab985974f90375e5d3463b441/ds_store-1.3.1.tar.gz", hash = "sha256:c27d413caf13c19acb85d75da4752673f1f38267f9eb6ba81b3b5aa99c2d207c", size = 27052, upload-time = "2022-11-24T06:13:34.376Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/47/bf/b1c10362a0d670ee8ae086d92c3ab795fca2a927e4ff25e7cd15224d3863/ds_store-1.3.1-py3-none-any.whl", hash = "sha256:fbacbb0bd5193ab3e66e5a47fff63619f15e374ffbec8ae29744251a6c8f05b5", size = 16268, upload-time = "2022-11-24T06:13:30.797Z" }, -] - -[[package]] -name = "et-xmlfile" -version = "2.0.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d3/38/af70d7ab1ae9d4da450eeec1fa3918940a5fafb9055e934af8d6eb0c2313/et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54", size = 17234, upload-time = "2024-10-25T17:25:40.039Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa", size = 18059, upload-time = "2024-10-25T17:25:39.051Z" }, -] - [[package]] name = "executing" version = "2.2.0" @@ -489,15 +397,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164, upload-time = "2025-03-23T22:55:42.101Z" }, ] -[[package]] -name = "filelock" -version = "3.18.0" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0a/10/c23352565a6544bdc5353e0b15fc1c563352101f30e24bf500207a54df9a/filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", size = 18075, upload-time = "2025-03-14T07:11:40.47Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, -] - [[package]] name = "flask" version = "3.0.3" @@ -752,28 +651,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965, upload-time = "2024-03-12T12:37:32.36Z" }, ] -[[package]] -name = "lief" -version = "0.16.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/68/c7df68afe1c37be667f1adb74544b06316fd1338dd577fd0c1289817d2d1/lief-0.16.5-cp312-cp312-win32.whl", hash = "sha256:768f91db886432c4b257fb88365a2c6842f26190b73964cf9274c276bc17b490", size = 3049882, upload-time = "2025-04-19T16:51:53.584Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/8b/0fdc6b420e24df7c8cc02be595c425e821f2d4eb1be98eb16a7cf4e87fd0/lief-0.16.5-cp312-cp312-win_amd64.whl", hash = "sha256:587225fd6e1ec424a1a776928beb67095894254c51148b78903844d62faa1a2d", size = 3178830, upload-time = "2025-04-19T16:51:55.254Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e5/a6/f751d12b88527b591f26a7c4a2b896806c065d9bdfb49eaabec9e6aead41/lief-0.16.5-cp312-cp312-win_arm64.whl", hash = "sha256:ef043c1796d221f128597dc32819fa6bb31da26d2a9b911a32d4a5cdfb566f85", size = 3066592, upload-time = "2025-04-19T16:51:57.86Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/97/72fe8e8bfbfea9d76350635965f668e855490c6f2779c08bf1b9ab3a505d/lief-0.16.5-cp313-cp313-win32.whl", hash = "sha256:6fc879c1c90bf31f7720ece90bd919cbfeeb3bdbc9327f6a16d4dc1af273aef9", size = 3049849, upload-time = "2025-04-19T16:52:11.327Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/66/fc/6faf93a5b44f9e7df193e9fc95b93a7f34b2155b1b470ef61f2f25704a84/lief-0.16.5-cp313-cp313-win_amd64.whl", hash = "sha256:2f208359d10ade57ace7f7625e2f5e4ca214b4b67f9ade24ca07dafb08e37b0c", size = 3178645, upload-time = "2025-04-19T16:52:13.112Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6d/47/d0a47b6856d832a2ab0896faa773b4506b41e39131684892017351e8ff28/lief-0.16.5-cp313-cp313-win_arm64.whl", hash = "sha256:afb7d946aa2b62c95831d3be45f2516324418335b077f5337012b779e8dcc97b", size = 3066502, upload-time = "2025-04-19T16:52:14.787Z" }, -] - -[[package]] -name = "mac-alias" -version = "2.2.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ea/a3/83b50f620d318a98363dc7e701fb94856eaaecc472e23a89ac625697b3ea/mac_alias-2.2.2.tar.gz", hash = "sha256:c99c728eb512e955c11f1a6203a0ffa8883b26549e8afe68804031aa5da856b7", size = 34073, upload-time = "2022-12-06T00:37:47.779Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/39/a1/4136777ed6a56df83e7c748ad28892f0672cbbcdc3b3d15a57df6ba72443/mac_alias-2.2.2-py3-none-any.whl", hash = "sha256:504ab8ac546f35bbd75ad014d6ad977c426660aa721f2cd3acf3dc2f664141bd", size = 21220, upload-time = "2022-12-06T00:37:46.025Z" }, -] - [[package]] name = "macholib" version = "1.16.3" @@ -901,18 +778,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ec/af/572825252f16f36eeecbc8e3b721913d2640d69b984fdb8907aa8b4b0975/objprint-0.3.0-py3-none-any.whl", hash = "sha256:489083bfc8baf0526f8fd6af74673799511532636f0ce4141133255ded773405", size = 41619, upload-time = "2024-11-09T00:05:14.852Z" }, ] -[[package]] -name = "openpyxl" -version = "3.1.5" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -dependencies = [ - { name = "et-xmlfile" }, -] -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/3d/f9/88d94a75de065ea32619465d2f77b29a0469500e99012523b91cc4141cd1/openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050", size = 186464, upload-time = "2024-06-28T14:03:44.161Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2", size = 250910, upload-time = "2024-06-28T14:03:41.161Z" }, -] - [[package]] name = "packaging" version = "24.2" @@ -965,20 +830,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" }, ] -[[package]] -name = "patchelf" -version = "0.17.2.2" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/0d/41/dc3ee5838db2d90be935adb53ae7745135d9c719d070b1989b246f983c7f/patchelf-0.17.2.2.tar.gz", hash = "sha256:080b2ac3074fd4ab257700088e82470425e56609aa0dd07abe548f04b7b3b007", size = 149517, upload-time = "2025-03-16T08:30:21.909Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/15/25b5d10d971f509fe6bc8951b855f0f05be4c24e0dd1616c14a6e1a9116a/patchelf-0.17.2.2-py3-none-manylinux1_i686.manylinux_2_5_i686.musllinux_1_1_i686.whl", hash = "sha256:3b8a4d7cccac04d8231dec321245611bf147b199cbf4da305d1a364ff689fb58", size = 524182, upload-time = "2025-03-16T08:30:11.93Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f2/f9/e070956e350ccdfdf059251836f757ad91ac0c01b0ba3e033ea7188d8d42/patchelf-0.17.2.2-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:e334ebb1c5aa9fc740fd95ebe449271899fe1e45a3eb0941300b304f7e3d1299", size = 466519, upload-time = "2025-03-16T08:30:13.383Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/56/0d/dc3ac6c6e9e9d0d3e40bee1abe95a07034f83627319e60a7dc9abdbfafee/patchelf-0.17.2.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:3d32cd69442a229724f7f071b61cef1f87ccd80cf755af0b1ecefd553fa9ae3f", size = 462123, upload-time = "2025-03-16T08:30:15.57Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/f6/b842b19c2b72df1c524ab3793c3ec9cf3926c7c841e0b64b34f95d7fb806/patchelf-0.17.2.2-py3-none-manylinux2014_armv7l.manylinux_2_17_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:05f6bbdbe484439cb025e20c60abd37e432e6798dfa3f39a072e6b7499072a8c", size = 412347, upload-time = "2025-03-16T08:30:17.332Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a8/0b/33eb3087703d903dd01cf6b0d64e067bf3718a5e8b1239bc6fc2c4b1fdb2/patchelf-0.17.2.2-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:b54e79ceb444ec6a536a5dc2e8fc9c771ec6a1fa7d5f4dbb3dc0e5b8e5ff82e1", size = 522827, upload-time = "2025-03-16T08:30:18.774Z" }, - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4f/25/6379dc26714b5a40f51b3c7927d668b00a51517e857da7f3cb09d1d0bcb6/patchelf-0.17.2.2-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.musllinux_1_1_s390x.whl", hash = "sha256:24374cdbd9a072230339024fb6922577cb3231396640610b069f678bc483f21e", size = 565961, upload-time = "2025-03-16T08:30:20.524Z" }, -] - [[package]] name = "pefile" version = "2023.2.7" @@ -1739,15 +1590,6 @@ wheels = [ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8d/c9/e862cefc5735e11c101b2bc45f37a2b767666ffe673d262004202b506f3b/streamlit_ace-0.1.1-py3-none-any.whl", hash = "sha256:cdf908a90058fa831fb720d29e2aef35a0f5799eac33e2b2e58ba9f3631b1aa5", size = 3642928, upload-time = "2021-12-07T19:01:11.288Z" }, ] -[[package]] -name = "striprtf" -version = "0.0.29" -source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" } -sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f3/86/7154b7c625a3ff704581dab70c05389e1de90233b7a751f79f712c2ca0e9/striprtf-0.0.29.tar.gz", hash = "sha256:5a822d075e17417934ed3add6fc79b5fc8fb544fe4370b2f894cdd28f0ddd78e", size = 7533, upload-time = "2025-03-27T22:55:56.874Z" } -wheels = [ - { url = "https://pypi.tuna.tsinghua.edu.cn/packages/08/3e/1418afacc4aae04690cff282078f22620c89a99490499878ececc3021654/striprtf-0.0.29-py3-none-any.whl", hash = "sha256:0fc6a41999d015358d19627776b616424dd501ad698105c81d76734d1e14d91b", size = 7879, upload-time = "2025-03-27T22:55:55.977Z" }, -] - [[package]] name = "tenacity" version = "9.1.2" @@ -1961,10 +1803,8 @@ dependencies = [ [package.dev-dependencies] dev = [ - { name = "cx-freeze" }, { name = "ipykernel" }, { name = "objprint" }, - { name = "openpyxl" }, { name = "pyinstaller" }, { name = "pytest" }, { name = "pytest-asyncio" }, @@ -1996,10 +1836,8 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ - { name = "cx-freeze", specifier = ">=6.15.0" }, { name = "ipykernel", specifier = ">=6.29.5" }, { name = "objprint", specifier = ">=0.3.0" }, - { name = "openpyxl", specifier = ">=3.1.5" }, { name = "pyinstaller", specifier = ">=6.13.0" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest-asyncio", specifier = ">=1.1.0" }, diff --git a/zsim/api_src/models/apl.py b/zsim/api_src/models/apl.py index 9d0d60e1..dedb02ba 100644 --- a/zsim/api_src/models/apl.py +++ b/zsim/api_src/models/apl.py @@ -3,7 +3,7 @@ 定义APL API请求和响应的数据模型 """ -from typing import List, Dict, Any, Optional, TypeVar, Generic +from typing import List, Optional, TypeVar, Generic from pydantic import BaseModel, Field diff --git a/zsim/api_src/services/database/apl_db.py b/zsim/api_src/services/database/apl_db.py index 816ad392..7422e99f 100644 --- a/zsim/api_src/services/database/apl_db.py +++ b/zsim/api_src/services/database/apl_db.py @@ -3,13 +3,15 @@ 负责APL相关数据的数据库操作 """ +import asyncio import os -import toml import uuid +from typing import Any, Dict, List, Optional + import aiosqlite -import asyncio -from typing import Dict, List, Any, Optional -from zsim.define import DEFAULT_APL_DIR, COSTOM_APL_DIR, SQLITE_PATH +import toml + +from zsim.define import COSTOM_APL_DIR, DEFAULT_APL_DIR, SQLITE_PATH class APLDatabase: @@ -240,11 +242,9 @@ def get_apl_file_content(self, file_id: str) -> Optional[Dict[str, Any]]: # 解析file_id获取source和相对路径 try: if file_id.startswith("default_"): - source = "default" rel_path = file_id[len("default_") :] base_dir = DEFAULT_APL_DIR elif file_id.startswith("custom_"): - source = "custom" rel_path = file_id[len("custom_") :] base_dir = COSTOM_APL_DIR else: @@ -299,7 +299,6 @@ def update_apl_file(self, file_id: str, content: str) -> bool: # 不允许更新默认文件 return False elif file_id.startswith("custom_"): - source = "custom" rel_path = file_id[len("custom_") :] base_dir = COSTOM_APL_DIR else: @@ -326,7 +325,6 @@ def delete_apl_file(self, file_id: str) -> bool: # 不允许删除默认文件 return False elif file_id.startswith("custom_"): - source = "custom" rel_path = file_id[len("custom_") :] base_dir = COSTOM_APL_DIR else: diff --git a/zsim/api_src/services/database/character_db.py b/zsim/api_src/services/database/character_db.py index c3d54a4c..d673f8cc 100644 --- a/zsim/api_src/services/database/character_db.py +++ b/zsim/api_src/services/database/character_db.py @@ -1,6 +1,5 @@ -import json import aiosqlite -from typing import Any, List, Optional +from typing import Any, List from zsim.define import SQLITE_PATH from zsim.models.character.character_config import CharacterConfig from datetime import datetime diff --git a/zsim/api_src/services/database/enemy_db.py b/zsim/api_src/services/database/enemy_db.py index 92d94363..8fd68fce 100644 --- a/zsim/api_src/services/database/enemy_db.py +++ b/zsim/api_src/services/database/enemy_db.py @@ -1,6 +1,6 @@ import json import aiosqlite -from typing import Any, List, Optional +from typing import Any, List from zsim.define import SQLITE_PATH from zsim.models.enemy.enemy_config import EnemyConfig from datetime import datetime diff --git a/zsim/define.py b/zsim/define.py index 4caf14a9..7a6ac75d 100644 --- a/zsim/define.py +++ b/zsim/define.py @@ -96,7 +96,7 @@ def update_json_config(template: dict, user: dict) -> bool: "Taction": 30, # 角色弹刀与闪避动作的持续时间,不开放给用户更改。 "Tbase": 273, # 人类反应时间大数据中位数,单位ms,不可更改! "PlayerLevel": _config["apl_mode"]["player_level"], # 玩家水平系数,由用户自己填写。 - "PerfectPlayer": _config["apl_mode"].get("perfect_player", True), # 是否是完美玩家(默认是) + "PerfectPlayer": _config["apl_mode"].get("perfect_player", True), # 是否是完美玩家(默认是) "theta": 90, # θ,人类胜利最小反应时间(神经传导极限),为90ms,不可更改! "c": 0.5, # 波动调节系数,暂取0.5,不开放给用户更改。 "delta": 30, # 玩家水平系数所导致的中位数波动单位,暂时取30ms,不开放给用户更改。 @@ -104,9 +104,7 @@ def update_json_config(template: dict, user: dict) -> bool: PARRY_BASE_PARAMETERS: dict[str, int | float] = { "ChainParryActionTimeCost": 10, # 连续招架动作的时间消耗 } -CHAR_PARRY_STRATEGY_MAP: dict = { - 1411: "1411_Assault_Aid_A" -} +CHAR_PARRY_STRATEGY_MAP: dict = {1411: "1411_Assault_Aid_A"} # debug参数,用于检查APL在窗口期间的想法 APL_THOUGHT_CHECK: bool = _config["apl_mode"].get("apl_thought_check", False) diff --git a/zsim/lib_webui/process_dmg_result.py b/zsim/lib_webui/process_dmg_result.py index 317ce9c0..5a2d9506 100644 --- a/zsim/lib_webui/process_dmg_result.py +++ b/zsim/lib_webui/process_dmg_result.py @@ -596,7 +596,9 @@ def prepare_dmg_data_and_cache( char_chart_data = prepare_char_chart_data(uuid_df) # st.write(char_chart_data) calculate_and_save_anomaly_attribution( - int(rid) if isinstance(rid, int) else rid, char_chart_data["char_dmg_df"], char_chart_data["char_element_df"] + int(rid) if isinstance(rid, int) else rid, + char_chart_data["char_dmg_df"], + char_chart_data["char_element_df"], ) return { "dmg_result_df": dmg_result_df, diff --git a/zsim/models/event_enums.py b/zsim/models/event_enums.py index 5688fa3d..3febfc19 100644 --- a/zsim/models/event_enums.py +++ b/zsim/models/event_enums.py @@ -25,10 +25,10 @@ class PostInitObjectType(Enum): class ListenerBroadcastSignal(Enum): """监听器广播函数所涉及到的更新信号""" - SWITCHING_IN = "switching_in_event" # 角色切入前场 - ENTER_BATTLE = "enter_battle_event" # 角色进入战斗 - ANOMALY = "anomaly_event" # 属性异常事件 - STUN = "stun_event" # 失衡事件 - PARRY = "parry_event" # 招架事件 - BLOCK = "block_event" # 格挡事件(其他具备格挡功能的技能响应进攻事件) + SWITCHING_IN = "switching_in_event" # 角色切入前场 + ENTER_BATTLE = "enter_battle_event" # 角色进入战斗 + ANOMALY = "anomaly_event" # 属性异常事件 + STUN = "stun_event" # 失衡事件 + PARRY = "parry_event" # 招架事件 + BLOCK = "block_event" # 格挡事件(其他具备格挡功能的技能响应进攻事件) diff --git a/zsim/page_character_config.py b/zsim/page_character_config.py index a60fe560..29bca226 100644 --- a/zsim/page_character_config.py +++ b/zsim/page_character_config.py @@ -10,7 +10,6 @@ def page_character_config(): if "name_box" in saved_char_config: default_chars = saved_char_config["name_box"] from zsim.lib_webui.constants import ( - char_options, equip_set2_options, equip_set4_options, weapon_options, @@ -97,20 +96,21 @@ def page_character_config(): ] else: filtered_weapon_options = list(weapon_options) - + # 根据稀有度筛选 filtered_weapon_options = [ - w for w in filtered_weapon_options - if (show_rarity_s and weapon_rarity_map.get(w) == "S") or - (show_rarity_a and weapon_rarity_map.get(w) == "A") or - (show_rarity_b and weapon_rarity_map.get(w) == "B") + w + for w in filtered_weapon_options + if (show_rarity_s and weapon_rarity_map.get(w) == "S") + or (show_rarity_a and weapon_rarity_map.get(w) == "A") + or (show_rarity_b and weapon_rarity_map.get(w) == "B") ] rarity_order = {"S": 0, "A": 1, "B": 2} filtered_weapon_options = sorted( filtered_weapon_options, - key=lambda w: (rarity_order.get(weapon_rarity_map.get(w), 3), w) + key=lambda w: (rarity_order.get(weapon_rarity_map.get(w), 3), w), ) - + if name in saved_char_config: current_weapon = saved_char_config[name].get("weapon") else: @@ -135,7 +135,7 @@ def page_character_config(): f"{' ' + weapon_char_map.get(x) if weapon_char_map.get(x) else ''}) {x}" ), ) - + col_rarity = st.columns(4) with col_rarity[0]: show_adapted_weapon = st.checkbox( diff --git a/zsim/sim_progress/Buff/Buff0Manager/Buff0ManagerClass.py b/zsim/sim_progress/Buff/Buff0Manager/Buff0ManagerClass.py index 3ad4df09..429a3d19 100644 --- a/zsim/sim_progress/Buff/Buff0Manager/Buff0ManagerClass.py +++ b/zsim/sim_progress/Buff/Buff0Manager/Buff0ManagerClass.py @@ -16,7 +16,6 @@ if TYPE_CHECKING: from zsim.simulator.simulator_class import Simulator - from zsim.sim_progress.Character.character import Character class Buff0Manager: @@ -27,7 +26,7 @@ def __init__( weapon_dict: dict[str, list], cinema_dict: dict, char_obj_dict: dict | None, - sim_instance: "Simulator", + sim_instance: "Simulator | None", ): # 加载文件 self.EXIST_FILE = pd.read_csv(EXIST_FILE_PATH, index_col="BuffName") diff --git a/zsim/sim_progress/Buff/BuffXLogic/MetanukiMorphosisAPBonus.py b/zsim/sim_progress/Buff/BuffXLogic/MetanukiMorphosisAPBonus.py index 15daa423..783dbb8b 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/MetanukiMorphosisAPBonus.py +++ b/zsim/sim_progress/Buff/BuffXLogic/MetanukiMorphosisAPBonus.py @@ -1,5 +1,6 @@ from .. import Buff, JudgeTools, check_preparation from typing import TYPE_CHECKING + if TYPE_CHECKING: from zsim.sim_progress.Preload import SkillNode @@ -20,9 +21,7 @@ def __init__(self, buff_instance): self.record: MetanukiMorphosisAPBonusRecord | None = None def get_prepared(self, **kwargs): - return check_preparation( - buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs - ) + return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs) def check_record_module(self): if self.equipper is None: diff --git a/zsim/sim_progress/Buff/BuffXLogic/MiyabiCoreSkill_IceFire.py b/zsim/sim_progress/Buff/BuffXLogic/MiyabiCoreSkill_IceFire.py index 48e031d7..a0321705 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/MiyabiCoreSkill_IceFire.py +++ b/zsim/sim_progress/Buff/BuffXLogic/MiyabiCoreSkill_IceFire.py @@ -4,6 +4,7 @@ from .. import Buff, JudgeTools, check_preparation from typing import TYPE_CHECKING + if TYPE_CHECKING: from zsim.sim_progress.Preload import SkillNode diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyBuildupBonus.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyBuildupBonus.py index 48fcde98..b0e6c3de 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyBuildupBonus.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyBuildupBonus.py @@ -1,5 +1,4 @@ from .. import Buff, JudgeTools, check_preparation -from ....define import YUZUHA_REPORT class YuzuhaAdditionalAbilityAnomalyBuildupBonusRecord: diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyDmgBonus.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyDmgBonus.py index ccc91764..b81e9296 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyDmgBonus.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaAdditionalAbilityAnomalyDmgBonus.py @@ -55,4 +55,6 @@ def special_hit_logic(self, **kwargs): self.buff_instance.update_to_buff_0(buff_0=self.buff_0) if YUZUHA_REPORT: self.buff_instance.sim_instance.schedule_data.change_process_state() - print(f"【柚叶组队被动】检测到【狸之愿】激活,当前柚叶的异常掌控为{am:.2f}点,共计提供{count * 0.2:.2f}%的异常积蓄效率以及属性异常/紊乱增伤") + print( + f"【柚叶组队被动】检测到【狸之愿】激活,当前柚叶的异常掌控为{am:.2f}点,共计提供{count * 0.2:.2f}%的异常积蓄效率以及属性异常/紊乱增伤" + ) diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinem1EleResReduce.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinem1EleResReduce.py index ae77f799..ccab0b0a 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinem1EleResReduce.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinem1EleResReduce.py @@ -1,5 +1,6 @@ from .. import Buff, JudgeTools, check_preparation from typing import TYPE_CHECKING + if TYPE_CHECKING: from ...Preload import SkillNode @@ -20,9 +21,7 @@ def __init__(self, buff_instance): self.xjudge = self.special_judge_logic def get_prepared(self, **kwargs): - return check_preparation( - buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs - ) + return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs) def check_record_module(self): if self.buff_0 is None: @@ -36,9 +35,7 @@ def check_record_module(self): def special_judge_logic(self, **kwargs): """只有两种强化E和大招的重攻击才能触发甜蜜惊吓效果""" self.check_record_module() - self.get_prepared( - char_CID=1411, enemy=1 - ) + self.get_prepared(char_CID=1411, enemy=1) skill_node: "SkillNode" = kwargs.get("skill_node") if skill_node is None: return False @@ -48,4 +45,3 @@ def special_judge_logic(self, **kwargs): return False else: return True - diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema2Trigger.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema2Trigger.py index 2772e5be..aa100504 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema2Trigger.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema2Trigger.py @@ -1,6 +1,7 @@ from .. import Buff, JudgeTools, check_preparation -from zsim.define import YUZUHA_REPORT +from zsim.define import YUZUHA_REPORT from typing import TYPE_CHECKING + if TYPE_CHECKING: from zsim.sim_progress.Preload import SkillNode @@ -25,9 +26,7 @@ def __init__(self, buff_instance): self.record: YuzuhaCinema2TriggerRecord | None = None def get_prepared(self, **kwargs): - return check_preparation( - buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs - ) + return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs) def check_record_module(self): if self.buff_0 is None: @@ -42,7 +41,7 @@ def special_judge_logic(self, **kwargs): self.check_record_module() self.get_prepared(char_CID=1411, enemy=1) if self.record.skill_node_be_changed is not None: - raise ValueError(f"【浮波柚叶2画触发器】存在尚未处理的更新信号!!") + raise ValueError("【浮波柚叶2画触发器】存在尚未处理的更新信号!!") if self.record.enemy.dynamic.stun: return False skill_node: "SkillNode" = kwargs.get("skill_node") @@ -73,11 +72,13 @@ def special_hit_logic(self, **kwargs): self.check_record_module() self.get_prepared(char_CID=1411) if self.record.skill_node_be_changed is None: - raise ValueError(f"【浮波柚叶2画触发器】未发现更新信号!!") + raise ValueError("【浮波柚叶2画触发器】未发现更新信号!!") skill_node: "SkillNode" = self.record.skill_node_be_changed skill_node.force_qte_trigger = True if YUZUHA_REPORT: self.buff_instance.sim_instance.schedule_data.change_process_state() - print(f"【柚叶2画】检测到{skill_node.skill_tag}的重攻击即将命中非失衡敌人!修改参数使其能够触发QTE!") + print( + f"【柚叶2画】检测到{skill_node.skill_tag}的重攻击即将命中非失衡敌人!修改参数使其能够触发QTE!" + ) self.record.skill_node_be_changed = None self.record.last_update_tick = self.buff_instance.sim_instance.tick diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema4QuickAssistTrigger.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema4QuickAssistTrigger.py index e2cd27b4..b0754f14 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema4QuickAssistTrigger.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema4QuickAssistTrigger.py @@ -1,6 +1,7 @@ from .. import Buff, JudgeTools, check_preparation from zsim.define import YUZUHA_REPORT from typing import TYPE_CHECKING + if TYPE_CHECKING: from zsim.sim_progress.Preload import SkillNode from zsim.sim_progress.data_struct.QuickAssistSystem import QuickAssistSystem @@ -9,7 +10,11 @@ class YuzuhaCinema4QuickAssistTriggerRecord: def __init__(self): self.char = None - self.allowed_skill_tag_list = ["1411_Assault_Aid", "1411_Assault_Aid_A", "1411_Assault_Aid_B"] + self.allowed_skill_tag_list = [ + "1411_Assault_Aid", + "1411_Assault_Aid_A", + "1411_Assault_Aid_B", + ] self.trigger_skill_node = None @@ -23,9 +28,7 @@ def __init__(self, buff_instance): self.record: YuzuhaCinema4QuickAssistTriggerRecord | None = None def get_prepared(self, **kwargs): - return check_preparation( - buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs - ) + return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs) def check_record_module(self): if self.buff_0 is None: @@ -41,7 +44,7 @@ def special_judge_logic(self, **kwargs): self.check_record_module() self.get_prepared(char_CID=1411) if self.record.trigger_skill_node is not None: - raise ValueError(f"【柚叶4画触发器】存在尚未处理的快支触发事件!") + raise ValueError("【柚叶4画触发器】存在尚未处理的快支触发事件!") skill_node: "SkillNode" = kwargs.get("skill_node") if skill_node is None: return False @@ -59,8 +62,14 @@ def special_hit_logic(self, **kwargs): sim_instance = self.buff_instance.sim_instance QAS: "QuickAssistSystem" = sim_instance.preload.preload_data.quick_assist_system target_char_obj = sim_instance.char_data.find_next_char_obj(char_now=1411, direction=1) - QAS.force_active_quick_assist(tick_now=sim_instance.tick, skill_node=self.record.trigger_skill_node, char_name=target_char_obj.NAME) + QAS.force_active_quick_assist( + tick_now=sim_instance.tick, + skill_node=self.record.trigger_skill_node, + char_name=target_char_obj.NAME, + ) if YUZUHA_REPORT: sim_instance.schedule_data.change_process_state() - print(f"【柚叶4画】技能 {self.record.trigger_skill_node.skill_tag} 最后一击命中,并且成功激活了 {target_char_obj.NAME} 的快速支援!") + print( + f"【柚叶4画】技能 {self.record.trigger_skill_node.skill_tag} 最后一击命中,并且成功激活了 {target_char_obj.NAME} 的快速支援!" + ) self.record.trigger_skill_node = None diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SheelTrigger.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SheelTrigger.py index db7f0b1e..a11484ed 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SheelTrigger.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SheelTrigger.py @@ -1,6 +1,7 @@ from .. import Buff, JudgeTools, check_preparation from zsim.define import YUZUHA_REPORT from typing import TYPE_CHECKING + if TYPE_CHECKING: from zsim.sim_progress.Character.Yuzuha import Yuzuha from zsim.sim_progress.Preload import SkillNode @@ -17,6 +18,7 @@ def __init__(self): class YuzuhaCinema6SheelTrigger(Buff.BuffLogic): """6画的炮弹触发逻辑""" + def __init__(self, buff_instance): super().__init__(buff_instance) self.buff_instance: Buff = buff_instance @@ -26,9 +28,7 @@ def __init__(self, buff_instance): self.record: YuzuhaCinema6SheelTriggerRecord | None = None def get_prepared(self, **kwargs): - return check_preparation( - buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs - ) + return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs) def check_record_module(self): if self.buff_0 is None: @@ -73,6 +73,7 @@ def special_effect_logic(self, **kwargs): self.check_record_module() self.get_prepared(char_CID=1411) from zsim.sim_progress.data_struct.SchedulePreload import schedule_preload_event_factory + sim_instance = self.buff_instance.sim_instance preload_tick_list = [sim_instance.tick] skill_tag_list = ["1411_Cinema_6"] @@ -81,14 +82,12 @@ def special_effect_logic(self, **kwargs): preload_tick_list=preload_tick_list, skill_tag_list=skill_tag_list, preload_data=preload_data, - sim_instance=sim_instance) + sim_instance=sim_instance, + ) self.record.sheel_counter += 1 self.record.charging_tick = 0 if YUZUHA_REPORT: sim_instance.schedule_data.change_process_state() - print(f"【柚叶6画】检测到正在›蓄力支援突击,将发射1枚炮弹,这是本次蓄力的第{self.record.sheel_counter}枚") - - - - - + print( + f"【柚叶6画】检测到正在›蓄力支援突击,将发射1枚炮弹,这是本次蓄力的第{self.record.sheel_counter}枚" + ) diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SugarBurstMaxTrigger.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SugarBurstMaxTrigger.py index 62f673e4..2dfd1167 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SugarBurstMaxTrigger.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaCinema6SugarBurstMaxTrigger.py @@ -9,6 +9,7 @@ def __init__(self): class YuzuhaCinema6SugarBurstMaxTrigger(Buff.BuffLogic): """炮弹命中甜蜜惊吓状态的敌人时,会触发一次彩糖花火·极""" + def __init__(self, buff_instance): super().__init__(buff_instance) self.buff_instance: Buff = buff_instance @@ -18,9 +19,7 @@ def __init__(self, buff_instance): self.record: YuzuhaCinema6SugarBurstMaxTriggerRecord | None = None def get_prepared(self, **kwargs): - return check_preparation( - buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs - ) + return check_preparation(buff_instance=self.buff_instance, buff_0=self.buff_0, **kwargs) def check_record_module(self): if self.buff_0 is None: diff --git a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaTanukiWishAtkBonus.py b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaTanukiWishAtkBonus.py index e0b90ff0..5df7dc3e 100644 --- a/zsim/sim_progress/Buff/BuffXLogic/YuzuhaTanukiWishAtkBonus.py +++ b/zsim/sim_progress/Buff/BuffXLogic/YuzuhaTanukiWishAtkBonus.py @@ -43,4 +43,6 @@ def special_hit_logic(self, **kwargs): self.buff_instance.update_to_buff_0(buff_0=self.buff_0) if YUZUHA_REPORT: self.buff_instance.sim_instance.schedule_data.change_process_state() - print(f"【狸之愿】柚叶核心被动触发!柚叶场外站街攻击力为{static_atk:.2f}点,【狸之愿】为队友提供{count:.2f}点攻击力!") + print( + f"【狸之愿】柚叶核心被动触发!柚叶场外站街攻击力为{static_atk:.2f}点,【狸之愿】为队友提供{count:.2f}点攻击力!" + ) diff --git a/zsim/sim_progress/Buff/JudgeTools/DetectEdges.py b/zsim/sim_progress/Buff/JudgeTools/DetectEdges.py index c73fcd5c..6bb33140 100644 --- a/zsim/sim_progress/Buff/JudgeTools/DetectEdges.py +++ b/zsim/sim_progress/Buff/JudgeTools/DetectEdges.py @@ -1,4 +1,3 @@ def detect_edge(pair, mode_func): # 使用自定义的mode_func函数来判断是否为上升沿或下降沿 return mode_func(pair[0], pair[1]) - diff --git a/zsim/sim_progress/Buff/JudgeTools/__init__.py b/zsim/sim_progress/Buff/JudgeTools/__init__.py index 034dc06f..129bda89 100644 --- a/zsim/sim_progress/Buff/JudgeTools/__init__.py +++ b/zsim/sim_progress/Buff/JudgeTools/__init__.py @@ -20,7 +20,14 @@ from .. import Buff -def check_preparation(buff_0, buff_instance: "Buff", **kwargs): +def check_preparation( + buff_0: "Buff", + buff_instance: "Buff", + equipper: str | None = None, + char_CID: int | None = None, + char_NAME: str | None = None, + **kwargs, +): """ 这是一个综合函数。根据传入的参数,来执行不同的内容。 """ @@ -30,9 +37,6 @@ def check_preparation(buff_0, buff_instance: "Buff", **kwargs): record = buff_0.history.record # 参数获取 - equipper: str = kwargs.get("equipper") - char_CID: int = kwargs.get("char_CID") - char_NAME: str = kwargs.get("char_NAME") enemy = kwargs.get("enemy") sub_exist_buff_dict = kwargs.get("sub_exist_buff_dict") dynamic_buff_list = kwargs.get("dynamic_buff_list") diff --git a/zsim/sim_progress/Buff/ScheduleBuffSettle.py b/zsim/sim_progress/Buff/ScheduleBuffSettle.py index c9f3e25f..0bd3ab76 100644 --- a/zsim/sim_progress/Buff/ScheduleBuffSettle.py +++ b/zsim/sim_progress/Buff/ScheduleBuffSettle.py @@ -8,7 +8,6 @@ if TYPE_CHECKING: from zsim.simulator.simulator_class import Simulator - from zsim.sim_progress.Preload import SkillNode from zsim.sim_progress.Load import LoadingMission diff --git a/zsim/sim_progress/Character/AstraYao.py b/zsim/sim_progress/Character/AstraYao.py index 818cafa2..91c079bf 100644 --- a/zsim/sim_progress/Character/AstraYao.py +++ b/zsim/sim_progress/Character/AstraYao.py @@ -35,6 +35,7 @@ class Dynamic(Character.Dynamic): def __init__(self, char_instantce: Character): super().__init__(char_instantce) + self.character: "AstraYao" = char_instantce # type: ignore self._on_field = False # 初始化父类的普通属性 @property @@ -66,7 +67,7 @@ def special_resources(self, *args, **kwargs) -> None: def get_resources(self) -> tuple[str, int]: return "咏叹华彩", self.idyllic_cadenza - def get_special_stats(self, *args, **kwargs) -> dict[str, int | float | bool]: + def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]: return {"和弦": self.chord} @@ -142,6 +143,7 @@ def determine_target_char(self, tick: int): 1、下一个角色是耀嘉音——跳过耀嘉音,直接触发下下位角色的快速支援, 2、下一个角色不是耀嘉音——正常触发下一位角色的快速支援。 """ + assert self.manager.preload_data is not None, "preload_data is not initialized" _operating_node = self.manager.preload_data.get_on_field_node(tick) all_name_order_box = self.manager.preload_data.load_data.all_name_order_box if _operating_node is None: @@ -158,6 +160,8 @@ def determine_target_char(self, tick: int): def __active(self, tick: int, skill_node): """触发快速支援!不包含CD判断,只包含触发逻辑。""" if self.manager.preload_data is None: + if self.manager.char.sim_instance is None: + raise ValueError("sim_instance is None, cannot find preload_data") self.manager.preload_data = JudgeTools.find_preload_data( sim_instance=self.manager.char.sim_instance ) @@ -165,6 +169,7 @@ def __active(self, tick: int, skill_node): raise TypeError("快速支援管理器无法找到PreloadData实例!") self.last_update_tick = tick target_char = self.determine_target_char(tick) + assert self.manager.preload_data.quick_assist_system is not None self.manager.preload_data.quick_assist_system.force_active_quick_assist( tick, skill_node, target_char ) @@ -200,13 +205,13 @@ def __init__(self, manager_instance): def c2_ready(self, tick: int): return tick - self.c2_update_tick >= self.c2_trigger_cd - def try_spawn_chord_coattack(self, tick: int, skill_node: "SkillNode" = None): + def try_spawn_chord_coattack(self, tick: int, skill_node: "SkillNode | None"): """给外部的接口,尝试执行!""" if self.manager.char.sp < 25: return self.coattack_active(tick, skill_node) - def coattack_active(self, tick: int, skill_node: "SkillNode" = None): + def coattack_active(self, tick: int, skill_node: "SkillNode | None"): """ 给外部函数调用的接口,用于生成协同攻击。 如果有顺带传入skill_node参数,则意味着调用来自Buff系统的触发器, @@ -215,7 +220,7 @@ def coattack_active(self, tick: int, skill_node: "SkillNode" = None): c2_count = 1 if self.manager.char.cinema >= 2 else 0 loop_times = self.coattack_base_count + c2_count self.__chord_group_spawn_loop(tick, loop_times) - if skill_node: + if skill_node is not None: self.__add_core_passive_buff(skill_node) def __chord_group_spawn_loop(self, tick: int, loop_times: int): @@ -224,6 +229,8 @@ def __chord_group_spawn_loop(self, tick: int, loop_times: int): 并且可以进行重复执行,模仿耀嘉音技能模组中,多次触发的情况。 """ if self.preload_data is None: + if self.manager.char.sim_instance is None: + raise ValueError("sim_instance is None, cannot find preload_data") self.preload_data = JudgeTools.find_preload_data( sim_instance=self.manager.char.sim_instance ) @@ -247,6 +254,8 @@ def __chord_group_spawn_loop(self, tick: int, loop_times: int): preload_tick + self.tremolo_tick, ] preload_tick += self.tremolo_tick + self.tone_clusters_tick + if self.manager.char.sim_instance is None: + raise ValueError("sim_instance is None, cannot schedule event") schedule_preload_event_factory( skill_tag_list=skill_tag_list, preload_tick_list=skill_preload_tick_list, @@ -261,11 +270,15 @@ def __add_core_passive_buff(self, skill_node: "SkillNode"): benifit_list = list(set(add_buff_list)) from zsim.sim_progress.Buff.BuffAddStrategy import buff_add_strategy + if self.manager.char.sim_instance is None: + raise ValueError("sim_instance is None, cannot add buff") + buff_add_strategy( self.core_passive_buff_index, benifit_list=benifit_list, sim_instance=self.manager.char.sim_instance, ) if ASTRAYAO_REPORT: + assert self.manager.char.sim_instance is not None self.manager.char.sim_instance.schedule_data.change_process_state() print(f"核心被动触发器激活!为{benifit_list}添加了{self.core_passive_buff_index}!") diff --git a/zsim/sim_progress/Character/Hugo.py b/zsim/sim_progress/Character/Hugo.py index 2c63cdf3..8274b7c5 100644 --- a/zsim/sim_progress/Character/Hugo.py +++ b/zsim/sim_progress/Character/Hugo.py @@ -10,6 +10,7 @@ def __init__(self, **kwargs): def special_resources(self, *args, **kwargs) -> None: """雨果的特殊资源模块""" if not self.listener_creat: + assert self.sim_instance is not None self.sim_instance.listener_manager.listener_factory( listener_owner=self, initiate_signal="Hugo", @@ -17,8 +18,8 @@ def special_resources(self, *args, **kwargs) -> None: ) return - def get_resources(self) -> tuple[str, float]: - pass + def get_resources(self) -> tuple[str | None, int | float | bool | None]: + return "特殊资源", 0.0 - def get_special_stats(self, *args, **kwargs) -> dict[str, int | float | bool]: - pass + def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]: + return {} diff --git a/zsim/sim_progress/Character/Jane.py b/zsim/sim_progress/Character/Jane.py index 1e5c8da0..21fa9432 100644 --- a/zsim/sim_progress/Character/Jane.py +++ b/zsim/sim_progress/Character/Jane.py @@ -59,10 +59,12 @@ def special_resources(self, *args, **kwargs) -> None: self.salchow_jump -= 1 self.__check_salchow_jump() - labels = node.labels if node.labels is not None else {} - passion_get = labels.get("passion_get", 0) - passion_consume = labels.get("passion_consume", 0) - passion_direct_add = labels.get("passion_direct_add", 0) + labels: dict[str, list[str] | str | int | float] = ( + node.labels if node.labels is not None else {} + ) + passion_get: float = float(labels.get("passion_get", 0)) # type: ignore + passion_consume: float = float(labels.get("passion_consume", 0)) # type: ignore + passion_direct_add: float = float(labels.get("passion_direct_add", 0)) # type: ignore self.__passion_core(passion_get, passion_consume, passion_direct_add) # TODO 关于萨霍夫跳的第一段(1301_SNA_1)的拆分问题: @@ -91,7 +93,7 @@ def external_passion_change(self): def get_resources(self) -> tuple[str, float]: return "狂热心流", self.passion_stream - def get_special_stats(self, *args, **kwargs) -> dict[str, int | float | bool]: + def get_special_stats(self, *args, **kwargs) -> dict[str | None, object | None]: """获取简的特殊状态""" return { "狂热心流": self.passion_stream, diff --git a/zsim/sim_progress/Character/Lighter.py b/zsim/sim_progress/Character/Lighter.py index 0d8ef603..9c75ebbd 100644 --- a/zsim/sim_progress/Character/Lighter.py +++ b/zsim/sim_progress/Character/Lighter.py @@ -59,12 +59,13 @@ def special_resources(self, *args, **kwargs) -> None: self.morale = 0 # 时间每 6 ticks 更新 - tick = self.sim_instance.tick + assert self.sim_instance is not None + tick: int = self.sim_instance.tick if tick is not None: if (minus := tick - self.last_tick) >= 6: self.morale += minus // 6 * 29 # 地板除保证整形对齐 self.last_tick = tick - minus % 6 # 求余以保证余数不计入本次计算 self.morale = min(self.morale, 10000) - def get_resources(self, *args, **kwargs) -> tuple[str, float]: + def get_resources(self) -> tuple[str, float]: return "士气", self.morale / 100 diff --git a/zsim/sim_progress/Character/Miyabi.py b/zsim/sim_progress/Character/Miyabi.py index e9930b26..801dc909 100644 --- a/zsim/sim_progress/Character/Miyabi.py +++ b/zsim/sim_progress/Character/Miyabi.py @@ -1,3 +1,4 @@ +from typing import Literal from zsim.sim_progress.anomaly_bar import Disorder from zsim.sim_progress.Preload import SkillNode from zsim.sim_progress.Report import report_to_log @@ -64,6 +65,7 @@ def special_resources(self, *args, **kwargs) -> None: def _shatter_internal_cd(self) -> bool: """判断落霜叠层是否处于CD""" + assert self.sim_instance is not None tick: int = self.sim_instance.tick if self.last_tick is None: self.last_tick = tick @@ -74,5 +76,5 @@ def _shatter_internal_cd(self) -> bool: self.last_tick = tick return False - def get_resources(self): + def get_resources(self) -> tuple[Literal["落霜"], int]: return "落霜", self.frosty diff --git a/zsim/sim_progress/Character/Qingyi.py b/zsim/sim_progress/Character/Qingyi.py index b8cec811..931547ca 100644 --- a/zsim/sim_progress/Character/Qingyi.py +++ b/zsim/sim_progress/Character/Qingyi.py @@ -11,7 +11,7 @@ def __init__(self, **kwargs): self.__MAX_VOLTAGE: int = 10000 self.__QUAN_VOLTAGE: float = self.__MAX_VOLTAGE / 100 * (1.3 if self.cinema >= 1 else 1) self.__FLASH_THRESHOLD: float = self.__MAX_VOLTAGE * 0.75 - self.VOLTAGE_MAP: dict = { + self.VOLTAGE_MAP: dict[str, float] = { "1300_NA_3_NFC": self.__QUAN_VOLTAGE * 4.6875, "1300_NA_3_FC": self.__QUAN_VOLTAGE * 4.6875 * 16, "1300_SNA": self.__QUAN_VOLTAGE * 1.94, @@ -26,7 +26,7 @@ def __init__(self, **kwargs): "1300_Q": self.__QUAN_VOLTAGE * 80, } - self.flash_connect_voltage: int = ( + self.flash_connect_voltage: float = ( 0 if self.cinema == 0 else self.__MAX_VOLTAGE ) # 闪络电压,初始化为0 self.flash_connect: bool = False if self.cinema == 0 else True # 闪络状态 diff --git a/zsim/sim_progress/Character/Soldier0_Anby.py b/zsim/sim_progress/Character/Soldier0_Anby.py index 1eeed1d7..c977644d 100644 --- a/zsim/sim_progress/Character/Soldier0_Anby.py +++ b/zsim/sim_progress/Character/Soldier0_Anby.py @@ -23,7 +23,7 @@ def __init__(self, **kwargs): self.c6_answer: bool = False # 6画的触发响应器 self.silver_star_basic_cost = 33.333333 # 单E的银星基本消耗 self.max_silver_star: float = 100.1 # 银星上限 - self.silver_star_gain_dict: dict[str:float] = { + self.silver_star_gain_dict: dict[str, float] = { "1381_NA_1": 4.6875, "1381_NA_2": 7.53472222, "1381_NA_3": 15.12152778, diff --git a/zsim/sim_progress/Character/Soldier11.py b/zsim/sim_progress/Character/Soldier11.py index 84100fc8..4aab66b9 100644 --- a/zsim/sim_progress/Character/Soldier11.py +++ b/zsim/sim_progress/Character/Soldier11.py @@ -14,7 +14,8 @@ def special_resources(self, *args, **kwargs) -> None: """模拟11号的火力镇压机制""" # 输入类型检查 skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs) - tick = self.sim_instance.tick + assert self.sim_instance is not None + tick: int = self.sim_instance.tick for node in skill_nodes: if self.settle_tick is not None: # 超时重置 diff --git a/zsim/sim_progress/Character/Yuzuha/__init__.py b/zsim/sim_progress/Character/Yuzuha/__init__.py index 2dd54eb0..aea888bb 100644 --- a/zsim/sim_progress/Character/Yuzuha/__init__.py +++ b/zsim/sim_progress/Character/Yuzuha/__init__.py @@ -25,9 +25,13 @@ def special_resources(self, *args, **kwargs) -> None: skill_nodes: list[SkillNode] = _skill_node_filter(*args, **kwargs) for node in skill_nodes: sim_instance: "Simulator" = self.sim_instance - sim_instance.schedule_data.enemy.special_state_manager.broadcast_and_update(signal=SSUS.CHARACTER, skill_node=node) + sim_instance.schedule_data.enemy.special_state_manager.broadcast_and_update( + signal=SSUS.CHARACTER, skill_node=node + ) if node.skill_tag == "1411_Assault_Aid_B" and self.cinema < 6: - raise ValueError(f"企图在非6画状态下对支援突击进行蓄力!请检查define中的招架支援配置!") + raise ValueError( + "企图在非6画状态下对支援突击进行蓄力!请检查define中的招架支援配置!" + ) if node.char_name != self.NAME: continue if node.skill.labels is not None and "sugar_points" in node.skill.labels: @@ -95,12 +99,18 @@ def spawn_hard_candy_shot(self, update_signal: "SkillNode" = None): def POST_INIT_DATA(self, sim_insatnce: "Simulator"): """柚叶的后置初始化函数,用于后置创建甜蜜惊吓特殊状态""" enemy = sim_insatnce.schedule_data.enemy - self.sweet_scare = enemy.special_state_manager.special_state_factory(state_type=PIOT.SweetScare) - self.sp = 40.00 if self.cinema < 1 else 70.00 # 初始化能量值 + self.sweet_scare = enemy.special_state_manager.special_state_factory( + state_type=PIOT.SweetScare + ) + self.sp = 40.00 if self.cinema < 1 else 70.00 # 初始化能量值 if self.cinema >= 2: - sim_insatnce.listener_manager.listener_factory(listener_owner=self, initiate_signal="Yuzuha_1", sim_instance=sim_insatnce) + sim_insatnce.listener_manager.listener_factory( + listener_owner=self, initiate_signal="Yuzuha_1", sim_instance=sim_insatnce + ) if self.cinema >= 6: - sim_insatnce.listener_manager.listener_factory(listener_owner=self, initiate_signal="Yuzuha_2", sim_instance=sim_insatnce) + sim_insatnce.listener_manager.listener_factory( + listener_owner=self, initiate_signal="Yuzuha_2", sim_instance=sim_insatnce + ) def get_resources(self, *args, **kwargs) -> tuple[str | None, int | float | None]: return "甜度点", self.sugar_points diff --git a/zsim/sim_progress/Character/character.py b/zsim/sim_progress/Character/character.py index a3ca8141..5b832d47 100644 --- a/zsim/sim_progress/Character/character.py +++ b/zsim/sim_progress/Character/character.py @@ -144,7 +144,7 @@ def __init__( self.cinema: int = cinema self.baseCRIT_score: float = 60 self.sp_get_ratio: float = 1 # 能量获得效率 - self.sp_limit: int = sp_limit + self.sp_limit: int = int(sp_limit) self.sp: float = 40.0 self.decibel: float = 1000.0 @@ -154,13 +154,14 @@ def __init__( self.crit_balancing: bool = crit_balancing self.crit_rate_limit: float = crit_rate_limit + self.sheer_attack_conversion_rate: dict[int, float] | None = None # 初始化角色基础属性 .\data\character.csv self._init_base_attribute(name) # fmt: off # 如果并行配置没有移除套装,就初始化套装效果和主副词条 if sim_cfg is not None: - if sim_cfg.func == "attr_curve": + if isinstance(sim_cfg, ExecAttrCurveCfg): if not sim_cfg.remove_equip: self.__init_all_equip_static(drive4, drive5, drive6, equip_set2_a, equip_set2_b, equip_set2_c, equip_set4, equip_style, @@ -168,7 +169,7 @@ def __init__( scCRIT_DMG, scDEF, scDEF_percent, scHP, scHP_percent, scPEN) self.__init_attr_curve_config(sim_cfg) self._init_weapon_primitive(weapon, weapon_level) - elif sim_cfg.func == "weapon": + elif isinstance(sim_cfg, ExecWeaponCfg): self.__init_all_equip_static(drive4, drive5, drive6, equip_set2_a, equip_set2_b, equip_set2_c, equip_set4, equip_style, scATK, scATK_percent, scAnomalyProficiency, scCRIT, @@ -198,7 +199,7 @@ def __init__( self.action_list = self.skill_object.action_list self.skills_dict = self.skill_object.skills_dict self.dynamic = self.Dynamic(self) - self.sim_instance = None # 模拟器实例 + self.sim_instance: "Simulator | None" = None # 模拟器实例 self.equip_buff_map: dict[int, "Buff"] = {} # 来自装备的Buff0的指针 # fmt: off @@ -345,7 +346,7 @@ class Dynamic: def __init__(self, char_instantce: Character): self.character = char_instantce self.lasting_node = LastingNode(self.character) - from zsim.sim_progress.data_struct.QuickAssistSystem.QuickAssistManager import ( + from zsim.sim_progress.data_struct.QuickAssistSystem.quick_assist_manager import ( QuickAssistManager, ) diff --git a/zsim/sim_progress/Dot/BaseDot.py b/zsim/sim_progress/Dot/BaseDot.py index f44d183f..89765f8f 100644 --- a/zsim/sim_progress/Dot/BaseDot.py +++ b/zsim/sim_progress/Dot/BaseDot.py @@ -4,15 +4,16 @@ from zsim.sim_progress.anomaly_bar import AnomalyBar if TYPE_CHECKING: + from zsim.sim_progress.Preload import SkillNode from zsim.simulator.simulator_class import Simulator class Dot: def __init__( self, - bar: AnomalyBar = None, + bar: "AnomalyBar | None", skill_tag: str | None = None, - sim_instance: "Simulator" = None, + sim_instance: "Simulator | None" = None, ): self.sim_instance = sim_instance self.ft = self.DotFeature(sim_instance=self.sim_instance) @@ -20,7 +21,7 @@ def __init__( self.history = self.DotHistory() # 默认情况下不创建anomlay_data。 self.anomaly_data = None - self.skill_node_data = None + self.skill_node_data: "SkillNode | None" = None if bar is not None and skill_tag is not None: raise ValueError("Dot的构造函数不可以同时传入bar和skill_tag") if bar: @@ -29,6 +30,9 @@ def __init__( from zsim.sim_progress.Buff import JudgeTools from zsim.sim_progress.Preload.SkillsQueue import spawn_node + if self.sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + preload_data = JudgeTools.find_preload_data(sim_instance=self.sim_instance) tick = JudgeTools.find_tick(sim_instance=self.sim_instance) self.skill_node_data = spawn_node(skill_tag, tick, preload_data.skills) @@ -46,15 +50,15 @@ class DotFeature: 4:碎冰——只有含有重攻击的技能在end标签处才能触发。 """ - sim_instance: "Simulator" + sim_instance: "Simulator | None" update_cd: int | float = 0 - index: str = None - name: str = None - dot_from: str = None - effect_rules: int = None - max_count: int = None - max_duration: int = None - incremental_step: int = None + index: str | None = None + name: str | None = None + dot_from: str | None = None + effect_rules: int | None = None + max_count: int | None = None + max_duration: int | None = None + incremental_step: int | None = None max_effect_times: int = 30 count_as_skill_hit: bool = ( False # dot生效时的伤害能否视作技能的一次命中(从而参与其他的命中类dot的触发) @@ -69,9 +73,9 @@ class DotDynamic: start_ticks: int = 0 end_ticks: int = 0 last_effect_ticks: int = 0 - active: bool = None + active: bool | None = None count: int = 0 - ready: bool = None + ready: bool | None = None effect_times: int = 0 @dataclass @@ -82,19 +86,19 @@ class DotHistory: last_end_ticks: int = 0 last_duration: int = 0 - def ready_judge(self, timenow): + def ready_judge(self, timenow: int): if not self.dy.ready: if timenow - self.dy.last_effect_ticks >= self.ft.update_cd: self.dy.ready = True - def end(self, timenow): + def end(self, timenow: int): self.dy.active = False self.dy.count = 0 self.history.last_end_ticks = timenow self.history.last_duration = timenow - self.dy.start_ticks self.history.end_times += 1 - def start(self, timenow): + def start(self, timenow: int): self.dy.active = True self.dy.start_ticks = timenow self.dy.last_effect_ticks = timenow @@ -107,5 +111,5 @@ def start(self, timenow): self.dy.effect_times = 1 self.dy.ready = False - def exit_judge(self, **kwargs): + def exit_judge(self, **kwargs) -> None: pass diff --git a/zsim/sim_progress/Dot/Dots/AuricInkCorruption.py b/zsim/sim_progress/Dot/Dots/AuricInkCorruption.py index 8d779e80..b5f9f3d8 100644 --- a/zsim/sim_progress/Dot/Dots/AuricInkCorruption.py +++ b/zsim/sim_progress/Dot/Dots/AuricInkCorruption.py @@ -4,33 +4,36 @@ from .. import Dot if TYPE_CHECKING: + from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.simulator.simulator_class import Simulator class AuricInkCorruption(Dot): - def __init__(self, bar=None, sim_instance: "Simulator" = None): + def __init__(self, bar: "AnomalyBar | None", sim_instance: "Simulator | None"): super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法 self.ft = self.DotFeature(sim_instance=sim_instance) @dataclass class DotFeature(Dot.DotFeature): - sim_instance: "Simulator" + sim_instance: "Simulator | None" char_name_box: list[str] = field(init=False) - update_cd: int = 30 - index: str = "AuricInkCorruption" - name: str = "玄墨侵蚀" - dot_from: str = "enemy" - effect_rules: int = 2 - max_count: int = 1 - incremental_step: int = 1 - max_duration: int | None = None + update_cd: int | float = 30 + index: str | None = "AuricInkCorruption" + name: str | None = "玄墨侵蚀" + dot_from: str | None = "enemy" + effect_rules: int | None = 2 + max_count: int | None = 1 + incremental_step: int | None = 1 + max_duration: int | None = 600 max_effect_times = 30 def __post_init__(self): + if self.sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + self.char_name_box = self.sim_instance.init_data.name_box - self.max_duration: int = 600 """ 如果某角色在角色列表里,侵蚀和最大生效次数就要发生变化。 """ if "某角色" in self.char_name_box: - self.max_duration: int = 600 + 180 + self.max_duration = 600 + 180 diff --git a/zsim/sim_progress/Dot/Dots/Corruption.py b/zsim/sim_progress/Dot/Dots/Corruption.py index bbba3c92..43c2f01e 100644 --- a/zsim/sim_progress/Dot/Dots/Corruption.py +++ b/zsim/sim_progress/Dot/Dots/Corruption.py @@ -4,33 +4,37 @@ from .. import Dot if TYPE_CHECKING: + from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.simulator.simulator_class import Simulator class Corruption(Dot): - def __init__(self, bar=None, sim_instance: "Simulator" = None): + def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None): super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法 self.ft = self.DotFeature(sim_instance=sim_instance) @dataclass class DotFeature(Dot.DotFeature): - sim_instance: "Simulator" + sim_instance: "Simulator | None" char_name_box: list[str] = field(init=False) - update_cd: int = 30 - index: str = "Corruption" - name: str = "侵蚀" - dot_from: str = "enemy" - effect_rules: int = 2 - max_count: int = 1 - incremental_step: int = 1 + update_cd: int | float = 30 + index: str | None = "Corruption" + name: str | None = "侵蚀" + dot_from: str | None = "enemy" + effect_rules: int | None = 2 + max_count: int | None = 1 + incremental_step: int | None = 1 max_duration: int | None = None - max_effect_times = 30 + max_effect_times: int = 30 def __post_init__(self): + if self.sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + self.char_name_box = self.sim_instance.init_data.name_box - self.max_duration: int = 600 + self.max_duration = 600 """ 如果某角色在角色列表里,侵蚀和最大生效次数就要发生变化。 """ if "某角色" in self.char_name_box: - self.max_duration: int = 600 + 180 + self.max_duration = 600 + 180 diff --git a/zsim/sim_progress/Dot/Dots/Freez.py b/zsim/sim_progress/Dot/Dots/Freez.py index 66bf5f25..a7d5de77 100644 --- a/zsim/sim_progress/Dot/Dots/Freez.py +++ b/zsim/sim_progress/Dot/Dots/Freez.py @@ -6,37 +6,42 @@ from .. import Dot if TYPE_CHECKING: + from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.simulator.simulator_class import Simulator class Freez(Dot): - def __init__(self, bar=None, sim_instance: "Simulator" = None): + def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None): super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法 self.ft = self.DotFeature(sim_instance=sim_instance) @dataclass class DotFeature(Dot.DotFeature): - sim_instance: "Simulator" + sim_instance: "Simulator | None" enemy = None update_cd: int | float = np.inf - index: str = "Freez" - name: str = "冻结" - dot_from: str = "enemy" - effect_rules: int = 4 - max_count: int = 1 - incremental_step: int = 1 - max_duration: int | float | None = None - max_effect_times = 1 + index: str | None = "Freez" + name: str | None = "冻结" + dot_from: str | None = "enemy" + effect_rules: int | None = 4 + max_count: int | None = 1 + incremental_step: int | None = 1 + max_duration: int | None = None + max_effect_times: int = 1 def __post_init__(self): + if self.sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + self.enemy = self.sim_instance.schedule_data.enemy - self.max_duration = 240 * (1 + self.enemy.freeze_resistance) + self.max_duration = int(240 * (1 + self.enemy.freeze_resistance)) - def start(self, timenow): + def start(self, timenow: int): self.dy.active = True self.dy.start_ticks = timenow self.dy.last_effect_ticks = timenow - self.dy.end_ticks = self.dy.start_ticks + self.ft.max_duration + if self.ft.max_duration is not None: + self.dy.end_ticks = self.dy.start_ticks + self.ft.max_duration self.history.start_times += 1 self.history.last_start_ticks = timenow self.dy.count = 1 diff --git a/zsim/sim_progress/Dot/Dots/Ignite.py b/zsim/sim_progress/Dot/Dots/Ignite.py index ba1b6ef3..c3d1cf64 100644 --- a/zsim/sim_progress/Dot/Dots/Ignite.py +++ b/zsim/sim_progress/Dot/Dots/Ignite.py @@ -4,6 +4,7 @@ from .. import Dot if TYPE_CHECKING: + from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.simulator.simulator_class import Simulator @@ -12,7 +13,7 @@ class Ignite(Dot): 灼烧dot,固定时间,内置CD0.5s """ - def __init__(self, bar=None, sim_instance: "Simulator" = None): + def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None): super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法 self.ft = self.DotFeature( sim_instance=sim_instance @@ -21,24 +22,27 @@ def __init__(self, bar=None, sim_instance: "Simulator" = None): # 你可以在这里添加特定于Ignite的行为或方法 @dataclass class DotFeature(Dot.DotFeature): - sim_instance: "Simulator" + sim_instance: "Simulator | None" char_name_box: list[str] = field(init=False) - update_cd: int = 30 - index: str = "Ignite" - name: str = "灼烧" - dot_from: str = "enemy" - effect_rules: int = 1 - max_count: int = 1 - incremental_step: int = 1 + update_cd: int | float = 30 + index: str | None = "Ignite" + name: str | None = "灼烧" + dot_from: str | None = "enemy" + effect_rules: int | None = 1 + max_count: int | None = 1 + incremental_step: int | None = 1 max_duration: int | None = None - max_effect_times = 30 + max_effect_times: int = 30 def __post_init__(self): + if self.sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + self.char_name_box = self.sim_instance.global_stats.name_box """ 如果柏妮思在角色列表里,灼烧和最大生效次数就要发生变化。 """ if "柏妮思" in self.char_name_box: - self.max_duration: int = 600 + 180 + self.max_duration = 600 + 180 else: - self.max_duration: int = 600 + self.max_duration = 600 diff --git a/zsim/sim_progress/Dot/Dots/Shock.py b/zsim/sim_progress/Dot/Dots/Shock.py index 5affe05b..80621934 100644 --- a/zsim/sim_progress/Dot/Dots/Shock.py +++ b/zsim/sim_progress/Dot/Dots/Shock.py @@ -4,39 +4,43 @@ from .. import Dot if TYPE_CHECKING: + from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.simulator.simulator_class import Simulator class Shock(Dot): - def __init__(self, bar=None, sim_instance: "Simulator" = None): + def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None): super().__init__(bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法 self.ft = self.DotFeature(sim_instance=sim_instance) @dataclass class DotFeature(Dot.DotFeature): - sim_instance: "Simulator" + sim_instance: "Simulator | None" char_name_box: list[str] = field(init=False) exist_buff_dict: dict[str, dict[str, object]] = field(init=False) - update_cd: int = 60 - index: str = "Shock" - name: str = "感电" - dot_from: str = "enemy" - effect_rules: int = 2 - max_count: int = 1 - incremental_step: int = 1 + update_cd: int | float = 60 + index: str | None = "Shock" + name: str | None = "感电" + dot_from: str | None = "enemy" + effect_rules: int | None = 2 + max_count: int | None = 1 + incremental_step: int | None = 1 """ 如果丽娜在角色列表里,灼烧和最大生效次数就要发生变化。 """ max_duration: int | None = None - max_effect_times = 30 + max_effect_times: int = 30 def __post_init__(self): + if self.sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + self.char_name_box = self.sim_instance.init_data.name_box self.exist_buff_dict = self.sim_instance.load_data.exist_buff_dict if "丽娜" in self.char_name_box: if "Buff-角色-丽娜-组队被动-延长感电" in self.exist_buff_dict["丽娜"]: - self.max_duration: int = 600 + 180 + self.max_duration = 600 + 180 else: - self.max_duration: int = 600 + self.max_duration = 600 else: - self.max_duration: int = 600 + self.max_duration = 600 diff --git a/zsim/sim_progress/Dot/Dots/ViviansProphecy.py b/zsim/sim_progress/Dot/Dots/ViviansProphecy.py index 2f802acf..1f352575 100644 --- a/zsim/sim_progress/Dot/Dots/ViviansProphecy.py +++ b/zsim/sim_progress/Dot/Dots/ViviansProphecy.py @@ -7,39 +7,42 @@ from .. import Dot if TYPE_CHECKING: + from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.simulator.simulator_class import Simulator class ViviansProphecy(Dot): - def __init__(self, sim_instance: "Simulator" = None): - super().__init__(sim_instance=sim_instance) # 调用父类Dot的初始化方法 + def __init__(self, bar: "AnomalyBar | None" = None, sim_instance: "Simulator | None" = None): + super().__init__(bar=bar, sim_instance=sim_instance) # 调用父类Dot的初始化方法 self.ft = self.DotFeature(sim_instance=sim_instance) - self.preload_data = JudgeTools.find_preload_data(sim_instance=self.sim_instance) - tick = JudgeTools.find_tick(sim_instance=self.sim_instance) + if sim_instance is None: + raise ValueError("sim_instance is None, but it should not be.") + + self.preload_data = JudgeTools.find_preload_data(sim_instance=sim_instance) + tick = JudgeTools.find_tick(sim_instance=sim_instance) self.skill_node_data = spawn_node("1331_Core_Passive", tick, self.preload_data.skills) @dataclass class DotFeature(Dot.DotFeature): - sim_instance: "Simulator" - update_cd: int = 33 - index: str = "ViviansProphecy" - name: str = "薇薇安的预言" - dot_from: str = "薇薇安" - effect_rules: int = 1 - max_count: int = 999999 - incremental_step: int = 1 - max_duration: int = 999999 + sim_instance: "Simulator | None" + update_cd: int | float = 33 + index: str | None = "ViviansProphecy" + name: str | None = "薇薇安的预言" + dot_from: str | None = "薇薇安" + effect_rules: int | None = 1 + max_count: int | None = 999999 + incremental_step: int | None = 1 + max_duration: int | None = 999999 complex_exit_logic = True - def exit_judge(self, **kwargs) -> bool: + def exit_judge(self, **kwargs): """薇薇安的预言 dot的退出逻辑:敌人只要处于异常状态,就不会退出。""" enemy = kwargs.get("enemy", None) if enemy is None: - return False + return from zsim.sim_progress.Enemy import Enemy if not isinstance(enemy, Enemy): raise TypeError("enemy参数必须是Enemy类的实例") if not enemy.dynamic.is_under_anomaly(): - return True - return False + self.dy.active = False diff --git a/zsim/sim_progress/Enemy/EnemyAttack/EnemyAttackClass.py b/zsim/sim_progress/Enemy/EnemyAttack/EnemyAttackClass.py index bc8de35d..6ceb07a5 100644 --- a/zsim/sim_progress/Enemy/EnemyAttack/EnemyAttackClass.py +++ b/zsim/sim_progress/Enemy/EnemyAttack/EnemyAttackClass.py @@ -68,13 +68,9 @@ def __init__(self, ID: int = 0, enemy_instance: "Enemy" = None): enemy_attack_action = EnemyAttackAction(int(action_id)) self.action_set[action_rate] = enemy_attack_action if ENEMY_ATTACK_REPORT: - print( - f"【进攻交互系统初始化】:为敌人添加进攻动作:{enemy_attack_action}" - ) + print(f"【进攻交互系统初始化】:为敌人添加进攻动作:{enemy_attack_action}") if ENEMY_ATTACK_REPORT: - print( - "【进攻交互系统初始化】:敌人进攻动作初始化完毕!" - ) + print("【进攻交互系统初始化】:敌人进攻动作初始化完毕!") print( f"【进攻交互系统初始化】:敌人({self.enemy.name})共拥有{len(self.action_set)}个进攻动作,每次进攻决策的冷却时间为:{self.rest_tick}tick!" ) diff --git a/zsim/sim_progress/Enemy/QTEManager/QTEData.py b/zsim/sim_progress/Enemy/QTEManager/QTEData.py index 0d9848de..1666edf7 100644 --- a/zsim/sim_progress/Enemy/QTEManager/QTEData.py +++ b/zsim/sim_progress/Enemy/QTEManager/QTEData.py @@ -57,8 +57,10 @@ def __init__(self, enemy_instance): } self.preload_data = None - def check_myself(self, single_hit: SingleHit = None) -> bool: + def check_myself(self, single_hit: SingleHit | None = None) -> bool: """该函数用于检查自身目前的状态,即当前是彩色失衡还是灰色失衡;""" + if self.qte_triggerable_times is None: + return False if self.qte_triggered_times > self.qte_triggerable_times: raise ValueError( f"QTE的实际响应总次数为{self.qte_triggered_times}次,大于其最大次数{self.qte_triggerable_times}次!" @@ -68,6 +70,8 @@ def check_myself(self, single_hit: SingleHit = None) -> bool: f"QTE总计包含了{len(self.qte_received_box)}个skill_tag,而实际的响应次数为{self.qte_triggered_times}次!" ) if self.enemy_instance.dynamic.stun: + if self.qte_triggerable_times is None: + return False if self.qte_triggered_times < self.qte_triggerable_times: return True else: @@ -90,7 +94,10 @@ def check_myself(self, single_hit: SingleHit = None) -> bool: return True else: if single_hit is not None: - if single_hit.skill_node.skill.trigger_buff_level == 5: + if ( + single_hit.skill_node + and single_hit.skill_node.skill.trigger_buff_level == 5 + ): return True return False @@ -122,6 +129,7 @@ def try_qte(self, hit: SingleHit) -> None: # 3.1、如果是能够激发连携的hit,而此时又没有SingleHit存在,那么就是激活了新的QTE阶段,进入下一步判断。 self.single_qte = SingleQTE(self, single_hit=hit) self.enemy_instance.sim_instance.schedule_data.change_process_state() + assert hit.skill_node is not None print( f"{hit.skill_node.char_name} 的 {hit.skill_node.skill.skill_text} 激发了连携技!当前已经激发过{self.qte_triggered_times + 1}次连携技!" ) @@ -174,10 +182,12 @@ def qte_active_selector(self, _hit: SingleHit) -> bool: if not isinstance(self.preload_data, PreloadData): raise TypeError("QTEData的preload_data属性不是PreloadData类!") - if not self.enemy_instance.dynamic.stun and _hit.skill_node.force_qte_trigger: - # FIXME: 临时解决方案,理论上2画触发连携技需要柚叶在前台,合轴会导致激发失败; - # 但是这涉及到的底层逻辑较为复杂,APL也不太好改,所以这里暂时先这么临时解决一下 - return True + if not self.enemy_instance.dynamic.stun: + assert _hit.skill_node is not None + if _hit.skill_node.force_qte_trigger: + # FIXME: 临时解决方案,理论上2画触发连携技需要柚叶在前台,合轴会导致激发失败; + # 但是这涉及到的底层逻辑较为复杂,APL也不太好改,所以这里暂时先这么临时解决一下 + return True if self.preload_data.operating_now is None: """说明目前没有任何角色在前台""" return False @@ -189,7 +199,9 @@ class SingleQTE: def __init__(self, qte_data: QTEData, single_hit: SingleHit): self.qte_data = qte_data self.qte_received_box: list[str] = [] # 用于接受QTE阶段输入的QTE skill_tag - self.qte_triggerable_times: int = self.qte_data.qte_triggerable_times # 最多可以触发几次QTE + self.qte_triggerable_times: int | None = ( + self.qte_data.qte_triggerable_times + ) # 最多可以触发几次QTE self.qte_triggered_times: int = 0 # 已经响应了几次QTE self.qte_activation_available = False # 彩色失衡阶段 self.__is_hitted = ( @@ -218,10 +230,10 @@ def receive_hit(self, _single_hit: SingleHit): """非失衡阶段的QTE接收逻辑(强制触发的QTE)""" self.receive_qte_without_stun(_single_hit) - def receive_hit_while_stun(self, _single_hit: SingleHit): """失衡期接收QTE的业务逻辑""" self.qte_triggered_times += 1 + assert _single_hit.skill_node is not None if "QTE" not in _single_hit.skill_tag: """说明QTE被取消了""" self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state() @@ -230,6 +242,7 @@ def receive_hit_while_stun(self, _single_hit: SingleHit): ) else: """角色响应了QTE,释放连携技""" + assert self.active_by.skill_node is not None if _single_hit.skill_node.char_name == self.active_by.skill_node.char_name: raise ValueError(f"{_single_hit.skill_node.char_name} 企图响应自己激发的QTE!") self.qte_received_box.append(_single_hit.skill_tag) @@ -253,6 +266,7 @@ def merge_single_qte(self): def receive_qte_without_stun(self, _single_hit: SingleHit): """在非失衡阶段接收hit""" + assert _single_hit.skill_node is not None if "QTE" not in _single_hit.skill_tag: """说明QTE被取消了""" self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state() @@ -261,10 +275,12 @@ def receive_qte_without_stun(self, _single_hit: SingleHit): ) else: """角色响应了QTE,释放连携技""" - if _single_hit.skill_node.char_name == self.active_by.skill_node.char_name and not self.active_by.skill_node.force_qte_trigger: - raise ValueError( - f"{_single_hit.skill_node.char_name} 企图响应自己激发的QTE!" - ) + assert self.active_by.skill_node is not None + if ( + _single_hit.skill_node.char_name == self.active_by.skill_node.char_name + and not self.active_by.skill_node.force_qte_trigger + ): + raise ValueError(f"{_single_hit.skill_node.char_name} 企图响应自己激发的QTE!") # FIXME:由于柚叶2画有强行在非失衡期触发连携技的特性,为了系统稳定暂时让这个激发在后台也能生效。 # 所以这里的验错也需要避开这个情况,否则就会报“自己响应自己QTE”的错误 self.qte_data.enemy_instance.sim_instance.schedule_data.change_process_state() @@ -273,4 +289,3 @@ def receive_qte_without_stun(self, _single_hit: SingleHit): ) self.__is_hitted = True self.merge_single_qte() - diff --git a/zsim/sim_progress/Enemy/__init__.py b/zsim/sim_progress/Enemy/__init__.py index c455e3f8..2b2979f3 100644 --- a/zsim/sim_progress/Enemy/__init__.py +++ b/zsim/sim_progress/Enemy/__init__.py @@ -647,7 +647,7 @@ def __init__(self): # self.dynamic_dot_list = MonitoredList() # 用来装dot的list self.dynamic_dot_list = [] # 用来装dot的list - self.active_anomaly_bar_dict = { + self.active_anomaly_bar_dict: dict[int, type[AnomalyBar] | None] = { number: AnomalyBar for number in range(6) } # 用来装激活属性异常的字典。 diff --git a/zsim/sim_progress/Load/LoadDamageEvent.py b/zsim/sim_progress/Load/LoadDamageEvent.py index ef369bcd..3811678f 100644 --- a/zsim/sim_progress/Load/LoadDamageEvent.py +++ b/zsim/sim_progress/Load/LoadDamageEvent.py @@ -137,8 +137,8 @@ def DamageEventJudge( # dynamic_buff_dict = kwargs.get("dynamic_buff_dict", None) process_overtime_mission(timetick, load_mission_dict) for mission in load_mission_dict.values(): - if not isinstance(mission, LoadingMission | Dot.Dot): - raise TypeError(f"{mission}不是LoadingMission或是Dot类!") + if not isinstance(mission, LoadingMission): + raise TypeError(f"{mission}不是LoadingMission类!") if mission.is_hit_now(timetick): SpawnDamageEvent(mission, event_list) # 当Mission触发时,检查 effect_rules == 2 的 Dot diff --git a/zsim/sim_progress/Load/loading_mission.py b/zsim/sim_progress/Load/loading_mission.py index bcc0f2e7..7ab75cdb 100644 --- a/zsim/sim_progress/Load/loading_mission.py +++ b/zsim/sim_progress/Load/loading_mission.py @@ -13,9 +13,9 @@ def __init__(self, mission: SkillNode): self.mission_end_tick = mission.end_tick self.mission_character = mission.char_name self.preload_tick = mission.preload_tick - self.mission_node.loading_mission = self + self.mission_node.loading_mission = self # type: ignore - def mission_start(self, timenow, **kwargs): + def mission_start(self, timenow: int, **kwargs) -> None: report = kwargs.get("report", True) self.mission_active_state = True timecost = self.mission_node.skill.ticks @@ -42,17 +42,17 @@ def mission_start(self, timenow, **kwargs): else: self.mission_dict[timenow] = "hit" - def mission_end(self): + def mission_end(self) -> None: self.mission_active_state = False self.hitted_count = 0 self.mission_dict = {} - def check_myself(self, timenow): + def check_myself(self, timenow: int) -> None: if self.mission_end_tick < timenow: self.mission_end() return - def get_first_hit(self): + def get_first_hit(self) -> int | None: """返回首次命中的时间""" tick_list = list(self.mission_dict.keys()) while tick_list: @@ -71,7 +71,7 @@ def is_hit_now(self, tick_now: int) -> bool: else: return False - def get_last_hit(self): + def get_last_hit(self) -> int | None: """返回最后一次命中的时间""" tick_list = list(self.mission_dict.keys()) while tick_list: @@ -81,13 +81,19 @@ def get_last_hit(self): else: tick_list.remove(tick) - def is_first_hit(self, tick: int): - return tick - 1 < self.get_first_hit() <= tick + def is_first_hit(self, tick: int) -> bool: + first_hit = self.get_first_hit() + if first_hit is None: + return False + return tick - 1 < first_hit <= tick - def is_last_hit(self, tick: int): - return tick - 1 < self.get_last_hit() <= tick + def is_last_hit(self, tick: int) -> bool: + last_hit = self.get_last_hit() + if last_hit is None: + return False + return tick - 1 < last_hit <= tick - def is_heavy_hit(self, tick: int): + def is_heavy_hit(self, tick: int) -> bool: if not self.is_last_hit(tick): return False else: diff --git a/zsim/sim_progress/Preload/APLModule/APLClass.py b/zsim/sim_progress/Preload/APLModule/APLClass.py index b751f019..667a38bb 100644 --- a/zsim/sim_progress/Preload/APLModule/APLClass.py +++ b/zsim/sim_progress/Preload/APLModule/APLClass.py @@ -27,11 +27,11 @@ class APLClass: def __init__( self, all_apl_unit_list: list, - preload_data: "PreloadData" = None, - sim_instance: "Simulator" = None, + preload_data: "PreloadData | None" = None, + sim_instance: "Simulator | None" = None, ): self.game_state: dict | None = None - self.sim_instance: "Simulator" = sim_instance + self.sim_instance: "Simulator | None" = sim_instance self.preload_data = preload_data self.actions_list = all_apl_unit_list self.na_manager_dict: dict[int, BaseNAManager] = {} @@ -50,12 +50,18 @@ def execute(self, tick, mode: int) -> tuple[str, int, ActionAPLUnit]: if self.game_state is None: self.get_game_state() if self.apl_operator is None: + assert self.preload_data is not None, "Preload_data is not initialized" + assert self.sim_instance is not None, "Simulator instance is not initialized" + assert self.game_state is not None, "Game state is not initialized" self.apl_operator = APLOperator( self.actions_list, self.game_state, simulator_instance=self.sim_instance, preload_data=self.preload_data, ) + assert self.preload_data is not None + assert self.apl_operator is not None + assert self.preload_data.atk_manager is not None cid, skill_tag, apl_priority, apl_unit = ( self.apl_operator.spawn_next_action_in_common_mode(tick) if not self.preload_data.atk_manager.attacking @@ -75,7 +81,8 @@ def get_game_state(self) -> dict | None: # if main_module is None: # raise ImportError("Main module not found.") # self.game_state = main_module.game_state # 获取 main 中的 A - self.game_state = self.preload_data.sim_instance.game_state + if self.preload_data and self.preload_data.sim_instance: + self.game_state = self.preload_data.sim_instance.game_state except Exception as e: print(f"Error loading dictionary A: {e}") return self.game_state @@ -85,7 +92,10 @@ def perform_action(self, CID: int, action: str, tick: int) -> str: if self.game_state is None: self.game_state = self.get_game_state() if self.preload_data is None: - self.preload_data = self.game_state.get("preload").preload_data + assert self.game_state is not None, "Game state is not initialized" + preload_from_game_state = self.game_state.get("preload") + assert preload_from_game_state is not None, "Preload object not in game_state" + self.preload_data = preload_from_game_state.preload_data output = self.action_processor(CID, action, tick) return output @@ -103,8 +113,10 @@ def action_processor(self, CID, action, tick) -> str: def spawn_action_directly(self, CID, action): """在没有被快速支援、或是其他技能拦截的情况下,直接生成动作""" + assert self.preload_data is not None if action == "auto_NA": if CID not in self.na_manager_dict: + assert self.preload_data.char_data is not None for _char_obj in self.preload_data.char_data.char_obj_list: if _char_obj.CID == CID: self.na_manager_dict[CID] = na_manager_factory(_char_obj) diff --git a/zsim/sim_progress/Preload/APLModule/APLManager.py b/zsim/sim_progress/Preload/APLModule/APLManager.py index 904fc064..29160b92 100644 --- a/zsim/sim_progress/Preload/APLModule/APLManager.py +++ b/zsim/sim_progress/Preload/APLModule/APLManager.py @@ -15,7 +15,7 @@ class APLManager: """APL管理器,用于管理和加载APL代码文件""" - def __init__(self, sim_instance: "Simulator" = None): + def __init__(self, sim_instance: "Simulator | None" = None): self.default_apl_dir = DEFAULT_APL_DIR self.custom_apl_dir = COSTOM_APL_DIR self._ensure_directories() @@ -46,7 +46,7 @@ def get_apl_path(self, name: str) -> Optional[str]: return path return None - def load_apl(self, path: str, mode: int = 0, preload_data: "PreloadData" = None) -> APLClass: + def load_apl(self, path: str, mode: int, preload_data: "PreloadData") -> APLClass: """ 加载并解析APL文件 :param path: APL文件路径 @@ -54,6 +54,7 @@ def load_apl(self, path: str, mode: int = 0, preload_data: "PreloadData" = None) :param preload_data: 外部传入的Preload_data :return: 已初始化的APLClass实例 """ + return APLClass( APLParser(file_path=path).parse(mode=mode), preload_data=preload_data, diff --git a/zsim/sim_progress/Preload/APLModule/ActionReplaceManager.py b/zsim/sim_progress/Preload/APLModule/ActionReplaceManager.py index 9af0ef22..2152ebc2 100644 --- a/zsim/sim_progress/Preload/APLModule/ActionReplaceManager.py +++ b/zsim/sim_progress/Preload/APLModule/ActionReplaceManager.py @@ -1,10 +1,11 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING + from zsim.define import ENEMY_ATTACK_REPORT from zsim.models.event_enums import ListenerBroadcastSignal as LBS +from zsim.sim_progress.data_struct.QuickAssistSystem import QuickAssistManager if TYPE_CHECKING: - from zsim.sim_progress.Character.character import Character from zsim.sim_progress.data_struct.EnemyAttackEvent import EnemyAttackEventManager from zsim.sim_progress.Preload import SkillNode from zsim.sim_progress.Preload.PreloadDataClass import PreloadData @@ -25,6 +26,7 @@ def __init__(self, preload_data): def action_replace_factory(self, CID: int, action: str, tick: int) -> tuple[bool, str]: """该函数主要用于拦截APL的主动动作,使其被其他动作替代,用来模拟各种特殊情况""" """如果目前正处于黄光阶段(窗口期),那么此时的所有切人动作都会被无条件换成格挡,哪怕此时快支正处于激活状态""" + assert self.preload_data.sim_instance is not None if "耀嘉音" in self.preload_data.sim_instance.init_data.name_box: """有耀嘉音时,优先检测快支替换""" if self.quick_assist_strategy.condition_judge(CID=CID, action=action, tick=tick): @@ -57,21 +59,21 @@ def action_replace_factory(self, CID: int, action: str, tick: int) -> tuple[bool return False, action class __BaseStrategy(ABC): - def __init__(self, preload_data): + def __init__(self, preload_data: "PreloadData"): self.preload_data: "PreloadData" = preload_data @abstractmethod - def condition_judge(self, *args, **kwargs): + def condition_judge(self, *args, **kwargs) -> bool | str: pass @abstractmethod - def spawn_new_action(self, *args, **kwargs): + def spawn_new_action(self, *args, **kwargs) -> str: pass class QuickAssistStrategy(__BaseStrategy): def __init__(self, preload_data): super().__init__(preload_data) - self.manager_box = {} + self.manager_box: dict[int, "QuickAssistManager"] = {} def condition_judge(self, CID: int, tick: int, action: str, *args, **kwargs) -> bool: """ @@ -108,7 +110,7 @@ def condition_judge(self, CID: int, tick: int, action: str, *args, **kwargs) -> else: return False - def spawn_new_action(self, CID: int, action: str): + def spawn_new_action(self, CID: int, action: str) -> str: if action == "wait": return action for _obj in self.preload_data.skills: @@ -168,7 +170,11 @@ def condition_judge(self, CID: int, tick: int, action: str, *args, **kwargs) -> """ if self.knock_back_signal: return "knock_back" + atk_manager = self.preload_data.atk_manager + if atk_manager is None: + return False + char_on_field_cid = self.preload_data.operating_now if not atk_manager.attacking and not self.knock_back_signal: return False @@ -187,6 +193,7 @@ def condition_judge(self, CID: int, tick: int, action: str, *args, **kwargs) -> # 命中次数不为0的情况,主要分为两种:【连续招架】以及【单次招架的后续】。 if self.consecutive_parry_mode: # 若连续招架的开关打开,那么无需任何函数判定,都放行并且返回【连续招架】的更新信号 + assert atk_manager.action is not None if atk_manager.hitted_count == atk_manager.action.hit - 1: return "final_parry" else: @@ -203,7 +210,7 @@ def __first_parry_condition_judge( CID: int, tick: int, action: str, - char_on_field_cid: int, + char_on_field_cid: int | None, ) -> bool: """ 判断当前情况是否满足第一次招架。条件有: @@ -218,7 +225,12 @@ def __first_parry_condition_judge( return False # 条件2检查——角色有招架能力 - char_obj: "Character" = self.preload_data.sim_instance.char_data.find_char_obj(CID=CID) + if self.preload_data.sim_instance is None: + return False + char_obj = self.preload_data.sim_instance.char_data.find_char_obj(CID=CID) + if char_obj is None: + return False + if char_obj.aid_type != "招架": if "parry" in action.lower(): raise ValueError( @@ -232,13 +244,16 @@ def __first_parry_condition_judge( else: return True - def spawn_new_action(self, CID: int, action: str, tick: int, *args, **kwargs): + def spawn_new_action(self, CID: int, action: str, tick: int, *args, **kwargs) -> str: """根据当前的状态,执行招架支援tag的替换。""" - atk_manager = self.preload_data.atk_manager replace_signal: bool | str = self.condition_judge(CID=CID, action=action, tick=tick) # 若条件判断返回的是False,则说明不执行替换,返回原始tag。 if not replace_signal: return action + + atk_manager = self.preload_data.atk_manager + assert atk_manager is not None + if replace_signal == "first_parry": return self.__first_parry_replace_handler( atk_manager=atk_manager, CID=CID, action=action, tick=tick @@ -254,19 +269,22 @@ def spawn_new_action(self, CID: int, action: str, tick: int, *args, **kwargs): else: raise ValueError(f"无法识别的替换信号:{replace_signal}!") - def spawn_parry_aid_tag(self, CID: int, mode: int): + def spawn_parry_aid_tag(self, CID: int, mode: int) -> str: if mode == 0: return f"{CID}_Light_parry_Aid" elif mode == 1: + assert self.consecutive_parry_node is not None return ( f"{self.consecutive_parry_node.skill_tag.strip().split('_')[0]}_Chain_parry_Aid" ) elif mode == 2: + assert self.consecutive_parry_node is not None return ( f"{self.consecutive_parry_node.skill_tag.strip().split('_')[0]}_Heavy_parry_Aid" ) elif mode == 3: """这是衔接在招架支援后的后退动作,无法取消。""" + assert self.final_parry_node is not None return f"{self.final_parry_node.skill_tag.strip().split('_')[0]}_knock_back_cause_parry" else: raise ValueError(f"不支持的招架模式:{mode}!") @@ -277,11 +295,13 @@ def __first_parry_replace_handler( CID: int, action: str, tick: int, - ): + ) -> str: """负责处理“首次招架”分支的tag替换业务""" # 注意,执行本函数的情况,正常情况下总是符合时间窗口的(APL和前方的信号函数都已经处理过这个逻辑了 if not atk_manager.is_in_response_window(tick=tick): raise ValueError("首次招架的技能标签替换失败,因为当前时间窗口已经过期!") + if atk_manager.action is None: + raise ValueError("atk_manager.action is None, but it should not be.") if atk_manager.action.hit_type in ["Light", "Chain"]: return self.spawn_parry_aid_tag(CID=CID, mode=0) elif atk_manager.action.hit_type == "Heavy": @@ -295,7 +315,7 @@ def __consecutive_parry_replace_handler( CID: int, action: str, tick: int, - ): + ) -> str: """ 负责处理“连续招架”分支的tag替换业务 当处于连续招架的情况下,应该在窗口合法时,第一时间抛出连续招架, @@ -303,12 +323,12 @@ def __consecutive_parry_replace_handler( """ settled_tick = tick + self.chain_parry_tick - if atk_manager.hit_check(settled_tick): + if atk_manager.hit_check(int(settled_tick)): return self.spawn_parry_aid_tag(CID=CID, mode=1) else: return "wait" - def __knock_back_replace_handler(self, CID: int): + def __knock_back_replace_handler(self, CID: int) -> str: """ 负责处理“KnockBack”分支的tag替换业务 该分支的职能是:准确识别“击退信号”并且抛出“击退”动作。 @@ -326,12 +346,13 @@ def __final_parry_replace_handler(self, CID: int, atk_manager: "EnemyAttackEvent 该分支的职能是:复核验证最后一击的判定结果, 同时根据本次进攻信号的数量,抛出对应的tag """ + assert atk_manager.action is not None if atk_manager.hitted_count > atk_manager.action.hit - 1: raise ValueError( "当前已结算次数与进攻信号的命中次数不匹配!这种情况理应不该送入“final_parry”分支,请检查函数逻辑。" ) if atk_manager.action.hit < 3: - return self.spawn_parry_aid_tag(CID=0, mode=1) + return self.spawn_parry_aid_tag(CID=CID, mode=1) else: return self.spawn_parry_aid_tag(CID=CID, mode=2) @@ -340,28 +361,34 @@ def update_myself(self, skill_node: "SkillNode", tick: int): if skill_node.skill.trigger_buff_level not in [7, 8, 9]: if "knock_back" not in skill_node.skill_tag: return + if "Light_parry" in skill_node.skill_tag: self.parry_interaction_in_progress = True if ENEMY_ATTACK_REPORT: - self.preload_data.sim_instance.schedule_data.change_process_state() + if self.preload_data.sim_instance: + self.preload_data.sim_instance.schedule_data.change_process_state() print( f"检测到来自于{skill_node.char_name}的招架技能{skill_node.skill_tag}!招架交互开始!" ) + assert self.preload_data.atk_manager is not None + assert self.preload_data.atk_manager.action is not None if self.preload_data.atk_manager.action.hit > 1: self.consecutive_parry_mode = True self.consecutive_parry_node = skill_node print("当前进攻事件是多次命中的,所以开启连续招架模式!") elif "knock_back" in skill_node.skill_tag: if ENEMY_ATTACK_REPORT: - self.preload_data.sim_instance.schedule_data.change_process_state() + if self.preload_data.sim_instance: + self.preload_data.sim_instance.schedule_data.change_process_state() print(f"角色 {skill_node.char_name} 因招架而被击退!") self.knock_back_signal = False self.final_parry_node = None self.parry_interaction_in_progress = False self.assault_aid_enable = True self.assault_aid_disable_tick = tick + 60 - listener_manager = self.preload_data.sim_instance.listener_manager - listener_manager.broadcast_event(event=skill_node, signal=LBS.PARRY) + if self.preload_data.sim_instance: + listener_manager = self.preload_data.sim_instance.listener_manager + listener_manager.broadcast_event(event=skill_node, signal=LBS.PARRY) elif "Assault_Aid" in skill_node.skill_tag: if tick > self.assault_aid_disable_tick: raise ValueError( @@ -370,12 +397,15 @@ def update_myself(self, skill_node: "SkillNode", tick: int): self.assault_aid_enable = False self.assault_aid_disable_tick = tick if ENEMY_ATTACK_REPORT: - self.preload_data.sim_instance.schedule_data.change_process_state() + if self.preload_data.sim_instance: + self.preload_data.sim_instance.schedule_data.change_process_state() print(f"角色 {skill_node.char_name} 在招架完成后释放支援突击!") elif any( [_sub_tag in skill_node.skill_tag for _sub_tag in ["Chain_parry", "Heavy_parry"]] ): # 在检测连续招架或是重招架时,必须检测当前的命中次数,确保正确关闭连续招架状态。 + assert self.preload_data.atk_manager is not None + assert self.preload_data.atk_manager.action is not None if ( self.preload_data.atk_manager.hitted_count == self.preload_data.atk_manager.action.hit diff --git a/zsim/sim_progress/Preload/APLModule/SubConditionUnit/StatusSubUnit.py b/zsim/sim_progress/Preload/APLModule/SubConditionUnit/StatusSubUnit.py index 1168c49f..2c491501 100644 --- a/zsim/sim_progress/Preload/APLModule/SubConditionUnit/StatusSubUnit.py +++ b/zsim/sim_progress/Preload/APLModule/SubConditionUnit/StatusSubUnit.py @@ -52,7 +52,10 @@ def __init__(self, element_type_1: int, element_type_2: int): self.element_type_2 = element_type_2 def handler(self, enemy): - result = enemy.anomaly_bars_dict[self.element_type_1].get_buildup_pct() - enemy.anomaly_bars_dict[self.element_type_2].get_buildup_pct() + result = ( + enemy.anomaly_bars_dict[self.element_type_1].get_buildup_pct() + - enemy.anomaly_bars_dict[self.element_type_2].get_buildup_pct() + ) return result class StunPctHandler(CheckHandler): @@ -170,7 +173,7 @@ def handler(cls, enemy): "is_corruption": CorruptionHandler, "quick_assist_available": QuickAssistHandler, "assist_waiting_for_anwser": WaitingAssistHandler, - "buildup_pct_delta": BuildupPctHandler + "buildup_pct_delta": BuildupPctHandler, } def check_myself( diff --git a/zsim/sim_progress/Preload/PreloadClass.py b/zsim/sim_progress/Preload/PreloadClass.py index 9867190d..6ec474ad 100644 --- a/zsim/sim_progress/Preload/PreloadClass.py +++ b/zsim/sim_progress/Preload/PreloadClass.py @@ -1,23 +1,27 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Iterable from .PreloadDataClass import PreloadData from .PreloadStrategy import SwapCancelStrategy if TYPE_CHECKING: + from zsim.sim_progress.Character.skill_class import Skill + from zsim.simulator.dataclasses import LoadData from zsim.simulator.simulator_class import Simulator class PreloadClass: def __init__( self, - skills, + skills: Iterable["Skill"], *, - load_data, + load_data: "LoadData", apl_path: str | None = None, - sim_instance: "Simulator" = None, + sim_instance: "Simulator | None" = None, **kwargs, ): - self.preload_data = PreloadData(skills, load_data=load_data, sim_instance=sim_instance) + self.preload_data: "PreloadData" = PreloadData( + skills, load_data=load_data, sim_instance=sim_instance + ) self.apl_path = apl_path self.strategy = SwapCancelStrategy(self.preload_data, apl_path) diff --git a/zsim/sim_progress/Preload/PreloadDataClass.py b/zsim/sim_progress/Preload/PreloadDataClass.py index dfa41fe9..7c19fd09 100644 --- a/zsim/sim_progress/Preload/PreloadDataClass.py +++ b/zsim/sim_progress/Preload/PreloadDataClass.py @@ -1,30 +1,40 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Iterable -from zsim.sim_progress.data_struct import ( - EnemyAttackEventManager, - NodeStack, - QuickAssistSystem, -) - -from . import SkillNode from zsim.models.event_enums import ListenerBroadcastSignal as LBS +from . import SkillNode + if TYPE_CHECKING: + from zsim.sim_progress.Character.skill_class import Skill + from zsim.sim_progress.data_struct import ( + EnemyAttackEventManager, + NodeStack, + QuickAssistSystem, + ) + from zsim.sim_progress.Load.loading_mission import LoadingMission + from zsim.simulator.dataclasses import CharacterData, LoadData from zsim.simulator.simulator_class import Simulator class PreloadData: """循环于Preload阶段内部的数据""" - def __init__(self, skills, sim_instance, **kwargs): - load_data = kwargs.get("load_data") - self.sim_instance: "Simulator" = sim_instance + def __init__( + self, + skills: Iterable["Skill"], + sim_instance: "Simulator | None", + load_data: "LoadData", + **kwargs, + ): + self.sim_instance: "Simulator | None" = sim_instance self.preload_action: list[SkillNode] = [] # 最终return返回给外部申请的数据结构 - from zsim.sim_progress.Character.skill_class import Skill + self.skills: Iterable["Skill"] = ( + skills # 用于创建SkillNode,是SkillNode构造函数的必要参数。 + ) - self.skills: list[Skill] = skills # 用于创建SkillNode,是SkillNode构造函数的必要参数。 + from zsim.sim_progress.data_struct import NodeStack - self.personal_node_stack: dict[int, NodeStack] = {} # 个人的技能栈 - self.current_node_stack: NodeStack = NodeStack(length=5) # Preload阶段的总技能栈 + self.personal_node_stack: dict[int, "NodeStack[SkillNode]"] = {} # 个人的技能栈 + self.current_node_stack: "NodeStack" = NodeStack(length=5) # Preload阶段的总技能栈 self.latest_active_generation_node: SkillNode | None = ( None # 最近一次主动生成的skillnode,#TODO:可能是无用参数! ) @@ -32,11 +42,11 @@ def __init__(self, skills, sim_instance, **kwargs): tuple[str, bool, int] ] = [] # 当前tick需要执行preload的SkillTag列表,列表中的元素是(skill_tag, active_generation),其中,active_generation指的是动作是否是主动生成。 self.name_box: list[str] | None = None - self.char_data = None - self.load_data = load_data - self.load_mission_dict: dict = load_data.load_mission_dict - self.quick_assist_system = None - self.atk_manager: EnemyAttackEventManager | None = None + self.char_data: "CharacterData | None" = None + self.load_data: "LoadData" = load_data + self.load_mission_dict: dict[str, "LoadingMission"] = load_data.load_mission_dict + self.quick_assist_system: "QuickAssistSystem | None" = None + self.atk_manager: "EnemyAttackEventManager | None" = None @property def operating_now(self) -> int | None: @@ -48,11 +58,14 @@ def operating_now(self) -> int | None: def push_node_in_swap_cancel(self, node: SkillNode, tick: int): """合轴模式中的内部数据更新函数。将构造好的SkillNode加入preload_action中,同时更新Preload板块的内部数据。""" + assert self.sim_instance is not None self.check_myself_before_push_node() self.preload_action.append(node) char_cid = int(node.skill_tag.split("_")[0]) self.current_node_stack.push(node) if char_cid not in self.personal_node_stack: + from zsim.sim_progress.data_struct import NodeStack + self.personal_node_stack[char_cid] = NodeStack(length=3) if not self.personal_node_stack[char_cid].last_node_is_end(tick): @@ -70,20 +83,24 @@ def push_node_in_swap_cancel(self, node: SkillNode, tick: int): if node.active_generation: self.latest_active_generation_node = node if self.quick_assist_system is None: + assert self.char_data is not None + from zsim.sim_progress.data_struct import QuickAssistSystem + self.quick_assist_system = QuickAssistSystem( self.char_data.char_obj_list, sim_instance=self.sim_instance ) self.quick_assist_system.update(tick, node, self.load_data.all_name_order_box) - if self.atk_manager.attacking: + if self.atk_manager is not None and self.atk_manager.attacking: self.atk_manager.answered_action.append(node) from zsim.sim_progress.Preload.APLModule.ActionReplaceManager import ( ActionReplaceManager, ) - action_replace_manager: ActionReplaceManager = ( + action_replace_manager: "ActionReplaceManager | None" = ( self.sim_instance.preload.strategy.apl_engine.apl.action_replace_manager ) - action_replace_manager.parry_aid_strategy.update_myself(skill_node=node, tick=tick) + if action_replace_manager is not None: + action_replace_manager.parry_aid_strategy.update_myself(skill_node=node, tick=tick) def check_myself_before_push_node(self): """Confirm阶段自检""" @@ -124,7 +141,9 @@ def reset_myself(self, name_box): def force_change_action(self, skill_node: SkillNode): """强制更新动作,用于技能强制顶替、被打断或是类似场合""" char_cid = int(skill_node.skill_tag.strip().split("_")[0]) - node_be_changed: SkillNode = self.personal_node_stack[char_cid].peek() + node_be_changed: "SkillNode | None" = self.personal_node_stack[char_cid].peek() + if node_be_changed is None: + raise ValueError if node_be_changed.end_tick <= skill_node.preload_tick: raise ValueError( f"尝试用{skill_node.skill_tag}来强制替换{node_be_changed.skill_tag},但是后者已经于{node_be_changed.end_tick}结束,这种情况不用调用强制替换方法。请检查调用逻辑。" @@ -136,9 +155,9 @@ def force_change_action(self, skill_node: SkillNode): f"{skill_node.skill_tag}正在尝试顶替一个最高优先级的技能:{node_be_changed.skill_tag}" ) - def delete_mission_in_preload_data(self, node_be_changed): + def delete_mission_in_preload_data(self, node_be_changed: "SkillNode"): """在PreloadData中强制干涉Load阶段,并且执行特定任务的删除。""" - mission_key_to_remove = [] + mission_key_to_remove: list[str] = [] for mission_key, mission in self.load_mission_dict.items(): from zsim.sim_progress.Load import LoadingMission diff --git a/zsim/sim_progress/Preload/PreloadEngine/APLEngine.py b/zsim/sim_progress/Preload/PreloadEngine/APLEngine.py index 564c9ac6..1f4fec94 100644 --- a/zsim/sim_progress/Preload/PreloadEngine/APLEngine.py +++ b/zsim/sim_progress/Preload/PreloadEngine/APLEngine.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING -from zsim.define import APL_PATH, APL_THOUGHT_CHECK, APL_THOUGHT_CHECK_WINDOW as ATCW +from zsim.define import APL_PATH, APL_THOUGHT_CHECK +from zsim.define import APL_THOUGHT_CHECK_WINDOW as ATCW from .. import SkillNode, SkillsQueue from ..APLModule import APLManager @@ -13,10 +14,12 @@ class APLEngine(BasePreloadEngine): """用于调动APL模块的Preload引擎""" - def __init__(self, data, apl_path: str | None = None, preload_data: "PreloadData" = None): + def __init__(self, data: "PreloadData", apl_path: str | None = None): super().__init__(data) - self.preload_data = preload_data + self.preload_data = data self.sim_instance = self.preload_data.sim_instance + if self.sim_instance is None: + raise ValueError("APLEngine requires a sim_instance to be provided.") self.apl_manager = APLManager(sim_instance=self.sim_instance) if apl_path is None: @@ -28,13 +31,16 @@ def __init__(self, data, apl_path: str | None = None, preload_data: "PreloadData apl_path = found_path self.apl = self.apl_manager.load_apl(apl_path, mode=0, preload_data=self.preload_data) + self.latest_node: SkillNode | None = None def run_myself(self, tick) -> SkillNode | None: """APL模块运行的最终结果:技能名、最终通过的APL代码优先级""" skill_tag, apl_priority, apl_unit = self.apl.execute(tick, mode=0) if APL_THOUGHT_CHECK: if tick in range(ATCW[0], ATCW[1]): - print(f"当前tick为:{tick},APL引擎在本tick想要运行的技能是:{skill_tag},该技能来自于优先级为 {apl_priority} 的APL单元(属于角色 {apl_unit.char_CID})") + print( + f"当前tick为:{tick},APL引擎在本tick想要运行的技能是:{skill_tag},该技能来自于优先级为 {apl_priority} 的APL单元(属于角色 {apl_unit.char_CID})" + ) if skill_tag == "wait": return None node = SkillsQueue.spawn_node( @@ -49,7 +55,7 @@ def run_myself(self, tick) -> SkillNode | None: def reset_myself(self): """APL模块暂时没有任何需要Reset的地方!""" - pass + self.latest_node = None def get_available_apls(self) -> list[str]: """获取所有可用的APL文件列表""" diff --git a/zsim/sim_progress/Preload/PreloadEngine/AttackAnswerEngine.py b/zsim/sim_progress/Preload/PreloadEngine/AttackAnswerEngine.py index d4fde9bc..9d94d181 100644 --- a/zsim/sim_progress/Preload/PreloadEngine/AttackAnswerEngine.py +++ b/zsim/sim_progress/Preload/PreloadEngine/AttackAnswerEngine.py @@ -1,7 +1,5 @@ from typing import TYPE_CHECKING -from zsim.sim_progress.data_struct import EnemyAttackEventManager - from .BasePreloadEngine import BasePreloadEngine if TYPE_CHECKING: @@ -16,16 +14,19 @@ class AttackResponseEngine(BasePreloadEngine): """进攻响应引擎,主要负责敌人进攻动作抛出,以及角色动作响应相关的内容;""" - def __init__(self, data, sim_instance: "Simulator" = None): + def __init__(self, data: "PreloadData", sim_instance: "Simulator | None" = None): + assert sim_instance is not None super().__init__(data) self.data: "PreloadData" = data self.game_state = None self.found_char_dict: dict[int, "Character"] = {} self.enemy: "Enemy | None" = None - self.sim_instance: "Simulator | None" = sim_instance + self.sim_instance: "Simulator" = sim_instance - def run_myself(self, tick: int, *args, **kwargs) -> None: + def run_myself(self, tick: int, *args, **kwargs) -> bool: if self.data.atk_manager is None: + from zsim.sim_progress.data_struct import EnemyAttackEventManager + self.data.atk_manager = EnemyAttackEventManager( enemy_instance=self.sim_instance.schedule_data.enemy ) @@ -41,19 +42,24 @@ def run_myself(self, tick: int, *args, **kwargs) -> None: """每次运行,都要让atk_manager自检一次,以更新状态。""" self.data.atk_manager.check_myself(tick=tick) + return True def try_spawn_enemy_attack(self) -> "EnemyAttackAction | None": """调用Enemy对象下的进攻模组,并且生成一次攻击,同时打包成事件存入本地""" - if self.sim_instance is None: + if self.sim_instance is None and self.data.sim_instance is not None: self.sim_instance = self.data.sim_instance if self.enemy is None: self.enemy = self.sim_instance.schedule_data.enemy - if not self.enemy.attack_method.active: - return None - if self.enemy.dynamic.stun: - return None - if self.data.atk_manager.interruption_recovery_check(tick=self.sim_instance.tick): + if ( + not self.enemy.attack_method.active + or self.enemy.dynamic.stun + or ( + self.data.atk_manager is not None + and self.data.atk_manager.interruption_recovery_check(tick=self.sim_instance.tick) + ) + ): return None + if self.enemy.attack_method.random_attack: enemy_attack_action = self.enemy.attack_method.probablity_driven_action_selection( current_tick=self.sim_instance.tick diff --git a/zsim/sim_progress/Preload/PreloadEngine/BasePreloadEngine.py b/zsim/sim_progress/Preload/PreloadEngine/BasePreloadEngine.py index 0d9ac797..5b5f45b4 100644 --- a/zsim/sim_progress/Preload/PreloadEngine/BasePreloadEngine.py +++ b/zsim/sim_progress/Preload/PreloadEngine/BasePreloadEngine.py @@ -1,7 +1,5 @@ from abc import ABC, abstractmethod -from typing import TYPE_CHECKING - -from zsim.sim_progress.data_struct import NodeStack +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: from .. import PreloadData @@ -14,19 +12,8 @@ def __init__(self, data: "PreloadData"): self.active_signal = False # 用于记录当前引擎在当前tick是否运行过。 @abstractmethod - def run_myself(self, *args, **kwargs): - pass + def run_myself(self, *args, **kwargs) -> Any: + return False -if __name__ == "__main__": - node_stack = NodeStack(3) - a = 1 - b = 2 - c = 3 - node_stack.push(a) - node_stack.push(b) - node_stack.push(c) - print(node_stack) - d = node_stack.peek() - e = node_stack.pop() - print(d, e, node_stack) +__all__ = ["BasePreloadEngine"] diff --git a/zsim/sim_progress/Preload/PreloadEngine/ConfirmEngine.py b/zsim/sim_progress/Preload/PreloadEngine/ConfirmEngine.py index df9ca60d..d32da2ac 100644 --- a/zsim/sim_progress/Preload/PreloadEngine/ConfirmEngine.py +++ b/zsim/sim_progress/Preload/PreloadEngine/ConfirmEngine.py @@ -5,6 +5,7 @@ from .. import SkillNode, SkillsQueue from ..PreloadEngine import BasePreloadEngine from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: from zsim.sim_progress.Character import Character from zsim.sim_progress.Preload.PreloadDataClass import PreloadData @@ -24,7 +25,7 @@ def __init__(self, data: "PreloadData"): self.external_add_skill_list = [] self.validators = [self._validate_timing] - def run_myself(self, tick: int, **kwargs): + def run_myself(self, tick: int, **kwargs) -> bool: """依次执行 Node构造、验证、内外部数据交互""" apl_skill_node: SkillNode | None = kwargs.get("apl_skill_node", None) apl_skill_tag = kwargs.get("apl_skill_tag", None) @@ -50,6 +51,7 @@ def run_myself(self, tick: int, **kwargs): # ) else: pass + return True def spawn_node_from_tag( self, @@ -76,22 +78,27 @@ def spawn_node_from_tag( def update_external_data(self, node: SkillNode, tick: int): """与外部数据交互,主要是和char进行交互。""" - for char in self.data.char_data.char_obj_list: - char.update_sp_and_decibel(node) - char.special_resources(node, tick=tick) - char.dynamic.lasting_node.update_node(node, tick) + if self.data.char_data: + for char in self.data.char_data.char_obj_list: + char.update_sp_and_decibel(node) + char.special_resources(node, tick=tick) + char.dynamic.lasting_node.update_node(node, tick) # 切人逻辑 name_box = self.data.name_box if ( isinstance(name_box, list) and all(isinstance(name, str) for name in name_box) and node.active_generation + and self.data.char_data is not None ): self.switch_char(node, self.data.char_data) - self.data.sim_instance.decibel_manager.update(skill_node=node) + if self.data.sim_instance: + self.data.sim_instance.decibel_manager.update(skill_node=node) def switch_char(self, this_node: SkillNode, char_data) -> None: name_box = self.data.name_box + if name_box is None: + return old_name_box = name_box.copy() name_index = name_box.index(this_node.char_name) # 更改前台角色(切人逻辑) @@ -108,9 +115,10 @@ def switch_char(self, this_node: SkillNode, char_data) -> None: if name_box[0] == char.NAME: if name_box[0] != old_name_box[0]: """在更新name_box的时候,将切人事件对所有监听器进行广播。""" - self.data.sim_instance.listener_manager.broadcast_event( - event=char, signal=LBS.SWITCHING_IN, skill_node=this_node - ) + if self.data.sim_instance: + self.data.sim_instance.listener_manager.broadcast_event( + event=char, signal=LBS.SWITCHING_IN, skill_node=this_node + ) char.dynamic.on_field = True else: char.dynamic.on_field = False diff --git a/zsim/sim_progress/Preload/PreloadEngine/ForceAddEngine.py b/zsim/sim_progress/Preload/PreloadEngine/ForceAddEngine.py index 3288afec..5af030b1 100644 --- a/zsim/sim_progress/Preload/PreloadEngine/ForceAddEngine.py +++ b/zsim/sim_progress/Preload/PreloadEngine/ForceAddEngine.py @@ -16,10 +16,12 @@ def __init__(self, data): self.game_state = None self.found_char_dict = {} - def run_myself(self, tick: int): + def run_myself(self, tick: int) -> bool: """当每个node结束时,都应该调用这个函数来判断强制添加。""" self.active_signal = False - for cid, stack in self.data.personal_node_stack.items(): + if not self.data.personal_node_stack: + return True + for _, stack in self.data.personal_node_stack.items(): node: SkillNode | None node = stack.peek() if node is None: @@ -27,7 +29,7 @@ def run_myself(self, tick: int): if node.end_tick > tick: """如果当前node并未结束,则不符合“node”结束的条件,则应该直接跳过。""" continue - follow_up: list = node.skill.follow_up + follow_up: list[str] = node.skill.follow_up if not follow_up: continue conditions_unit: list = node.skill.force_add_condition_APL @@ -42,30 +44,35 @@ def run_myself(self, tick: int): self.check_char(follow_up, index, node) # 检验数据的正确性 self.data.preload_action_list_before_confirm.append((follow_up[index], False, 0)) self.active_signal = True + return True - def check_char(self, follow_up, index, node): + def check_char(self, follow_up: list, index: int, node: SkillNode): """ 该函数的作用:确保:B角色技能在强制预载时,并没有动作存在即可。 如果程序流程合理,这个函数是不会被执行的。 """ follow_up_skill_CID = int(follow_up[index][:4]) follow_up_skill_add_tick = node.end_tick + if self.data.personal_node_stack is None: + return followed_char_stack = self.data.personal_node_stack.get(follow_up_skill_CID, None) if followed_char_stack is not None: - if node.end_tick < followed_char_stack.peek().end_tick: + latest_node: SkillNode | None = followed_char_stack.peek() + if latest_node is not None and node.end_tick < latest_node.end_tick: raise ValueError( f"出现了不应该出现的情况!技能{follow_up[index]}理应在{node.skill_tag}之后、于{follow_up_skill_add_tick}执行,但是此时角色{follow_up_skill_CID}尚有动作存在。" ) def prcoess_force_add_apl( - self, conditions_unit, sim_instance: "Simulator", **kwargs + self, conditions_unit, sim_instance: "Simulator | None", **kwargs ) -> tuple[bool, int]: """强制添加动作的前置判定,有APL模块则运行模块,无APL模块则直接通过。""" should_force_add = True index = 0 tick = kwargs.get("tick", None) if conditions_unit and self.game_state is None: - self.game_state = get_game_state(sim_instance=sim_instance) + if sim_instance: + self.game_state = get_game_state(sim_instance=sim_instance) if conditions_unit: """存在条件类APL判定""" for unit in conditions_unit: diff --git a/zsim/sim_progress/Preload/PreloadEngine/SwapCancelValidateEngine.py b/zsim/sim_progress/Preload/PreloadEngine/SwapCancelValidateEngine.py index cfb96cbf..c51afc3d 100644 --- a/zsim/sim_progress/Preload/PreloadEngine/SwapCancelValidateEngine.py +++ b/zsim/sim_progress/Preload/PreloadEngine/SwapCancelValidateEngine.py @@ -104,7 +104,7 @@ def run_myself( return True def _validate_char_avaliable( - self, skill_tag: str, apl_skill_node: SkillNode, tick: int + self, skill_tag: str, apl_skill_node: SkillNode | None, tick: int ) -> bool: """角色是否可以获取的判定""" cid = int(skill_tag.split("_")[0]) @@ -135,7 +135,11 @@ def _validate_char_avaliable( """对于衔接于招架之后的击退,要立即放行""" return True """正在进行的技能并非立即执行类型,而新的技能是立即执行类型,则放行""" - if apl_skill_node.skill.do_immediately and not char_latest_node.skill.do_immediately: + if ( + apl_skill_node is not None + and apl_skill_node.skill.do_immediately + and not char_latest_node.skill.do_immediately + ): return True else: return False @@ -186,7 +190,7 @@ def _validate_swap_tick(self, skill_tag: str, tick: int, **kwargs): ) return True - def _validate_qte_activation(self, tick: int, skill_node: SkillNode) -> bool: + def _validate_qte_activation(self, tick: int, skill_node: SkillNode | None) -> bool: """针对当前技能的QTE是否处于激活状态的检测,当检查到有角色正在释放QTE时,返回True""" # enemy = self.data.sim_instance.schedule_data.enemy # if enemy.qte_manager.qte_data.single_qte is not None: @@ -196,7 +200,7 @@ def _validate_qte_activation(self, tick: int, skill_node: SkillNode) -> bool: if stack.peek() is None: continue node_now = stack.peek() - if node_now.end_tick > tick: + if node_now is not None and node_now.end_tick > tick: if "QTE" in node_now.skill_tag: # FIXME: 由于伊芙琳的QTE是可以进行合轴的,这里一定会遇到Bug。 return True @@ -204,7 +208,7 @@ def _validate_qte_activation(self, tick: int, skill_node: SkillNode) -> bool: else: return False - def _validate_wait_event(self, apl_skill_tag: str = None) -> bool: + def _validate_wait_event(self, apl_skill_tag: str | None = None) -> bool: """用于检测传入的apl动作是否为wait。""" if apl_skill_tag == "wait": return True @@ -212,13 +216,15 @@ def _validate_wait_event(self, apl_skill_tag: str = None) -> bool: return False def _validate_char_task_conflict( - self, skill_tag: str, apl_skill_node: SkillNode, tick: int + self, skill_tag: str, apl_skill_node: SkillNode | None, tick: int ) -> bool: """ 针对角色自身的任务冲突的检测——尽管角色当前tick有空 但并不意味着apl抛出的动作就可以直接执行。 APL抛出的动作还需要和角色自身的任务进行冲突检测,相互竞争和覆盖。 """ + if apl_skill_node is None: + return True cid = int(skill_tag.split("_")[0]) for _tuples in self.data.preload_action_list_before_confirm: _tuples: tuple[str, bool, int] @@ -247,7 +253,10 @@ def _validate_char_task_conflict( skill_info = obj.get_skill_info(skill_tag=_tag, attr_info="labels") if skill_info is None: skill_info = {} - if "additional_damage" not in skill_info.keys(): + if ( + not isinstance(skill_info, dict) + or "additional_damage" not in skill_info + ): """但若当前tick被force_add 添加的skill_tag只是个普通技能,那么就要执行顶替。""" print( f"即将添加的衔接技能:{_tuples}被{skill_tag}顶替!" @@ -262,7 +271,9 @@ def _validate_char_task_conflict( else: return True - def _validate_swap_state_check(self, tick: int, skill_tag: str, apl_skill_node: SkillNode): + def _validate_swap_state_check( + self, tick: int, skill_tag: str, apl_skill_node: SkillNode | None + ): """检查角色当前的状态是否允许当前技能进行合轴""" cid = int(skill_tag.split("_")[0]) node_on_field: SkillNode | None = self.data.get_on_field_node(tick) @@ -303,6 +314,8 @@ def _validate_swap_state_check(self, tick: int, skill_tag: str, apl_skill_node: """当前角色的切人CD已经冷却完毕,则直接放行。""" return True else: + if apl_skill_node is None: + return False if ( any([_sub_tag in skill_tag for _sub_tag in ["QTE", "Aid", "knock_back"]]) or apl_skill_node.skill.do_immediately @@ -387,10 +400,11 @@ def _swap_cancel_debug_print( f"当前tick不满足{skill_tag}合轴所需的时间!" ) if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL else None elif mode == 5: - print( - f"{skill_tag}的上一个主动动作{last_actively_generated_node.skill.skill_tag}的APL策略为不要合轴!" - ) if not skill_compare else print( - f"{skill_tag}的上一个主动动作{last_actively_generated_node.skill.skill_tag}的APL策略为不要合轴!" - ) if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL else None + if last_actively_generated_node: + print( + f"{skill_tag}的上一个主动动作{last_actively_generated_node.skill.skill_tag}的APL策略为不要合轴!" + ) if not skill_compare else print( + f"{skill_tag}的上一个主动动作{last_actively_generated_node.skill.skill_tag}的APL策略为不要合轴!" + ) if skill_tag == SWAP_CANCEL_DEBUG_TARGET_SKILL else None else: raise ValueError("mode参数错误!") diff --git a/zsim/sim_progress/Preload/PreloadStrategy.py b/zsim/sim_progress/Preload/PreloadStrategy.py index 002e7ade..f4e37569 100644 --- a/zsim/sim_progress/Preload/PreloadStrategy.py +++ b/zsim/sim_progress/Preload/PreloadStrategy.py @@ -1,4 +1,6 @@ from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS from zsim.sim_progress.Preload.PreloadEngine import ( APLEngine, @@ -7,9 +9,10 @@ ForceAddEngine, SwapCancelValidateEngine, ) -from typing import TYPE_CHECKING if TYPE_CHECKING: + from zsim.simulator.simulator_class import Simulator + from .PreloadDataClass import PreloadData @@ -18,7 +21,7 @@ class BasePreloadStrategy(ABC): def __init__(self, data, apl_path): self.data: "PreloadData" = data - self.apl_engine = APLEngine(data, apl_path=apl_path, preload_data=self.data) + self.apl_engine = APLEngine(data, apl_path=apl_path) self.force_add_engine = ForceAddEngine(data) self.confirm_engine = ConfirmEngine(data) self.finish_post_init: bool = False # 是否完成了后置初始化 @@ -49,6 +52,7 @@ def generate_actions(self, enemy, tick: int) -> None: """合轴逻辑""" # 0、自检 self.check_myself(enemy, tick) + assert self.data.sim_instance is not None self.data.sim_instance.schedule_data.enemy.special_state_manager.broadcast_and_update( signal=SSUS.BEFORE_PRELOAD ) @@ -85,7 +89,6 @@ def generate_actions(self, enemy, tick: int) -> None: tick, apl_skill_node=apl_skill_node, apl_skill_tag=apl_skill_tag ) - def check_myself(self, enemy, tick, *args, **kwargs): """准备工作""" if not self.finish_post_init: @@ -98,8 +101,7 @@ def reset_myself(self): def post_init_all_object(self): """后置初始化所有数据""" - from ...simulator.simulator_class import Simulator - + assert self.data.sim_instance is not None sim_insatnce: Simulator = self.data.sim_instance for char_obj in sim_insatnce.char_data.char_obj_list: char_obj.POST_INIT_DATA(sim_insatnce=sim_insatnce) diff --git a/zsim/sim_progress/Preload/SkillsQueue.py b/zsim/sim_progress/Preload/SkillsQueue.py index ad4d14bc..5ec41213 100644 --- a/zsim/sim_progress/Preload/SkillsQueue.py +++ b/zsim/sim_progress/Preload/SkillsQueue.py @@ -1,13 +1,18 @@ import threading import uuid +from typing import TYPE_CHECKING, Iterable import pandas as pd -from zsim.define import APL_MODE, ElementType, ELEMENT_TYPE_MAPPING as etm +from zsim.define import APL_MODE, ElementType +from zsim.define import ELEMENT_TYPE_MAPPING as ETM from zsim.sim_progress.Character.skill_class import Skill from zsim.sim_progress.data_struct.LinkedList import LinkedList from zsim.sim_progress.Report import report_to_log +if TYPE_CHECKING: + from zsim.sim_progress.Load import LoadingMission + class SkillNode: _instance_counter = 0 @@ -56,7 +61,7 @@ def __init__( tick_list.append(tick_key) self.tick_list = tick_list - self.loading_mission = None + self.loading_mission: "LoadingMission | None" = None self._effective_anomaly_buildup: bool = True self._element_type_change: ElementType | None = None self.force_qte_trigger: bool = False @@ -70,15 +75,15 @@ def element_type(self) -> ElementType: return self._element_type_change @property - def element_type_change(self): + def element_type_change(self) -> ElementType | None: """技能的染色""" return self._element_type_change @element_type_change.setter - def element_type_change(self, value: ElementType): + def element_type_change(self, value: ElementType | None): if self._element_type_change is not None: raise ValueError( - f"技能{self.skill_tag}已经被染色为【{etm.get(self.element_type_change)}】属性!不能被重复染色!" + f"技能{self.skill_tag}已经被染色为【{ETM.get(self._element_type_change)}】属性!不能被重复染色!" ) self._element_type_change = value @@ -103,7 +108,7 @@ def have_label(self, label_key: str): """判断当前skill_node是否拥有传入数值的skill_label""" if self.skill.labels is None: return False - if label_key in self.labels.keys(): + if self.labels is not None and label_key in self.labels.keys(): return True else: return False @@ -136,7 +141,7 @@ def is_last_hit(self, tick: int): return tick - 1 < self.tick_list[-1] <= tick -def spawn_node(tag: str, preload_tick: int, skills: list[Skill], **kwargs) -> SkillNode: +def spawn_node(tag: str, preload_tick: int, skills: Iterable[Skill], **kwargs) -> SkillNode: """ 通过输入的tag和preload_tick,直接创建SkillNode。 """ @@ -184,6 +189,7 @@ def get_skills_queue( raise TypeError("输入的技能必须是 Skill 类") skills_queue = LinkedList() # 用于储存技能节点 + preload_skills: pd.Series = pd.Series() try: preload_skills: pd.Series = preload_table["skill_tag"] # 传入的数据必须包含 skill_tag 列 except KeyError: diff --git a/zsim/sim_progress/RandomNumberGenerator/__init__.py b/zsim/sim_progress/RandomNumberGenerator/__init__.py index 5a7e6c55..06fa0bb6 100644 --- a/zsim/sim_progress/RandomNumberGenerator/__init__.py +++ b/zsim/sim_progress/RandomNumberGenerator/__init__.py @@ -24,7 +24,7 @@ def __new__(cls, sim_instance: "Simulator"): cls._instances[sim_instance] = instance return cls._instances[sim_instance] - def __init__(self, sim_instance: "Simulator" = None): + def __init__(self, sim_instance: "Simulator"): """RNG的构造函数,每个进程只执行一次,反复调用构造函数会报错。""" if not hasattr(self, "_initialized"): self.seed: int | None = None @@ -44,15 +44,16 @@ def reseed(self, new_seed: int | None = None): raise ValueError("RNG模块在初始化时,并未传入Simulator对象") if self.sim_instance.in_parallel_mode: - """当多进程模式时,seed的创造应该基于进程的UUID""" - run_turn_uuid: str = self.sim_instance.sim_cfg.run_turn_uuid + # 当多进程模式时,seed的创造应该基于进程的UUID + assert self.sim_instance.sim_cfg is not None + run_turn_uuid: str | None = self.sim_instance.sim_cfg.run_turn_uuid if run_turn_uuid is None: raise ValueError("多进程模式下,sim_cfg中必须存在有效的run_turn_uuid") hashed_uuid = abs(hash(run_turn_uuid)) % (2**63) tick = self.sim_instance.tick new_seed = (hashed_uuid + tick) if new_seed is None else (new_seed + tick) else: - """当单进程模式时,seed的创造应该基于当前的time()返回的结果""" + # 当单进程模式时,seed的创造应该基于当前的time()返回的结果 tick = self.sim_instance.tick if new_seed is None: new_seed = int(time.time() * 1000000) + tick @@ -86,7 +87,7 @@ def normal_from_table(self) -> float: value = self.normal_table[idx] return float(value) - def __deepcopy__(self, memo): + def __deepcopy__(self, memo): # pylint: disable=unused-argument return self # 始终返回现有实例 def __copy__(self): diff --git a/zsim/sim_progress/Report/__init__.py b/zsim/sim_progress/Report/__init__.py index 7752bfea..ca23b970 100644 --- a/zsim/sim_progress/Report/__init__.py +++ b/zsim/sim_progress/Report/__init__.py @@ -6,25 +6,33 @@ from datetime import datetime from typing import TYPE_CHECKING -from .buff_handler import dump_buff_csv, report_buff_to_queue # noqa: F401 -from .log_handler import async_log_writer, log_queue, report_to_log # noqa: F401 -from .result_handler import ( # noqa: F401 +from zsim.define import NORMAL_MODE_ID_JSON + +from .buff_handler import dump_buff_csv, report_buff_to_queue +from .log_handler import async_log_writer, log_queue, report_to_log +from .result_handler import ( async_result_writer, report_dmg_result, result_queue, ) -from zsim.define import NORMAL_MODE_ID_JSON +__all__ = [ + "report_buff_to_queue", + "report_to_log", + "report_dmg_result", + "start_report_threads", + "stop_report_threads", +] -__result_id: str | None = None -__event_loop = None # 存储事件循环的引用 +__result_id: str = "Unknown" +__event_loop: asyncio.AbstractEventLoop | None = None # 存储事件循环的引用 if TYPE_CHECKING: - from zsim.simulator.dataclasses import SimCfg + from zsim.models.session.session_run import ExecAttrCurveCfg, ExecWeaponCfg -def regen_result_id(sim_cfg: "SimCfg | None", *, session_id=None) -> None: +def regen_result_id(sim_cfg: "ExecAttrCurveCfg | ExecWeaponCfg | None", *, session_id=None) -> None: """ 根据运行模式生成结果ID并处理相关文件。 @@ -48,9 +56,9 @@ def regen_result_id(sim_cfg: "SimCfg | None", *, session_id=None) -> None: if sim_cfg is not None: # 并行模式:session_id(API模式)/随机生成的uuid(WebUI模式) + 配置列表作为id if sim_cfg.func == "attr_curve": - __result_id = f"./results/{sim_cfg.run_turn_uuid}/{sim_cfg.func}_{sim_cfg.sc_name}_{sim_cfg.sc_value}" + __result_id = f"./results/{sim_cfg.run_turn_uuid}/{sim_cfg.func}_{sim_cfg.sc_name}_{sim_cfg.sc_value}" # type: ignore elif sim_cfg.func == "weapon": - __result_id = f"./results/{sim_cfg.run_turn_uuid}/{sim_cfg.func}_{sim_cfg.weapon_name}_{sim_cfg.weapon_level}" + __result_id = f"./results/{sim_cfg.run_turn_uuid}/{sim_cfg.func}_{sim_cfg.weapon_name}_{sim_cfg.weapon_level}" # type: ignore # 创建结果目录 os.makedirs(__result_id, exist_ok=True) # 将 parallel_config 保存为 JSON 文件 @@ -129,17 +137,17 @@ def regen_result_id(sim_cfg: "SimCfg | None", *, session_id=None) -> None: def start_async_tasks(): """启动异步任务处理日志和结果写入""" - global __event_loop - - # 如果已有事件循环在运行,则不再创建新的 - if __event_loop is not None: - return - - # 创建新的事件循环 - __event_loop = asyncio.new_event_loop() # 在新线程中运行事件循环 def run_event_loop(): + global __event_loop + + # 如果已有事件循环在运行,则不再创建新的 + if __event_loop is not None: + return + + # 创建新的事件循环 + __event_loop = asyncio.new_event_loop() asyncio.set_event_loop(__event_loop) __event_loop.create_task(async_log_writer(__result_id)) __event_loop.create_task(async_result_writer(__result_id)) diff --git a/zsim/sim_progress/Report/buff_handler.py b/zsim/sim_progress/Report/buff_handler.py index 4cba1f0a..73811967 100644 --- a/zsim/sim_progress/Report/buff_handler.py +++ b/zsim/sim_progress/Report/buff_handler.py @@ -1,11 +1,13 @@ import os from collections import defaultdict -import pandas as pd +import polars as pl from zsim.define import DEBUG, DEBUG_LEVEL -buffered_data: dict = defaultdict(lambda: defaultdict(lambda: defaultdict(int))) +buffered_data: dict[str, dict[int, dict[str, int]]] = defaultdict( + lambda: defaultdict(lambda: defaultdict(int)) +) def report_buff_to_queue( @@ -17,14 +19,22 @@ def report_buff_to_queue( buffered_data[character_name][time_tick - 1][buff_name] += buff_count -# TODO:切换为 polars def dump_buff_csv(result_id: str): - for char_name in buffered_data: - if char_name not in buffered_data: - raise ValueError("你tmd函数写错了!") + for char_name, char_data in buffered_data.items(): + if not char_data: + continue + + rows = [{"time_tick": tick, **buffs} for tick, buffs in char_data.items()] + + if not rows: + continue + buff_report_file_path = f"{result_id}/buff_log/{char_name}.csv" os.makedirs(os.path.dirname(buff_report_file_path), exist_ok=True) - df = pd.DataFrame.from_dict(buffered_data[char_name], orient="index").reset_index() - df.rename(columns={"index": "time_tick"}, inplace=True) - df = df.sort_values(by="time_tick") - df.to_csv(buff_report_file_path, index=False, encoding="utf-8-sig") + + df = pl.DataFrame(rows) + # Sort columns: time_tick first, then buff names alphabetically for deterministic output. + buff_columns = sorted([col for col in df.columns if col != "time_tick"]) + df = df.sort("time_tick").select(["time_tick"] + buff_columns) + + df.write_csv(buff_report_file_path, include_bom=True) diff --git a/zsim/sim_progress/Report/result_handler.py b/zsim/sim_progress/Report/result_handler.py index cc319ca9..43d61323 100644 --- a/zsim/sim_progress/Report/result_handler.py +++ b/zsim/sim_progress/Report/result_handler.py @@ -2,6 +2,7 @@ import os import queue import uuid +from typing import Literal import aiofiles import numpy as np @@ -14,7 +15,7 @@ def report_dmg_result( tick: int, - element_type: ElementType | int, + element_type: ElementType, skill_tag: str | None = None, dmg_expect: float | np.float64 = 0, dmg_crit: float | np.float64 | None = None, @@ -57,32 +58,21 @@ async def async_result_writer(result_id: str): buffer.append(result_dict) if len(buffer) >= max_buffer_size or result_queue.empty(): - if buffer: - result_df = pl.DataFrame(buffer) - csv_data = result_df.write_csv( - include_header=new_file, separator="," - ) - mode = "w" if new_file else "a" - async with aiofiles.open( - result_path, mode, encoding="utf-8-sig" - ) as file: - await file.write(csv_data) - - new_file = False - buffer.clear() + await write_result(result_path, new_file, buffer) + new_file = False result_queue.task_done() except queue.Empty: + await asyncio.sleep(0.01) if buffer: - result_df = pl.DataFrame(buffer) - csv_data = result_df.write_csv(include_header=new_file, separator=",") - mode = "w" if new_file else "a" - async with aiofiles.open( - result_path, mode, encoding="utf-8-sig" - ) as file: - await file.write(csv_data) - + await write_result(result_path, new_file, buffer) new_file = False - buffer.clear() - await asyncio.sleep(0.01) + +async def write_result(result_path: str, new_file: bool, buffer: list[dict]): + result_df = pl.DataFrame(buffer) + csv_data: str = result_df.write_csv(include_header=new_file, separator=",") + mode: Literal["w", "a"] = "w" if new_file else "a" + async with aiofiles.open(result_path, mode, encoding="utf-8-sig") as file: + await file.write(csv_data) + buffer.clear() diff --git a/zsim/sim_progress/ScheduledEvent/CalAnomaly.py b/zsim/sim_progress/ScheduledEvent/CalAnomaly.py index 935a7f91..f4d44c66 100644 --- a/zsim/sim_progress/ScheduledEvent/CalAnomaly.py +++ b/zsim/sim_progress/ScheduledEvent/CalAnomaly.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Literal import numpy as np @@ -6,6 +6,8 @@ from zsim.sim_progress.anomaly_bar import AnomalyBar from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import ( DirgeOfDestinyAnomaly as Abloom, +) +from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import ( Disorder, PolarityDisorder, ) @@ -17,6 +19,8 @@ from .Calculator import MultiplierData as MulData if TYPE_CHECKING: + from zsim.sim_progress.Buff import Buff + from zsim.sim_progress.Character import Character from zsim.simulator.simulator_class import Simulator @@ -25,7 +29,7 @@ def __init__( self, anomaly_obj: AnomalyBar, enemy_obj: Enemy, - dynamic_buff: dict, + dynamic_buff: dict[str, list["Buff"]], sim_instance: "Simulator", ): """ @@ -38,7 +42,7 @@ def __init__( """ self.sim_instance = sim_instance self.enemy_obj = enemy_obj - self.anomaly_obj = anomaly_obj + self.anomaly_obj: AnomalyBar = anomaly_obj self.dynamic_buff = dynamic_buff snapshot: tuple[ElementType, np.ndarray] = ( self.anomaly_obj.element_type, @@ -51,14 +55,15 @@ def __init__( print( f"【CalAnomaly Warnning】:检测到异常实例(属性类型:{anomaly_obj.element_type})的激活源为空,改异常实例将无法享受Buff加成。" ) + raise NotImplementedError else: - char_obj = anomaly_obj.activated_by.skill.char_obj + char_obj: "Character | None" = anomaly_obj.activated_by.skill.char_obj # 根据动态buff读取怪物面板 self.data: MulData = MulData( enemy_obj=self.enemy_obj, dynamic_buff=self.dynamic_buff, - judge_node=anomaly_obj, + judge_node=anomaly_obj, # type: ignore character_obj=char_obj, ) # 虚拟角色等级 @@ -100,7 +105,7 @@ def __init__( stun_vulnerability, special_mul, imp_mul, - stun_mul + stun_mul, ) @staticmethod @@ -203,7 +208,9 @@ def set_final_multipliers( def cal_anomaly_dmg(self) -> np.float64: """计算异常伤害期望""" - return np.float64(np.prod(self.final_multipliers)/(self.dmg_sp[0,9] * self.dmg_sp[0,10])) + return np.float64( + np.prod(self.final_multipliers) / (self.dmg_sp[0, 9] * self.dmg_sp[0, 10]) + ) class CalDisorder(CalAnomaly): @@ -211,7 +218,7 @@ def __init__( self, disorder_obj: Disorder, enemy_obj: Enemy, - dynamic_buff: dict, + dynamic_buff: dict[str, list["Buff"]], sim_instance: "Simulator", ): """ @@ -251,7 +258,9 @@ def cal_disorder_base_dmg(self, base_mul: np.float64) -> np.float64: case _: assert False, f"Invalid Element Type {self.element_type}" # 计算紊乱基础倍率增幅 - disorder_basic_mul_map = self.data.dynamic.disorder_basic_mul_map + disorder_basic_mul_map: dict[ElementType | Literal["all"], float] = ( + self.data.dynamic.disorder_basic_mul_map + ) disorder_base_dmg *= 1 + ( disorder_basic_mul_map[self.element_type] + disorder_basic_mul_map["all"] ) @@ -263,8 +272,8 @@ def cal_disorder_extra_mul(self) -> np.float64: 异常额外增伤区 = 1 + 对应属性异常额外增伤 """ - map = self.data.dynamic.ano_extra_bonus - return 1 + map[-1] + map: dict[ElementType | Literal["all", -1], float] = self.data.dynamic.ano_extra_bonus + return np.float64(1 + map[-1]) def cal_disorder_stun(self) -> np.float64: imp = self.final_multipliers[9] @@ -283,7 +292,7 @@ def __init__( self, disorder_obj: PolarityDisorder, enemy_obj: Enemy, - dynamic_buff: dict, + dynamic_buff: dict[str, list["Buff"]], sim_instance: "Simulator", ): super().__init__(disorder_obj, enemy_obj, dynamic_buff, sim_instance=sim_instance) @@ -297,7 +306,7 @@ def __init__( ) + (ap * disorder_obj.additional_dmg_ap_ratio) def __find_yanagi(self) -> Yanagi | None: - yanagi_obj: Yanagi | None = self.sim_instance.char_data.char_obj_dict.get("柳", None) + yanagi_obj: Yanagi | None = self.sim_instance.char_data.char_obj_dict.get("柳", None) # type: ignore if yanagi_obj is None: assert False, "没柳你哪来的极性紊乱" return yanagi_obj @@ -308,7 +317,7 @@ def __init__( self, abloom_obj: Abloom, enemy_obj: Enemy, - dynamic_buff: dict, + dynamic_buff: dict[str, list["Buff"]], sim_instance: "Simulator", ): super().__init__(abloom_obj, enemy_obj, dynamic_buff, sim_instance=sim_instance) diff --git a/zsim/sim_progress/ScheduledEvent/Calculator.py b/zsim/sim_progress/ScheduledEvent/Calculator.py index b75bde72..13dac69c 100644 --- a/zsim/sim_progress/ScheduledEvent/Calculator.py +++ b/zsim/sim_progress/ScheduledEvent/Calculator.py @@ -1,17 +1,16 @@ import json from functools import lru_cache -from typing import Any +from typing import Any, Literal import numpy as np from zsim.define import INVALID_ELEMENT_ERROR, ElementType +from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar from zsim.sim_progress.Character import Character from zsim.sim_progress.data_struct import cal_buff_total_bonus from zsim.sim_progress.Enemy import Enemy from zsim.sim_progress.Preload import SkillNode from zsim.sim_progress.Report import report_to_log -from zsim.sim_progress.anomaly_bar.AnomalyBarClass import AnomalyBar - with open( file="./zsim/sim_progress/ScheduledEvent/buff_effect_trans.json", @@ -30,16 +29,19 @@ def __new__( enemy_obj: Enemy, dynamic_buff: dict, character_obj: Character | None = None, - judge_node: SkillNode | None = None, + judge_node: SkillNode | AnomalyBar | None = None, ): hashable_dynamic_buff = tuple((key, tuple(value)) for key, value in dynamic_buff.items()) enemy_hashable = ( tuple(enemy_obj.dynamic.dynamic_debuff_list), tuple(enemy_obj.dynamic.dynamic_dot_list), ) - cache_key = tuple( - (enemy_hashable, hashable_dynamic_buff, id(character_obj), id(judge_node)) - ) + + node_id = id(judge_node) + if isinstance(judge_node, AnomalyBar): + node_id = judge_node.UUID + + cache_key = tuple((enemy_hashable, hashable_dynamic_buff, id(character_obj), node_id)) if cache_key in cls.mul_data_cache: return cls.mul_data_cache[cache_key] else: @@ -317,8 +319,6 @@ def __init__(self, dynamic_statement): self.disorder_dmg_mul: float = 0.0 self.all_anomaly_dmg_mul: float = 0.0 - - self.special_multiplier_zone: float = 0.0 self.stun_tick_increase: int = 0 @@ -367,7 +367,7 @@ def __init__(self, dynamic_statement): self.__read_dynamic_statement(dynamic_statement) """在更新完全部Buff效果后,再组成字典(提前组成字典会导致字典内容和后置的赋值脱钩)""" - self.ano_extra_bonus: dict[ElementType | str, float] = { + self.ano_extra_bonus: dict[ElementType | Literal["all", -1], float] = { 0: self.assault_dmg_mul, 1: self.burn_dmg_mul, 2: self.freeze_dmg_mul, @@ -377,7 +377,7 @@ def __init__(self, dynamic_statement): -1: self.disorder_dmg_mul, "all": self.all_anomaly_dmg_mul, } - self.anomaly_time_increase: dict[ElementType | str, float] = { + self.anomaly_time_increase: dict[ElementType | Literal["all"], float] = { 0: self.assault_time_increase, 1: self.burn_time_increase, 2: self.shock_time_increase, @@ -387,7 +387,7 @@ def __init__(self, dynamic_statement): "all": self.all_anomaly_time_increase, } - self.anomaly_time_increase_percentage: dict[ElementType | str, float] = { + self.anomaly_time_increase_percentage: dict[ElementType | Literal["all"], float] = { 0: self.assault_time_increase_percentage, 1: self.burn_time_increase_percentage, 2: self.shock_time_increase_percentage, @@ -397,7 +397,7 @@ def __init__(self, dynamic_statement): "all": self.all_anomaly_time_increase_percentage, } - self.disorder_basic_mul_map: dict[ElementType | str, float] = { + self.disorder_basic_mul_map: dict[ElementType | Literal["all"], float] = { 0: self.strike_disorder_basic_mul, 1: self.burn_disorder_basic_mul, 2: self.frostbite_disorder_basic_mul, @@ -459,6 +459,7 @@ def __init__( self.char_name: str | None = data.char_name self.cid: int | None = data.cid self.skill_node = skill_node + assert isinstance(data.judge_node, SkillNode) self.element_type = data.judge_node.element_type self.skill_tag = data.judge_node.skill_tag @@ -565,7 +566,7 @@ def cal_base_dmg(self, data: MultiplierData) -> float: 而代理人的攻击力也可以在代理人页面或战斗中的暂停菜单中查阅,当然考虑到战斗中可能存在的各种Buff, 实时的伤害计算请关注战斗中实际的攻击力数值。 """ - assert data.judge_node is not None, "非法的调用,没有获取到skill node" + assert isinstance(data.judge_node, SkillNode), "非法的调用,没有获取到skill node" # 伤害倍率 = 技能伤害倍率 / 攻击次数 dmg_ratio = data.judge_node.skill.damage_ratio / data.judge_node.hit_times # 获取伤害对应属性 @@ -600,12 +601,14 @@ def cal_base_attr(self, base_attr: int, data: MultiplierData): + data.dynamic.field_anomaly_mastery ) elif base_attr == 4: + assert data.char_instance is not None # 贯穿力属性的实时计算 if not hasattr(data.char_instance, "sheer_attack_conversion_rate"): raise AttributeError( f"{data.char_instance.NAME}作为命破属性代理人,必须拥有贯穿力转化字典!" ) base_sheer_atk = 0 + assert data.char_instance.sheer_attack_conversion_rate is not None for ( key, value, @@ -638,6 +641,7 @@ def cal_dmg_bonus(data: MultiplierData) -> float: 伤害类型增伤包括针对于各类技能(如普通攻击,强化特殊技,终结技等)的增伤。常见于音擎效果和鸣徽效果中。 进攻类型增伤即针对于角色进攻类型(斩击(Slash)、打击(Strike)和穿透(Pierce))的增伤。全类型增伤就是未作类型限定的增伤。 """ + assert isinstance(data.judge_node, SkillNode) element_type = data.judge_node.element_type # 获取属性伤害加成,初始化为1.0 if element_type == 0: @@ -724,6 +728,7 @@ def cal_personal_crit_rate(data: MultiplierData) -> float: def cal_crit_dmg(data: MultiplierData) -> float: """暴击伤害 = 静态面板暴击伤害 + buff暴击伤害 + 受暴击伤害增加""" # 获取指定label暴伤 + assert isinstance(data.judge_node, SkillNode) if ( data.judge_node.skill.labels is not None and data.judge_node.skill.labels.get("aftershock_attack") == 1 @@ -771,6 +776,7 @@ def cal_defense_mul(self, data: MultiplierData) -> float: 受击方有效防御 = 受击方防御 * (1 - 攻击方穿透率%) - 攻击方穿透值 ≥ 0 受击方防御 = (基础防御 * (1 + 战斗外防御%) + 战斗外固定防御) * (1 + 防御加成% - 防御降低%) + 固定防御 """ + assert isinstance(data.judge_node, SkillNode) base_attr = data.judge_node.skill.diff_multiplier if base_attr != 4: attacker_level: int = data.char_level if data.char_level is not None else 1 @@ -846,6 +852,7 @@ def cal_res_mul( ) -> float: """抗性区 = 1 - 受击方抗性 + 受击方抗性降低 + 攻击方抗性穿透""" if element_type is None: + assert isinstance(data.judge_node, SkillNode) element_type = data.judge_node.element_type # 获取抗性区,初始化为0 if element_type == 0: @@ -900,6 +907,7 @@ def cal_dmg_vulnerability( 减易伤区 = 1 + 减易伤 """ if element_type is None: + assert isinstance(data.judge_node, SkillNode) element_type = data.judge_node.element_type # 获取抗性区,初始化为0 if element_type == 0: @@ -942,6 +950,7 @@ def cal_special_mul(data: MultiplierData) -> float: @staticmethod def cal_sheer_dmg_bonus(data: MultiplierData) -> float: """计算贯穿伤害增幅区——贯穿伤害增加是一个独立乘区!""" + assert isinstance(data.judge_node, SkillNode) if data.judge_node.skill.diff_multiplier != 4: return 1.0 else: @@ -964,6 +973,7 @@ class AnomalyMul: """ def __init__(self, data: MultiplierData): + assert isinstance(data.judge_node, SkillNode) self.element_type: ElementType = data.judge_node.element_type self.anomaly_buildup: np.float64 = self.cal_anomaly_buildup(data) @@ -1004,13 +1014,14 @@ def cal_am(data: MultiplierData) -> np.float64: def cal_anomaly_buildup(data: MultiplierData) -> np.float64: """异常积蓄值 = 基础积蓄值 * 异常掌控/100 * (1 + 属性异常积蓄效率提升) * (1 - 属性异常积蓄抗性)""" # 基础蓄积值 + assert isinstance(data.judge_node, SkillNode) accumulation = data.judge_node.skill.anomaly_accumulation # 异常掌控 am = Calculator.AnomalyMul.cal_am(data) # 属性异常积蓄效率提升、属性异常积蓄抗性 element_type = data.judge_node.element_type - enemy_buildup_res = data.enemy_obj.anomaly_resistance_dict.get(element_type, 0) + enemy_buildup_res = data.enemy_obj.anomaly_resistance_dict.get(element_type, 0.0) if element_type == 0: element_buildup_bonus = ( @@ -1094,6 +1105,7 @@ def cal_anomaly_buildup(data: MultiplierData) -> np.float64: def cal_base_damage(data: MultiplierData) -> float: """基础伤害区 = 攻击力 * 对应属性的异常伤害倍率""" atk = data.static.atk * (1 + data.dynamic.field_atk_percentage) + data.dynamic.atk + assert isinstance(data.judge_node, SkillNode) element_type = data.judge_node.element_type if element_type == 0: base_damage = 7.13 * atk @@ -1112,6 +1124,7 @@ def cal_base_damage(data: MultiplierData) -> float: @staticmethod def cal_dmg_bonus(data: MultiplierData) -> float: """增伤区 = 1 + 属性增伤 + 全增伤""" + assert isinstance(data.judge_node, SkillNode) element_type = data.judge_node.element_type if element_type == 0: element_dmg_bonus = data.static.phy_dmg_bonus + data.dynamic.phy_dmg_bonus @@ -1149,6 +1162,7 @@ def cal_ap(data: MultiplierData): @staticmethod def cal_ano_extra_mul(data: MultiplierData) -> float: """异常额外增伤区 = 1 + 对应属性异常额外增伤""" + assert isinstance(data.judge_node, SkillNode) element_type = data.judge_node.element_type map = data.dynamic.ano_extra_bonus ano_dmg_mul = 1 + map.get(element_type, 0) + map["all"] @@ -1159,15 +1173,17 @@ def cal_anomaly_crit(self, data: MultiplierData) -> float: return 1 def cal_res_pen(self, data: MultiplierData) -> float: - if self.element_type == 0: + assert isinstance(data.judge_node, SkillNode) + element_type = data.judge_node.element_type + if element_type == 0: element_res_pen = data.dynamic.physical_res_pen_increase - elif self.element_type == 1: + elif element_type == 1: element_res_pen = data.dynamic.fire_res_pen_increase - elif self.element_type == 2 or self.element_type == 5: + elif element_type == 2 or element_type == 5: element_res_pen = data.dynamic.ice_res_pen_increase - elif self.element_type == 3: + elif element_type == 3: element_res_pen = data.dynamic.electric_res_pen_increase - elif self.element_type in [4, 6]: + elif element_type in [4, 6]: element_res_pen = data.dynamic.ether_res_pen_increase else: assert False, INVALID_ELEMENT_ERROR @@ -1181,6 +1197,7 @@ class StunMul: """ def __init__(self, data: MultiplierData): + assert isinstance(data.judge_node, SkillNode) self.element_type: ElementType = data.judge_node.element_type self.imp = self.cal_imp(data) self.stun_ratio = self.cal_stun_ratio(data) @@ -1208,6 +1225,7 @@ def cal_imp(data: MultiplierData) -> float: @staticmethod def cal_stun_ratio(data: MultiplierData) -> float: + assert isinstance(data.judge_node, SkillNode) stun_ratio = data.judge_node.skill.stun_ratio / data.judge_node.hit_times return stun_ratio @@ -1215,13 +1233,14 @@ def cal_stun_ratio(data: MultiplierData) -> float: def cal_stun_res( data: MultiplierData, element_type: ElementType, *, over_stun_res: float = 0 ) -> float: - enemy_stun_res = data.enemy_obj.stun_resistance_dict.get(element_type) + enemy_stun_res = data.enemy_obj.stun_resistance_dict.get(element_type, 0.0) stun_res = 1 - data.dynamic.stun_res - over_stun_res - enemy_stun_res return stun_res @staticmethod def cal_stun_bonus(data: MultiplierData) -> float: # 获取指定label失衡值增加 + assert isinstance(data.judge_node, SkillNode) if ( data.judge_node.skill.labels is not None and data.judge_node.skill.labels.get("aftershock_attack") == 1 @@ -1285,19 +1304,19 @@ def cal_dmg_expect(self) -> np.float64: # ] # print(self.skill_node.skill.skill_text, f"第{self.skill_node.loading_mission.hitted_count if self.skill_node.loading_mission else 1}次命中", ":", # [f"{__tag} : {__value:.2f}" for __tag, __value in zip(tag_list, multipliers)]) - return dmg_expect + return np.float64(dmg_expect) def cal_dmg_crit(self) -> np.float64: """计算暴击伤害""" multipliers: np.ndarray = self.regular_multipliers.get_array_crit() dmg_crit = np.prod(multipliers) - return dmg_crit + return np.float64(dmg_crit) def cal_dmg_not_crit(self) -> np.float64: """计算非暴击伤害""" multipliers: np.ndarray = self.regular_multipliers.get_array_not_crit() dmg_not_crit = np.prod(multipliers) - return dmg_not_crit + return np.float64(dmg_not_crit) def cal_snapshot(self) -> tuple[int, np.float64, np.ndarray]: """计算异常值与失衡值快照,返回一个一维数组,用于计算异常伤害的虚拟角色,鬼知道为什么那么麻烦""" @@ -1314,7 +1333,7 @@ def cal_stun(self) -> np.float64: """计算失衡值""" multipliers: np.ndarray = self.stun_multipliers.get_stun_array() stun = np.prod(multipliers) - return stun + return np.float64(stun) @staticmethod def update_stun_tick(enemy_obj: Enemy, data: MultiplierData): diff --git a/zsim/sim_progress/anomaly_bar/AnomalyBarClass.py b/zsim/sim_progress/anomaly_bar/AnomalyBarClass.py index ec7e54bf..e77ba484 100644 --- a/zsim/sim_progress/anomaly_bar/AnomalyBarClass.py +++ b/zsim/sim_progress/anomaly_bar/AnomalyBarClass.py @@ -22,9 +22,15 @@ class AnomalyBar: sim_instance: "Simulator" element_type: ElementType = 0 # 属性种类编号(1~5) is_disorder: bool = False # 是否是紊乱实例 - current_ndarray: np.ndarray = field(default_factory=lambda: np.zeros((1, 1), dtype=np.float64)) # 当前快照总和 - current_anomaly: np.float64 = field(default_factory=lambda: np.float64(0)) # 当前已经累计的积蓄值 - current_effective_anomaly: np.float64 = field(default_factory=lambda: np.float64(0)) # 有效积蓄值(参与快照的) + current_ndarray: np.ndarray = field( + default_factory=lambda: np.zeros((1, 1), dtype=np.float64) + ) # 当前快照总和 + current_anomaly: np.float64 = field( + default_factory=lambda: np.float64(0) + ) # 当前已经累计的积蓄值 + current_effective_anomaly: np.float64 = field( + default_factory=lambda: np.float64(0) + ) # 有效积蓄值(参与快照的) anomaly_times: int = 0 # 迄今为止触发过的异常次数 cd: int = 180 # 属性异常的内置CD, last_active: int = 0 # 上一次属性异常的时间 @@ -44,6 +50,9 @@ class AnomalyBar: def __post_init__(self): self.UUID = uuid.uuid4() + def __hash__(self): + return hash(self.UUID) + @property def is_full(self): assert self.max_anomaly is not None diff --git a/zsim/sim_progress/data_struct/ActionStack.py b/zsim/sim_progress/data_struct/ActionStack.py index b57d349d..897f0dd7 100644 --- a/zsim/sim_progress/data_struct/ActionStack.py +++ b/zsim/sim_progress/data_struct/ActionStack.py @@ -1,9 +1,65 @@ from collections import defaultdict +from typing import Generic, TypeVar from zsim.define import SWAP_CANCEL +from zsim.sim_progress.Preload import SkillNode +NODE_T = TypeVar("NODE_T", bound=SkillNode) -class ActionStack: + +class BaseStack(Generic[NODE_T]): + """通用栈结构的基类""" + + def __init__(self, length: int): + self.length = length + self.stack: list[NODE_T] = [] + + def push(self, item: NODE_T): + self.stack.append(item) + if len(self.stack) > self.length: + self.stack.pop(0) + + def pop(self) -> NODE_T | None: + if self.is_empty(): + return None + return self.stack.pop() + + def peek(self) -> NODE_T | None: + if self.is_empty(): + return None + return self.stack[-1] + + def is_empty(self) -> bool: + return len(self.stack) == 0 + + def peek_bottom(self) -> NODE_T | None: + if self.is_empty(): + return None + return self.stack[0] + + def reset(self): + self.stack = [] + + def __len__(self): + return len(self.stack) + + def __str__(self): + return str(self.stack) + + def __iter__(self): + return iter(self.stack) + + def __getitem__(self, index) -> NODE_T: + return self.stack[index] + + def __eq__(self, value: object) -> bool: + return self.stack == value or self.stack == getattr(value, "stack", None) + + def __ne__(self, value: object) -> bool: + return not self.__eq__(value) + + +class ActionStack(BaseStack[NODE_T]): """ 这个动作栈无法记录所有的动作,只能记录所有的前台角色的主动技能。 功能类似于wow的插件TrufigGCD。但是长度只有2,因为只需要记录“上一个动作”和“当前动作” @@ -21,26 +77,23 @@ def __init__(self, length: int = 2): pop、peek方法会返回对应角色的上一个动作, 但是相应的,如果我们没有开启合轴模式,那么传入Key参数时就会报错。 """ - self.length = length + super().__init__(length) if SWAP_CANCEL: - self.personal_stack = defaultdict(list) + self.personal_stack: defaultdict[str, list[NODE_T]] = defaultdict(list) self._swap_cancel_warning_printed = False # 标志变量,用于控制警告信息只打印一次 - self.stack = [] - - def push(self, item, /): + def push(self, item: NODE_T): """向栈中压入一个元素,如果栈内元素超过2个,移除最早的元素""" if SWAP_CANCEL: - key = item.mission_character - self.personal_stack[key].append(item) - if len(self.personal_stack[key]) > self.length: - self.personal_stack[key].pop(0) - self.stack.append(item) - # 如果栈的大小超过 2,就删除栈底元素(即最先加入的元素) - if len(self.stack) > self.length: - self.stack.pop(0) # pop(0) 移除栈底元素 - - def pop(self, /, key: str = None): + key = item.mission_character # type: ignore + if key: + self.personal_stack[key].append(item) + if len(self.personal_stack[key]) > self.length: + self.personal_stack[key].pop(0) + # 调用父类的push方法 + super().push(item) + + def pop(self, /, key: str | None = None) -> NODE_T | None: """从栈顶弹出一个元素""" if key: if not SWAP_CANCEL: @@ -50,12 +103,9 @@ def pop(self, /, key: str = None): pop_item = self.personal_stack[key].pop() return pop_item else: - if self.is_empty(): - return None - else: - return self.stack.pop() + return super().pop() - def peek(self, /, key: str = None): + def peek(self, /, key: str | None = None) -> NODE_T | None: """查看栈顶元素""" if key: if not SWAP_CANCEL: @@ -69,16 +119,9 @@ def peek(self, /, key: str = None): "Warning: 在开启合轴模式的情况下,在调用ActionStack的peek方法时并未传入key参数!\n这会导致在含有多个动作的tick,peek方法只会返回最后一个动作,从而让部分buff无法正常触发!" ) self._swap_cancel_warning_printed = True # 标记为已打印 - if not self.is_empty(): - return self.stack[-1] - else: - return None + return super().peek() - def is_empty(self): - """判断栈是否为空""" - return len(self.stack) == 0 - - def peek_bottom(self, /, key: str = None): + def peek_bottom(self, /, key: str | None = None) -> NODE_T | None: """查看栈底元素""" if key: if not SWAP_CANCEL: @@ -89,59 +132,20 @@ def peek_bottom(self, /, key: str = None): return None return self.personal_stack[key][0] else: - if not self.is_empty(): - return self.stack[0] - else: - return None - - def __len__(self): - return len(self.stack) - - def __str__(self): - return str(self.stack) - - def __iter__(self): - return iter(self.stack) - - def __getitem__(self, index, /): - return self.stack[index] - - def __eq__(self, value: object, /) -> bool: - return self.stack == value or self.stack == getattr(value, "stack", None) - - def __ne__(self, value: object, /) -> bool: - return not self.__eq__(value) + return super().peek_bottom() def reset_myself(self): if SWAP_CANCEL: self.personal_stack = defaultdict(list) self._swap_cancel_warning_printed = False # 标志变量,用于控制警告信息只打印一次 - self.stack = [] + self.reset() -class NodeStack: +class NodeStack(BaseStack[NODE_T]): def __init__(self, length: int = 3): - self.length = length - self.stack = [] - - def push(self, item, /): - self.stack.append(item) - if len(self.stack) > self.length: - self.stack.pop(0) - - def pop(self): - if self.is_empty(): - return None - else: - return self.stack.pop() - - def peek(self, /): - if not self.is_empty(): - return self.stack[-1] - else: - return None + super().__init__(length) - def peek_index(self, index: int): + def peek_index(self, index: int) -> NODE_T | None: if self.is_empty(): return None if index > len(self.stack): @@ -149,31 +153,22 @@ def peek_index(self, index: int): return None return self.stack[-index] - def is_empty(self): - """判断栈是否为空""" - return len(self.stack) == 0 - - def peek_bottom(self, /): - """查看栈底元素""" - if not self.is_empty(): - return self.stack[0] - else: - return None - - def get_effective_node(self): + def get_effective_node(self) -> NODE_T | None: """排除附加伤害技能,来获取有效的技能。""" if self.is_empty(): return None else: i = 1 while i <= len(self.stack): - if self.stack[-i].skill.labels is not None: - if "additional_damage" in self.stack[-i].skill.labels: + node = self.stack[-i] + if node.skill.labels is not None: + if "additional_damage" in node.skill.labels: i += 1 continue - return self.stack[-i] + return node + return None - def get_on_field_node(self, tick_now: int): + def get_on_field_node(self, tick_now: int) -> NODE_T | None: """ 这个函数是NodeStack的内置方法,用来获取当前Stack中的前台技能 鉴于合轴模式中,各角色的技能情况比较复杂,所以这个函数返回的结果并不一定准确。 @@ -181,7 +176,7 @@ def get_on_field_node(self, tick_now: int): 2、当目前场上只有1个ndoe时,无论这个node是什么类型,都返回该node 3、当目前场上存在多个node时,应返回最新的那个主动动作的SkillNode """ - _exist_node_list = [] + _exist_node_list: list[NODE_T] = [] _active_node_now = False for _node in self.stack: if _node.end_tick >= tick_now: @@ -202,34 +197,11 @@ def get_on_field_node(self, tick_now: int): else: """当场上的node全部都是被动动作时,只取其中最新的那个。""" return max((x for x in _exist_node_list), key=lambda x: x.preload_tick) - - def reset(self): - self.stack = [] + return None def last_node_is_end(self, tick) -> bool: """判断上一个skillnode是否结束""" - if self.is_empty(): - return True last_node = self.peek() - if last_node.end_tick <= tick: + if last_node is None: return True - else: - return False - - def __len__(self): - return len(self.stack) - - def __str__(self): - return str(self.stack) - - def __iter__(self): - return iter(self.stack) - - def __getitem__(self, index, /): - return self.stack[index] - - def __eq__(self, value: object, /) -> bool: - return self.stack == value or self.stack == getattr(value, "stack", None) - - def __ne__(self, value: object, /) -> bool: - return not self.__eq__(value) + return last_node.end_tick <= tick diff --git a/zsim/sim_progress/data_struct/BattleEventListener/CinderCobaltListener.py b/zsim/sim_progress/data_struct/BattleEventListener/CinderCobaltListener.py index e03f564b..89721a4f 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/CinderCobaltListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/CinderCobaltListener.py @@ -9,19 +9,19 @@ class CinderCobaltListener(BaseListener): """这个监听器的作用是监听佩戴者的进场。""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.active_signal: tuple[object, bool] | None = None - def listening_event(self, event_obj, signal: LBS, **kwargs): + def listening_event(self, event, signal: LBS, **kwargs): """监听到佩戴者的进场后,记录更新信号""" if signal not in [LBS.SWITCHING_IN, LBS.ENTER_BATTLE]: return - from zsim.sim_progress.Character import Character + from zsim.sim_progress.Character.character import Character - if not isinstance(event_obj, Character): + if not isinstance(event, Character): return - self.active_signal = (event_obj, True) + self.active_signal = (event, True) def listener_active(self): pass diff --git a/zsim/sim_progress/data_struct/BattleEventListener/HeartstringNocturneListener.py b/zsim/sim_progress/data_struct/BattleEventListener/HeartstringNocturneListener.py index af0072a9..c7d9a2f6 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/HeartstringNocturneListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/HeartstringNocturneListener.py @@ -2,6 +2,7 @@ from .BaseListenerClass import BaseListener from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: from zsim.simulator.simulator_class import Simulator @@ -9,7 +10,7 @@ class HeartstringNocturneListener(BaseListener): """监听入场事件,并且直接添加心弦夜响Buff""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.active_signal = None diff --git a/zsim/sim_progress/data_struct/BattleEventListener/HormonePunkListener.py b/zsim/sim_progress/data_struct/BattleEventListener/HormonePunkListener.py index c0edd02a..c98fd9a0 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/HormonePunkListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/HormonePunkListener.py @@ -2,6 +2,7 @@ from .BaseListenerClass import BaseListener from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: from zsim.simulator.simulator_class import Simulator @@ -9,19 +10,19 @@ class HormonePunkListener(BaseListener): """这个监听器的作用是监听佩戴者的进场。""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.active_signal: tuple[object, bool] | None = None - def listening_event(self, event_obj, signal: LBS, **kwargs): + def listening_event(self, event, signal: LBS, **kwargs): """监听到佩戴者的进场后,记录更新信号""" if signal not in [LBS.SWITCHING_IN, LBS.ENTER_BATTLE]: return - from zsim.sim_progress.Character import Character + from zsim.sim_progress.Character.character import Character - if not isinstance(event_obj, Character): + if not isinstance(event, Character): return - self.active_signal = (event_obj, True) + self.active_signal = (event, True) def listener_active(self): pass diff --git a/zsim/sim_progress/data_struct/BattleEventListener/HugoCorePassiveBuffListener.py b/zsim/sim_progress/data_struct/BattleEventListener/HugoCorePassiveBuffListener.py index d62f3e39..f431c5ad 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/HugoCorePassiveBuffListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/HugoCorePassiveBuffListener.py @@ -2,6 +2,7 @@ from .BaseListenerClass import BaseListener from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: from zsim.simulator.simulator_class import Simulator @@ -9,7 +10,7 @@ class HugoCorePassiveBuffListener(BaseListener): """这个监听器的作用是,尝试监听雨果致使怪物失衡的事件,并且触发一次核心被动Buff""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.buff_index = "Buff-角色-雨果-核心被动-暗渊回响" @@ -28,6 +29,8 @@ def listening_event(self, event, signal: LBS, **kwargs): if HUGO_REPORT: self.sim_instance.schedule_data.change_process_state() + if event.skill_node is None: + return print( f"雨果的失衡事件监听器监听到了雨果的技能{event.skill_tag}({event.skill_node.skill.skill_text})使怪物陷入失衡状态,根据核心被动,触发一次【暗渊回响】Buff" ) diff --git a/zsim/sim_progress/data_struct/BattleEventListener/YixuanAnomalyListener.py b/zsim/sim_progress/data_struct/BattleEventListener/YixuanAnomalyListener.py index 2b1879a9..163a5e2f 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/YixuanAnomalyListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/YixuanAnomalyListener.py @@ -6,7 +6,6 @@ from zsim.models.event_enums import ListenerBroadcastSignal as LBS if TYPE_CHECKING: - from zsim.sim_progress.Character.character import Character from zsim.sim_progress.Character.Yixuan import Yixuan from zsim.simulator.simulator_class import Simulator @@ -32,10 +31,13 @@ def ready(self) -> bool: def listening_event(self, event, signal: LBS, **kwargs): """监听到新的anomlay创建后,检查属性类型,通过判定则恢复闪能。""" if self.char is None: - char_obj: "Yixuan | Character | None" = self.sim_instance.char_data.find_char_obj( - CID=1371 - ) + from zsim.sim_progress.Character.Yixuan import Yixuan + + char_obj = self.sim_instance.char_data.find_char_obj(CID=1371) + if not isinstance(char_obj, Yixuan): + return self.char = char_obj + if signal != LBS.ANOMALY: return from zsim.sim_progress.anomaly_bar import AnomalyBar diff --git a/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC2QTEListener.py b/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC2QTEListener.py index aa34eeea..b800a02c 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC2QTEListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC2QTEListener.py @@ -1,39 +1,47 @@ from typing import TYPE_CHECKING + from zsim.define import YUZUHA_REPORT +from zsim.models.event_enums import ListenerBroadcastSignal as LBS from .BaseListenerClass import BaseListener -from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: - from zsim.sim_progress.Character.character import Character from zsim.sim_progress.Character.Yuzuha import Yuzuha + from zsim.sim_progress.Preload import SkillNode from zsim.simulator.simulator_class import Simulator class YuzuhaC2QTEListener(BaseListener): """这个监听器的作用是,监听其他角色通过连携技入场。""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.char: "Yuzuha | None" = None - def listening_event(self, event, signal: LBS, **kwargs): + def listening_event(self, event, signal: LBS, skill_node: "SkillNode | None" = None, **kwargs): """""" if self.char is None: - self.char = self.sim_instance.char_data.find_char_obj(CID=1411) - if not signal == LBS.SWITCHING_IN: - return - from zsim.sim_progress.Preload import SkillNode - skill_node = kwargs.get("skill_node") - if not isinstance(skill_node, SkillNode): - return - if skill_node.char_name == self.char.NAME: - return - if skill_node.skill.trigger_buff_level != 5: + from zsim.sim_progress.Character.Yuzuha import Yuzuha + + char_obj = self.sim_instance.char_data.find_char_obj(CID=1411) + if not isinstance(char_obj, Yuzuha): + return + self.char = char_obj + if ( + signal != LBS.SWITCHING_IN + or not isinstance(skill_node, SkillNode) + or skill_node.char_name == self.char.NAME + or skill_node.skill.trigger_buff_level != 5 + ): return else: self.listener_active() if YUZUHA_REPORT: self.sim_instance.schedule_data.change_process_state() - print(f"【柚叶2画】检测到队友 {skill_node.char_name} 通过连携技 {skill_node.skill_tag} 入场,为柚叶恢复1点甜度点") + print( + f"【柚叶2画】检测到队友 {skill_node.char_name} 通过连携技 {skill_node.skill_tag} 入场,为柚叶恢复1点甜度点" + ) def listener_active(self): + assert self.char is not None self.char.update_sugar_points(value=1) diff --git a/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC6ParryListener.py b/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC6ParryListener.py index 23da6e38..7951faa7 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC6ParryListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/YuzuhaC6ParryListener.py @@ -1,34 +1,48 @@ from typing import TYPE_CHECKING + from zsim.define import YUZUHA_REPORT +from zsim.models.event_enums import ListenerBroadcastSignal as LBS from .BaseListenerClass import BaseListener -from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: + from zsim.sim_progress.Character.Yuzuha import Yuzuha from zsim.simulator.simulator_class import Simulator class YuzuhaC6ParryListener(BaseListener): """这个监听器的作用是,监听自身的招架事件""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.char: "Yuzuha | None" = None def listening_event(self, event, signal: LBS, **kwargs): """获取“招架”类的广播信号。""" if self.char is None: - self.char = self.sim_instance.char_data.find_char_obj(CID=1411) + from zsim.sim_progress.Character.Yuzuha import Yuzuha + + char_obj = self.sim_instance.char_data.find_char_obj(CID=1411) + if not isinstance(char_obj, Yuzuha): + return + self.char = char_obj if signal != LBS.PARRY: return from zsim.sim_progress.Preload import SkillNode - if not isinstance(event, SkillNode): - return - if event.skill_tag not in ["1411_knock_back_cause_parry", "1411_SNA_3"]: + + if not isinstance(event, SkillNode) or event.skill_tag not in [ + "1411_knock_back_cause_parry", + "1411_SNA_3", + ]: return else: self.listener_active() if YUZUHA_REPORT: self.sim_instance.schedule_data.change_process_state() - print(f"【柚叶6画】检测到 柚叶 通过技能 {event.skill_tag} 招架/格挡了敌人的攻击,为 柚叶 恢复1点甜度点") + print( + f"【柚叶6画】检测到 柚叶 通过技能 {event.skill_tag} 招架/格挡了敌人的攻击,为 柚叶 恢复1点甜度点" + ) def listener_active(self): + assert self.char is not None self.char.update_sugar_points(value=1) diff --git a/zsim/sim_progress/data_struct/BattleEventListener/ZanshinHerbCaseListener.py b/zsim/sim_progress/data_struct/BattleEventListener/ZanshinHerbCaseListener.py index 5fe3c4e6..3cf4afe8 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/ZanshinHerbCaseListener.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/ZanshinHerbCaseListener.py @@ -2,6 +2,7 @@ from .BaseListenerClass import BaseListener from zsim.models.event_enums import ListenerBroadcastSignal as LBS + if TYPE_CHECKING: from zsim.simulator.simulator_class import Simulator @@ -9,14 +10,15 @@ class ZanshinHerbCaseListener(BaseListener): """这个监听器的作用是记录残心青囊的触发信号""" - def __init__(self, listener_id: str = None, sim_instance: "Simulator" = None): + def __init__(self, listener_id: str | None = None, sim_instance: "Simulator | None" = None): super().__init__(listener_id, sim_instance=sim_instance) self.active_signal: tuple[object, bool] | None = None - def listening_event(self, event_obj, signal: LBS, **kwargs): + def listening_event(self, event, signal: LBS, **kwargs): """监听到失衡事件或是触发了新的异常事件时,记录这个信号。""" if signal not in [LBS.STUN, LBS.ANOMALY]: - self.active_signal = (event_obj, True) + return + self.active_signal = (event, True) def listener_active(self): """置空信号""" diff --git a/zsim/sim_progress/data_struct/BattleEventListener/__init__.py b/zsim/sim_progress/data_struct/BattleEventListener/__init__.py index ca09baf3..5590c6de 100644 --- a/zsim/sim_progress/data_struct/BattleEventListener/__init__.py +++ b/zsim/sim_progress/data_struct/BattleEventListener/__init__.py @@ -1,7 +1,9 @@ import importlib from collections import defaultdict from typing import TYPE_CHECKING + from zsim.models.event_enums import ListenerBroadcastSignal as LBS + from .BaseListenerClass import BaseListener if TYPE_CHECKING: @@ -15,7 +17,7 @@ class ListenerManger: def __init__(self, sim_instance: "Simulator"): self.sim_instance = sim_instance - self._listeners_group: defaultdict[str | int : dict[str, BaseListener]] = defaultdict(dict) + self._listeners_group: defaultdict[str | int, dict[str, BaseListener]] = defaultdict(dict) self.__listener_map: dict[str, str] = { "Hugo_1": "HugoCorePassiveBuffListener", "Hormone_Punk_1": "HormonePunkListener", @@ -29,16 +31,21 @@ def __init__(self, sim_instance: "Simulator"): def add_listener(self, listener_owner: "Character | Enemy | None", listener: BaseListener): """添加一个监听器""" - owner_type = type(listener_owner).__bases__[0].__name__ - if owner_type == "Character": + if listener_owner is None or listener.listener_id is None: + raise TypeError("监听器所有者或监听器ID不能为空") + from zsim.sim_progress.Character.character import Character + + if isinstance(listener_owner, Character): self._listeners_group[listener_owner.CID][listener.listener_id] = listener - elif owner_type == "Enemy": + elif isinstance(listener_owner, Enemy): self._listeners_group["enemy"][listener.listener_id] = listener else: raise TypeError(f"无法解析的监听器所有者类型: {type(listener_owner)}") - def remove_listener(self, listener_owner, listener: BaseListener): + def remove_listener(self, listener_owner: "Character | Enemy | None", listener: BaseListener): """移除一个监听器""" + if listener_owner is None or listener.listener_id is None: + raise TypeError("监听器所有者或监听器ID不能为空") if isinstance(listener_owner, Character): listeners_group = self._listeners_group[listener_owner.CID] elif isinstance(listener_owner, Enemy): @@ -57,8 +64,8 @@ def broadcast_event(self, event, signal: LBS, **kwargs): def listener_factory( self, listener_owner: "Character | Enemy | None", - initiate_signal: str = None, - sim_instance: "Simulator" = None, + initiate_signal: str | None = None, + sim_instance: "Simulator | None" = None, ): """初始化监听器的工厂函数""" if initiate_signal is None: @@ -84,10 +91,12 @@ def get_listener( self, listener_owner: "Character | Enemy | None", listener_id: str ) -> BaseListener | None: """获取指定监听器""" - owner_type = type(listener_owner).__bases__[0].__name__ - if owner_type == "Character": + if listener_owner is None: + raise TypeError("监听器所有者不能为空") + + if isinstance(listener_owner, Character): listener = self._listeners_group[listener_owner.CID].get(listener_id, None) - elif owner_type == "Enemy": + elif isinstance(listener_owner, Enemy): listener = self._listeners_group["enemy"].get(listener_id, None) else: raise TypeError(f"无法解析的监听器所有者类型: {type(listener_owner)}") @@ -97,14 +106,10 @@ def get_listener( ) return listener - def __str__(self): - print("==========监听器组的现状如下==========") + def __str__(self) -> str: + output = "==========监听器组的现状如下==========\n" for owner_id, owner_dict in self._listeners_group.items(): - print(f"监听器组子集 ID: {owner_id}") - print(f"{['监听器' + __key + ' | ' for __key in owner_dict.keys()]}") - print("===================================") - - -# -# # import时,就创建一个单例 -# listener_manager_instance = ListenerManger() + output += f"监听器组子集 ID: {owner_id}\n" + output += f"{['监听器' + __key + ' | ' for __key in owner_dict.keys()]}\n" + output += "===================================" + return output diff --git a/zsim/sim_progress/data_struct/DecibelManager/DecibelManagerClass.py b/zsim/sim_progress/data_struct/DecibelManager/DecibelManagerClass.py index c018f7ad..7b2dcf40 100644 --- a/zsim/sim_progress/data_struct/DecibelManager/DecibelManagerClass.py +++ b/zsim/sim_progress/data_struct/DecibelManager/DecibelManagerClass.py @@ -1,6 +1,12 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any if TYPE_CHECKING: + from zsim.sim_progress.Character.character import Character + from zsim.sim_progress.data_struct.single_hit import SingleHit + from zsim.sim_progress.Enemy import Enemy + from zsim.sim_progress.Load.loading_mission import LoadingMission + from zsim.sim_progress.Preload.SkillsQueue import SkillNode + from zsim.simulator.dataclasses import ScheduleData from zsim.simulator.simulator_class import Simulator @@ -8,7 +14,7 @@ class Decibelmanager: def __init__(self, sim_instance: "Simulator"): # 原类属性改为实例属性 self.sim_instance = sim_instance - self.DECIBEL_EVENT_MAP = { + self.DECIBEL_EVENT_MAP: dict[str | int, list[int]] = { "interrupt_enemy": [10], 4: [20], "part_break": [20], @@ -20,7 +26,7 @@ def __init__(self, sim_instance: "Simulator"): "BH_Aid": [20], "BH_Aid_after_attacked": [30], } - self.REPORT_MAP = { + self.REPORT_MAP: dict[str | int, str] = { "interrupt_enemy": "打断敌人进攻", 4: "极限闪避", "part_break": "部位破坏", @@ -32,12 +38,12 @@ def __init__(self, sim_instance: "Simulator"): "BH_Aid": "支援角色触发的快速支援", "BH_Aid_after_attacked": "受击后触发的快速支援", } - self.char_obj_list: list[object] = [] - self.enemy = None - self.game_state = None + self.char_obj_list: list["Character"] = [] + self.enemy: "Enemy | None" = None + self.game_state: dict[str, Any] = {} def update(self, **kwargs): - decibel_value, node, output_key = self.get_decibel_value(kwargs) + decibel_value, node, output_key = self.get_decibel_value(**kwargs) if decibel_value == 0: return char_dict = self.split_char_list_by_cid(node) @@ -56,28 +62,33 @@ def add_decibel_to_char(self, decibel_value, char_name, output_key): from zsim.sim_progress.data_struct import ScheduleRefreshData refresh_data = ScheduleRefreshData(decibel_target=(char_name,), decibel_value=decibel_value) - self.game_state["schedule_data"].event_list.append(refresh_data) + schedule_data: "ScheduleData" = self.sim_instance.game_state["schedule_data"] + schedule_data.event_list.append(refresh_data) # print(f"{char_name}因{self.REPORT_MAP[output_key]}获得了{decibel_value}点喧响值!") - def get_decibel_value(self, kwargs): + def get_decibel_value( + self, + key: str | None = None, + skill_node: "SkillNode | None" = None, + single_hit: "SingleHit | None" = None, + loading_mission: "LoadingMission | None" = None, + **kwargs, + ): """根据程序的输入进行参数的初始化检查!并且返回本次运行所需要增加的喧响值""" - if self.game_state is None: - self.game_state = self.sim_instance.game_state - key = kwargs.get("key", None) - skill_node = kwargs.get("skill_node", None) - single_hit = kwargs.get("single_hit", None) - loading_mission = kwargs.get("loading_mission", None) if not any([skill_node, single_hit, loading_mission]): raise ValueError( "DecibelManager的update函数中,必须传入skill_node、single_hit、loading_mission中的一个!" ) + node: "SkillNode | None" = None if skill_node: node = skill_node - elif single_hit: + elif single_hit is not None: node = single_hit.skill_node - elif loading_mission: + elif loading_mission is not None: node = loading_mission.mission_node if key is None: + if node is None: + raise ValueError("DecibelManager的get_decibel_value函数中,node不能为空!") if node.skill.trigger_buff_level not in self.DECIBEL_EVENT_MAP: decibel_value = 0 output_key = 0 @@ -97,7 +108,8 @@ def get_decibel_value(self, kwargs): else: if key in ["anomaly", "disorder"]: if self.enemy is None: - self.enemy = self.game_state["schedule_data"].enemy + self.enemy = self.sim_instance.game_state["schedule_data"].enemy + assert self.enemy is not None decibel_value = self.DECIBEL_EVENT_MAP[key][ self.enemy.QTE_triggerable_times - 1 ] @@ -106,7 +118,9 @@ def get_decibel_value(self, kwargs): output_key = key return decibel_value, node, output_key - def split_char_list_by_cid(self, node): + def split_char_list_by_cid(self, node: "SkillNode | None"): + if node is None: + raise ValueError("DecibelManager的split_char_list_by_cid函数中,node不能为空!") char_id = int(node.skill_tag.strip().split("_")[0]) char_dict = {"major": [], "minor": []} if not self.char_obj_list: @@ -124,7 +138,3 @@ def split_char_list_by_cid(self, node): raise ValueError(f"找到多个CID为{char_id}的角色!") else: return char_dict - - -# 模块加载时创建唯一实例 -# decibel_manager_instance = Decibelmanager() diff --git a/zsim/sim_progress/data_struct/EnemyAttackEvent.py b/zsim/sim_progress/data_struct/EnemyAttackEvent.py index 9bca436d..41efe905 100644 --- a/zsim/sim_progress/data_struct/EnemyAttackEvent.py +++ b/zsim/sim_progress/data_struct/EnemyAttackEvent.py @@ -1,5 +1,5 @@ import math -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast from zsim.define import ENEMY_ATK_PARAMETER_DICT, ENEMY_ATTACK_REPORT @@ -49,7 +49,7 @@ def event_start(self, action: "EnemyAttackAction", start_tick: int): """开始一个进攻事件""" self.action = action self.last_start_tick = start_tick - self.last_end_tick = start_tick + action.duration + self.last_end_tick = start_tick + round(action.duration) response_window: tuple[int, int] = self.get_response_window() self.interaction_window_open_tick = response_window[0] self.interaction_window_close_tick = response_window[1] @@ -59,7 +59,7 @@ def event_start(self, action: "EnemyAttackAction", start_tick: int): f"敌人({self.enemy.name})开始了进攻事件:{action.tag},持续时间为{action.duration}tick" ) - def event_end(self, tick: int = None): + def event_end(self, tick: int | None = None): """结束一个进攻事件""" if self.action is None: raise ValueError("没有正在进行的进攻事件,无法结束!") @@ -72,7 +72,7 @@ def event_end(self, tick: int = None): self.hitted_count = 0 self.answered_count = 0 - def interrupted(self, tick: int, reason: str = None): + def interrupted(self, tick: int, reason: str | None = None): """中断当前进攻事件""" if self.action is None: raise ValueError("没有正在进行的进攻事件,无法中断!") @@ -127,7 +127,11 @@ def is_in_response_window(self, tick: int) -> bool: """判断当前tick是否处于首次响应窗口内""" if not self.attacking: return False - if self.interaction_window_open_tick <= tick <= self.interaction_window_close_tick: + if ( + self.interaction_window_open_tick is not None + and self.interaction_window_close_tick is not None + and self.interaction_window_open_tick <= tick <= self.interaction_window_close_tick + ): return True return False @@ -136,13 +140,14 @@ def get_rt(self) -> int: theta = ENEMY_ATK_PARAMETER_DICT.get("theta", None) if theta is None: raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有theta参数,请检查配置!") - perfect_player: bool = ENEMY_ATK_PARAMETER_DICT.get("perfect_player") + perfect_player: bool = bool(ENEMY_ATK_PARAMETER_DICT.get("perfect_player")) if perfect_player: """若是完美玩家将直接应用最短反应时间""" return round(theta / 1000 * 60) - Lp = ENEMY_ATK_PARAMETER_DICT.get("PlayerLevel", None) - if Lp is None: + Lp_val = ENEMY_ATK_PARAMETER_DICT.get("PlayerLevel", None) + if Lp_val is None: raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有PlayerLevel参数,请检查配置!") + Lp = cast(int, Lp_val) c = ENEMY_ATK_PARAMETER_DICT.get("c", None) if c is None: raise ValueError("ENEMY_ATK_PARAMETER_DICT中没有c参数,请检查配置!") @@ -166,10 +171,16 @@ def get_rt(self) -> int: def get_response_window(self) -> tuple[int, int]: """获取红黄光亮起的时间点""" + if not self.action: + raise ValueError("No action in progress") first_hit_tick = self.action.get_hit_tick() + self.last_start_tick - Ta = ENEMY_ATK_PARAMETER_DICT.get("Taction") + Ta_val = ENEMY_ATK_PARAMETER_DICT.get("Taction") + if not isinstance(Ta_val, (int, float)): + raise ValueError("Taction not configured in ENEMY_ATK_PARAMETER_DICT") + Ta = int(Ta_val) + left_bound = max( self.last_start_tick, first_hit_tick - Ta ) # 如果怪物前摇很短,动作时间也很短,那么怪物攻击动作开始的时间就是黄光亮起的时间。 @@ -178,6 +189,8 @@ def get_response_window(self) -> tuple[int, int]: def get_uncommon_response_window(self, another_ta: int) -> tuple[int, int]: """获取红黄光亮起的时间点,适用于非标准的进攻动作""" + if not self.action: + raise ValueError("No action in progress") first_hit_tick = self.action.get_hit_tick(another_ta=another_ta) + self.last_start_tick Ta = another_ta left_bonud = max(self.last_start_tick, first_hit_tick - Ta) @@ -193,7 +206,14 @@ def can_be_answered(self, rt_tick: int) -> tuple[bool, int, int]: f"当前动作:{self.action.tag}已经被{[_action.skill_tag for _action in self.answered_action]}响应过了!" ) return False, 0, 0 - Lp = ENEMY_ATK_PARAMETER_DICT.get("PlayerLevel") + Lp_val = ENEMY_ATK_PARAMETER_DICT.get("PlayerLevel") + if not isinstance(Lp_val, int): + raise ValueError("PlayerLevel not configured in ENEMY_ATK_PARAMETER_DICT") + Lp = Lp_val + + if self.interaction_window_close_tick is None or self.interaction_window_open_tick is None: + return False, 0, 0 + Td = self.interaction_window_close_tick - self.interaction_window_open_tick first_hit_tick = self.action.get_hit_tick() if Lp <= 2: @@ -244,6 +264,8 @@ def receive_single_hit(self, single_hit: "SingleHit", tick: int): def hit_check(self, tick: int) -> bool: """检查输入的tick是否存在命中节点""" + if not self.action: + return False if any([_hit_tick + self.last_start_tick == tick for _hit_tick in self.action.hit_list]): return True return False @@ -251,30 +273,31 @@ def hit_check(self, tick: int) -> bool: def single_hit_settlement(self, tick: int) -> None: """单次命中结算函数,用于结算当前tick的命中结果。""" sim_instance = self.enemy.sim_instance - char_on_field: int = sim_instance.preload.preload_data.operating_now + char_on_field_val = sim_instance.preload.preload_data.operating_now + if char_on_field_val is None: + return + char_on_field = char_on_field_val char_stack = sim_instance.preload.preload_data.personal_node_stack[char_on_field] self.hitted_count += 1 if char_stack is None: if ENEMY_ATTACK_REPORT: self.enemy.sim_instance.schedule_data.change_process_state() - print( - f"当前前台角色{char_on_field}并未进行有效交互(没有技能栈),将被打断!" - ) + print(f"当前前台角色{char_on_field}并未进行有效交互(没有技能栈),将被打断!") return nodes = char_stack.get_effective_node() if nodes is None: if ENEMY_ATTACK_REPORT: self.enemy.sim_instance.schedule_data.change_process_state() - print( - f"当前前台角色{char_on_field}并未进行有效交互(尚未行动过),将被打断!" - ) + print(f"当前前台角色{char_on_field}并未进行有效交互(尚未行动过),将被打断!") return if "interrupted" in nodes.skill_tag: print(f"角色{char_on_field}正处于被打断后摇中,打断后摇不刷新!") return - if ( - nodes.end_tick < tick - and not self.enemy.sim_instance.preload.strategy.apl_engine.apl.action_replace_manager.parry_aid_strategy.consecutive_parry_mode + if nodes.end_tick < tick and not ( + (apl := self.enemy.sim_instance.preload.strategy.apl_engine.apl) + and (arm := apl.action_replace_manager) + and (pas := arm.parry_aid_strategy) + and pas.consecutive_parry_mode ): if ENEMY_ATTACK_REPORT: self.enemy.sim_instance.schedule_data.change_process_state() @@ -296,10 +319,17 @@ def single_hit_settlement(self, tick: int) -> None: ActionReplaceManager, ) - action_replace_manager: ActionReplaceManager = ( + action_replace_manager: ActionReplaceManager | None = ( sim_instance.preload.strategy.apl_engine.apl.action_replace_manager ) - if self.action.hit == 1: + if not action_replace_manager: + return + + action = self.action + if not action: + return + + if action.hit == 1: self.enemy.sim_instance.schedule_data.change_process_state() print( f"【AtkEventManager】检测到来自角色{char_on_field}的招架技能{nodes.skill_tag},进攻交互式时间提前结束,角色即将被击退!" @@ -308,7 +338,7 @@ def single_hit_settlement(self, tick: int) -> None: action_replace_manager.parry_aid_strategy.final_parry_node = nodes self.event_end(tick=tick) else: - if self.action.hit == self.hitted_count: + if action.hit == self.hitted_count: action_replace_manager.parry_aid_strategy.knock_back_signal = True action_replace_manager.parry_aid_strategy.final_parry_node = nodes self.event_end(tick=tick) @@ -320,16 +350,12 @@ def single_hit_settlement(self, tick: int) -> None: if nodes.skill.trigger_buff_level in [2, 4, 5, 6, 7, 8, 9]: if ENEMY_ATTACK_REPORT: self.enemy.sim_instance.schedule_data.change_process_state() - print( - f"角色{char_on_field}选择释放{nodes.skill_tag}进行交互,交互成功。" - ) + print(f"角色{char_on_field}选择释放{nodes.skill_tag}进行交互,交互成功。") self.answered_count += 1 else: if ENEMY_ATTACK_REPORT: self.enemy.sim_instance.schedule_data.change_process_state() - print( - f"角色被打断!{nodes.skill_tag}技能被迫取消!" - ) + print(f"角色被打断!{nodes.skill_tag}技能被迫取消!") """取消当前正在进行的技能,同时添加一次被打断技能,模拟角色动作被打断。""" sim_instance.preload.preload_data.delete_mission_in_preload_data( node_be_changed=nodes diff --git a/zsim/sim_progress/data_struct/NormalAttackManager/BaseNAManager.py b/zsim/sim_progress/data_struct/NormalAttackManager/BaseNAManager.py index f5c9c458..c306c3fa 100644 --- a/zsim/sim_progress/data_struct/NormalAttackManager/BaseNAManager.py +++ b/zsim/sim_progress/data_struct/NormalAttackManager/BaseNAManager.py @@ -1,15 +1,20 @@ from abc import ABC +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from zsim.sim_progress.Character import Character + from zsim.sim_progress.Preload import SkillNode class BaseNAManager(ABC): - def __init__(self, char_obj, rule_inventory_dict: dict): - self.char = char_obj + def __init__(self, char_obj: "Character", rule_inventory_dict: dict): + self.char: "Character" = char_obj self.na_rule_inventory: dict = rule_inventory_dict self.RULE_MAP: dict = {"default": lambda: True} self.special_first_hit_list = [1141] @property - def first_hit(self): + def first_hit(self) -> str: """首次普攻""" return ( str(self.char.CID) + "_NA_1" @@ -17,7 +22,7 @@ def first_hit(self): else str(self.char.CID) + "_SNA_1" ) - def na_rule_selector(self) -> dict: + def na_rule_selector(self) -> dict[str, str]: """选择普攻策略!""" for rule_name, check_func in self.RULE_MAP.items(): if check_func(): @@ -25,7 +30,7 @@ def na_rule_selector(self) -> dict: else: return self.na_rule_inventory["default"] - def spawn_out_na(self, skill_node): + def spawn_out_na(self, skill_node: "SkillNode") -> str: """生成普攻""" _na_dict = self.na_rule_selector() if skill_node.skill_tag in _na_dict: diff --git a/zsim/sim_progress/data_struct/NormalAttackManager/NAManagerClasses.py b/zsim/sim_progress/data_struct/NormalAttackManager/NAManagerClasses.py index c9d91006..8f70e906 100644 --- a/zsim/sim_progress/data_struct/NormalAttackManager/NAManagerClasses.py +++ b/zsim/sim_progress/data_struct/NormalAttackManager/NAManagerClasses.py @@ -1,7 +1,7 @@ -from .BaseNAManager import BaseNAManager - from typing import TYPE_CHECKING +from .BaseNAManager import BaseNAManager + if TYPE_CHECKING: from zsim.sim_progress.Character.character import Character @@ -28,7 +28,7 @@ def first_hit(self) -> str: class HugoNAManager(BaseNAManager): - def __init__(self, char_obj, rule_inventory_dict: dict): + def __init__(self, char_obj: "Character", rule_inventory_dict: dict): super().__init__(char_obj, rule_inventory_dict) self.char = char_obj self.na_rule_inventory = rule_inventory_dict diff --git a/zsim/sim_progress/data_struct/QuickAssistSystem/__init__.py b/zsim/sim_progress/data_struct/QuickAssistSystem/__init__.py index 4529ee73..b63fa5ba 100644 --- a/zsim/sim_progress/data_struct/QuickAssistSystem/__init__.py +++ b/zsim/sim_progress/data_struct/QuickAssistSystem/__init__.py @@ -4,10 +4,11 @@ from zsim.sim_progress.Buff import JudgeTools -from .QuickAssistManager import QuickAssistManager +from .quick_assist_manager import QuickAssistManager if TYPE_CHECKING: from zsim.sim_progress.Character.character import Character + from zsim.sim_progress.Preload import SkillNode from zsim.simulator.simulator_class import Simulator @@ -21,7 +22,7 @@ def __init__(self, char_obj_list: list, sim_instance: Simulator): for char_obj in self.char_obj_list: self.quick_assist_manager_group[char_obj.NAME] = char_obj.dynamic.quick_assist_manager - def update(self, tick: int, skill_node, all_name_order_box: dict[str, list[str]]): + def update(self, tick: int, skill_node: "SkillNode", all_name_order_box: dict[str, list[str]]): """外部接口,通过传入的skill_node来判断如何激活快速支援。""" current_name_order_dict = all_name_order_box[skill_node.char_name] if skill_node.skill.aid_direction == 0: @@ -49,7 +50,7 @@ def update(self, tick: int, skill_node, all_name_order_box: dict[str, list[str]] ) self.answer_assist(tick, skill_node) - def answer_assist(self, tick: int, skill_node): + def answer_assist(self, tick: int, skill_node: "SkillNode"): """该函数用于在检测到角色响应了快速支援时,向Eventlist提前抛出结束事件。""" char_name = skill_node.char_name manager = self.quick_assist_manager_group[char_name] @@ -64,7 +65,9 @@ def answer_assist(self, tick: int, skill_node): event_list.append(end_event) # print(f'{skill_node.char_name}响应了快速支援!') - def spawn_event_group(self, tick_now: int, skill_node, active_manager: QuickAssistManager): + def spawn_event_group( + self, tick_now: int, skill_node: "SkillNode", active_manager: QuickAssistManager + ): """创建一个事件对,包含开始事件和结束事件,并将他们添加到event_list里面去。""" start_event = QuickAssistEvent( update_tick=tick_now, @@ -85,7 +88,7 @@ def spawn_event_group(self, tick_now: int, skill_node, active_manager: QuickAssi event_list.append(start_event) event_list.append(end_event) - def force_active_quick_assist(self, tick_now: int, skill_node, char_name: str): + def force_active_quick_assist(self, tick_now: int, skill_node: "SkillNode", char_name: str): """强制激活快速支援,主要是服务于外部调用。""" self.spawn_event_group(tick_now, skill_node, self.quick_assist_manager_group[char_name]) @@ -96,7 +99,7 @@ class QuickAssistEvent: def __init__( self, update_tick: int, - updated_by, + updated_by: "SkillNode", operation: bool, manager: QuickAssistManager, answer: bool = False, diff --git a/zsim/sim_progress/data_struct/QuickAssistSystem/QuickAssistManager.py b/zsim/sim_progress/data_struct/QuickAssistSystem/quick_assist_manager.py similarity index 81% rename from zsim/sim_progress/data_struct/QuickAssistSystem/QuickAssistManager.py rename to zsim/sim_progress/data_struct/QuickAssistSystem/quick_assist_manager.py index 25a6833d..f766a401 100644 --- a/zsim/sim_progress/data_struct/QuickAssistSystem/QuickAssistManager.py +++ b/zsim/sim_progress/data_struct/QuickAssistSystem/quick_assist_manager.py @@ -2,6 +2,7 @@ if TYPE_CHECKING: from zsim.sim_progress.Character import Character + from zsim.sim_progress.Preload import SkillNode class QuickAssistManager: @@ -15,24 +16,21 @@ def __init__(self, char: "Character"): self.quick_assist_available = False # 快速支援是否亮起 self.quick_assist_skill = f"{self.char.CID}_BH_Aid" # 快速支援技能名 self.assist_event_update_tick = 0 # 快速支援事件上报给eventlist的tick - self.last_update_node = None # 上一次导致快速支援激活的技能。 + self.last_update_node: "SkillNode | None" = None # 上一次导致快速支援激活的技能。 - def assist_waiting_for_anwser(self, tick: int): + def assist_waiting_for_anwser(self, tick: int) -> bool: """检查当前是否处于“快速支援即将触发但还未触发”的状态""" checked_node = self.last_update_node if checked_node is None: - """如果last_update_node是None,那说明当前根本没有技能尝试触发过快速支援,直接返回False""" + # 如果last_update_node是None,那说明当前根本没有技能尝试触发过快速支援,直接返回False return False - from zsim.sim_progress.Preload import SkillNode - if not isinstance(checked_node, SkillNode): - raise TypeError if checked_node.preload_tick + checked_node.skill.aid_lag_ticks > tick: return True else: return False - def state_change(self, tick: int, **kwargs): + def state_change(self, tick: int, **kwargs) -> None: """改变自身状态。""" operation = kwargs.get("operation", None) answer = kwargs.get("answer", False) # noqa: F841 diff --git a/zsim/sim_progress/data_struct/data_analyzer.py b/zsim/sim_progress/data_struct/data_analyzer.py index 14e60cce..20d23dfd 100644 --- a/zsim/sim_progress/data_struct/data_analyzer.py +++ b/zsim/sim_progress/data_struct/data_analyzer.py @@ -5,7 +5,7 @@ # from charset_normalizer.md import is_arabic_isolated_form -from zsim.define import BACK_ATTACK_RATE, ELEMENT_TYPE_MAPPING +from zsim.define import BACK_ATTACK_RATE from zsim.sim_progress.Report import report_to_log from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import NewAnomaly @@ -245,7 +245,6 @@ def __check_special_anomly(buff: "Buff", anomaly_node: "AnomalyBar") -> bool: ElectricAnomaly, AuricInkAnomaly, ) - from zsim.sim_progress.anomaly_bar.CopyAnomalyForOutput import NewAnomaly # 定义允许的标签类型 ALLOW_LABELS = ["only_anomaly"] diff --git a/zsim/sim_progress/data_struct/enemy_special_state_manager/__init__.py b/zsim/sim_progress/data_struct/enemy_special_state_manager/__init__.py index 6a447ebe..1e9a447c 100644 --- a/zsim/sim_progress/data_struct/enemy_special_state_manager/__init__.py +++ b/zsim/sim_progress/data_struct/enemy_special_state_manager/__init__.py @@ -1,2 +1,4 @@ -from .special_state_manager_class import SpecialStateManager from .special_state_class import EnemySpecialState +from .special_state_manager_class import SpecialStateManager + +__all__ = ["SpecialStateManager", "EnemySpecialState"] diff --git a/zsim/sim_progress/data_struct/enemy_special_state_manager/special_classes.py b/zsim/sim_progress/data_struct/enemy_special_state_manager/special_classes.py index b4821262..2272f80a 100644 --- a/zsim/sim_progress/data_struct/enemy_special_state_manager/special_classes.py +++ b/zsim/sim_progress/data_struct/enemy_special_state_manager/special_classes.py @@ -1,13 +1,17 @@ -from .special_state_class import EnemySpecialState -from zsim.define import ElementType, YUZUHA_REPORT, ELEMENT_TYPE_MAPPING as ETM -from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS from typing import TYPE_CHECKING +from zsim.define import ELEMENT_TYPE_MAPPING as ETM +from zsim.define import YUZUHA_REPORT, ElementType +from zsim.models.event_enums import SpecialStateUpdateSignal as SSUS + +from .special_state_class import EnemySpecialState + if TYPE_CHECKING: + from zsim.sim_progress.data_struct import SingleHit from zsim.sim_progress.Enemy import Enemy - from .special_state_manager_class import SpecialStateManager from zsim.sim_progress.Preload import SkillNode - from zsim.sim_progress.data_struct import SingleHit + + from .special_state_manager_class import SpecialStateManager class SweetScare(EnemySpecialState): @@ -69,7 +73,7 @@ def end(self): def update(self, update_signal: SSUS, **kwargs): """甜蜜惊吓的自更新函数,该函数需要在两个被广播式地调用,所以需要传入更新信号""" if update_signal == SSUS.RECEIVE_HIT: - self.__update_when_receive_hit(**kwargs) + self.__update_when_receive_hit(single_hit=kwargs.get("single_hit")) elif update_signal == SSUS.BEFORE_PRELOAD: self.__update_when_after_preload() elif update_signal == SSUS.CHARACTER: @@ -112,12 +116,11 @@ def flavor_match_update(self, skill_node: "SkillNode"): f"【甜蜜惊吓】状态被技能{skill_node.skill_tag}染为 {ETM.get(skill_node.element_type)} 属性!这将在接下来的2400tick内改变所有【彩糖花火】的属性!" ) - def __update_when_receive_hit(self, **kwargs): + def __update_when_receive_hit(self, single_hit: "SingleHit | None", **kwargs): """在受到攻击时执行,主要负责:彩糖花火·极、甜蜜惊吓状态的开启、染色状态的开启""" - single_hit: "SingleHit" = kwargs.get("single_hit") if single_hit is None: self.enemy.sim_instance.schedule_data.change_process_state() - print(f"【特殊状态:甜蜜惊吓 警告】在receive_hit的节点没有接收到预期中的SingleHit") + print("【特殊状态:甜蜜惊吓 警告】在receive_hit的节点没有接收到预期中的SingleHit") return """首先要检查的是彩糖花火·极的触发""" if single_hit.skill_tag in self.sugarburst_sparkless_max_trigger_origin_skill_tag: @@ -135,6 +138,7 @@ def __update_when_receive_hit(self, **kwargs): ) if YUZUHA_REPORT: self.sim_instance.schedule_data.change_process_state() + assert single_hit.skill_node is not None print( f"【甜蜜惊吓触发】由 {single_hit.skill_node.skill.skill_text} 触发了一次【彩糖花火·极】" ) @@ -157,7 +161,7 @@ def __update_when_receive_hit(self, **kwargs): if single_hit.skill_node is None: self.sim_instance.schedule_data.change_process_state() print( - f"【特殊状态:甜蜜惊吓警告】在试图更新染色状态时,检测到SingleHit并未关联SkillNode!染色失败!" + "【特殊状态:甜蜜惊吓警告】在试图更新染色状态时,检测到SingleHit并未关联SkillNode!染色失败!" ) return self.flavor_match_update(skill_node=single_hit.skill_node) @@ -182,7 +186,6 @@ def __update_when_after_preload(self, **kwargs): ) self.sugarburst_sparkless_update_tick = self.sim_instance.tick - def __update_when_in_character(self, **kwargs): + def __update_when_in_character(self, skill_node: "SkillNode", **kwargs): """在Character内部执行,主要负责染色""" - skill_node = kwargs.get("skill_node") self.try_change_attribute(skill_node=skill_node) diff --git a/zsim/sim_progress/data_struct/monitor_list_class.py b/zsim/sim_progress/data_struct/monitor_list_class.py index 0104b9a7..ced93b3e 100644 --- a/zsim/sim_progress/data_struct/monitor_list_class.py +++ b/zsim/sim_progress/data_struct/monitor_list_class.py @@ -1,5 +1,6 @@ def monitor_list_operation(func): """装饰器:监视列表操作并打印变化信息""" + def wrapper(self, *args, **kwargs): # 操作前状态 # print(f"Before {func.__name__}: {self}") @@ -8,6 +9,7 @@ def wrapper(self, *args, **kwargs): # 操作后状态 # print(f"After {func.__name__}: {self}") return result + return wrapper diff --git a/zsim/sim_progress/data_struct/single_hit.py b/zsim/sim_progress/data_struct/single_hit.py index 984c8306..933ffc73 100644 --- a/zsim/sim_progress/data_struct/single_hit.py +++ b/zsim/sim_progress/data_struct/single_hit.py @@ -1,6 +1,7 @@ from dataclasses import dataclass import numpy as np from typing import TYPE_CHECKING + if TYPE_CHECKING: from zsim.sim_progress.Preload import SkillNode @@ -17,10 +18,12 @@ class SingleHit: hitted_count: int proactive: bool # 该动作是否为主动技能(主要依靠检测skill_node的follow_by参数) heavy_hit = False # 重攻击标签——默认重攻击是 heavy_attack为True的技能的最后一个Hit - skill_node = None + skill_node: "SkillNode | None" = None def effective_anomlay_buildup(self) -> bool: """是否是有效积蓄""" + if self.skill_node is None: + return False return self.skill_node.effective_anomaly_buildup @property diff --git a/zsim/sim_progress/data_struct/summons_event/__init__.py b/zsim/sim_progress/data_struct/summons_event/__init__.py index b997659e..c8562bbc 100644 --- a/zsim/sim_progress/data_struct/summons_event/__init__.py +++ b/zsim/sim_progress/data_struct/summons_event/__init__.py @@ -1 +1,3 @@ from .summons_event_class import SummonsEvent + +__all__ = ["SummonsEvent"] diff --git a/zsim/sim_progress/summons/__init__.py b/zsim/sim_progress/summons/__init__.py index a800a429..cb044a74 100644 --- a/zsim/sim_progress/summons/__init__.py +++ b/zsim/sim_progress/summons/__init__.py @@ -1 +1,3 @@ from .summons_class import Summons + +__all__ = ["Summons"] \ No newline at end of file diff --git a/zsim/simulator/dataclasses.py b/zsim/simulator/dataclasses.py index 01e9ed26..5bb48b1b 100644 --- a/zsim/simulator/dataclasses.py +++ b/zsim/simulator/dataclasses.py @@ -2,14 +2,14 @@ from typing import TYPE_CHECKING, Literal from zsim.define import saved_char_config -from zsim.sim_progress import Buff +from zsim.models.session.session_run import CharConfig, CommonCfg +from zsim.sim_progress.Buff import Buff from zsim.sim_progress.Buff.Buff0Manager import Buff0ManagerClass, change_name_box from zsim.sim_progress.Character import Character, character_factory from zsim.sim_progress.data_struct import ActionStack from zsim.sim_progress.Enemy import Enemy -from zsim.models.session.session_run import CommonCfg, CharConfig -from .config_classes import SimulationConfig as SimCfg +from .config_classes import SimulationConfig as SimCfg if TYPE_CHECKING: from .simulator_class import Simulator @@ -103,13 +103,16 @@ def __api_init(self, common_cfg: CommonCfg): def __adjust_weapon_with_sim_cfg(self): # 根据sim_cfg调整武器配置 - if self.sim_cfg is not None and self.sim_cfg.func == "weapon": + from zsim.models.session.session_run import ExecWeaponCfg + + if self.sim_cfg is not None and isinstance(self.sim_cfg, ExecWeaponCfg): + if self.sim_cfg.adjust_char is None: + return adjust_char_index = ( self.sim_cfg.adjust_char - 1 ) # UI从1开始计数,这里需要转换为0开始的索引 if 0 <= adjust_char_index < len(self.name_box): char_dict_to_adjust = getattr(self, f"char_{adjust_char_index}") - # 更新武器名称和精炼等级 char_dict_to_adjust["weapon"] = self.sim_cfg.weapon_name char_dict_to_adjust["weapon_level"] = self.sim_cfg.weapon_level @@ -132,7 +135,13 @@ def __post_init__(self): sim_cfg = None if self.sim_cfg is not None and self.sim_cfg.adjust_char == i + 1: # UI那边不是从0开始数数的 - sim_cfg = self.sim_cfg + from zsim.models.session.session_run import ExecAttrCurveCfg, ExecWeaponCfg + + if isinstance(self.sim_cfg, (ExecAttrCurveCfg, ExecWeaponCfg)): + sim_cfg = self.sim_cfg + else: + # 如果类型不匹配,使用None + sim_cfg = None # 创建CharConfig对象 char_config = CharConfig(**char_dict) char_obj: Character = character_factory(char_config, sim_cfg=sim_cfg) @@ -164,37 +173,15 @@ def find_next_char_obj(self, char_now: int, direction: int = 1) -> Character: else: raise ValueError("direction参数错误!") else: - raise ValueError("未找到CID为%d的角色!" % char_now) - - def find_next_char_obj(self, char_now: int, direction: int = 1) -> Character: - """输入查找起点(CID),以及查找方向,返回下一位角色""" - __index = 0 - for char_obj in self.char_obj_list: - __index += 1 - if char_now != char_obj.CID: - continue - if direction == 1: - """顺向查找""" - if __index + 1 == len(self.char_obj_list): - return self.char_obj_list[0] - else: - return self.char_obj_list[__index] - elif direction == -1: - """逆向查找""" - if __index == 0: - return self.char_obj_list[-1] - else: - return self.char_obj_list[__index - 1] - else: - raise ValueError("direction参数错误!") - else: - raise ValueError("未找到CID为%d的角色!" % char_now) + raise ValueError(f"未找到CID为{char_now}的角色!") def reset_myself(self): for obj in self.char_obj_list: obj.reset_myself() - def find_char_obj(self, CID: int = None, char_name: str = None) -> Character | None: + def find_char_obj( + self, CID: int | None = None, char_name: str | None = None + ) -> Character | None: if not CID and not char_name: raise ValueError("查找角色时,必须提供CID或是char_name中的一个!") for char_obj in self.char_obj_list: @@ -221,7 +208,7 @@ class LoadData: all_name_order_box: dict = field(default_factory=dict) preload_tick_stamp: dict = field(default_factory=dict) char_obj_dict: dict | None = None - sim_instance: "Simulator" = None + sim_instance: "Simulator | None" = None def __post_init__(self): self.buff_0_manager = Buff0ManagerClass.Buff0Manager( @@ -262,9 +249,9 @@ class ScheduleData: char_obj_list: list[Character] event_list: list = field(default_factory=list) # judge_required_info_dict = {"skill_node": None} - loading_buff: dict[str, list[Buff.Buff]] = field(default_factory=dict) - dynamic_buff: dict[str, list[Buff.Buff]] = field(default_factory=dict) - sim_instance: "Simulator" = None + loading_buff: dict[str, list[Buff]] = field(default_factory=dict) + dynamic_buff: dict[str, list[Buff]] = field(default_factory=dict) + sim_instance: "Simulator | None" = None processed_event: bool = False # 记录已处理的事件次数, 给外部判断是否有事件发生, 便于前端跳过没有 event 的帧的 log # 实际执行时, 当 event 是 Preload.SkillNode | LoadingMission 时, 大多数情况是没有 log 输出的, 所以仍然会输出大量空帧. @@ -299,8 +286,8 @@ def reset_processed_event(self): @dataclass class GlobalStats: name_box: list - DYNAMIC_BUFF_DICT: dict[str, list[Buff.Buff]] = field(default_factory=dict) - sim_instance: "Simulator" = None + DYNAMIC_BUFF_DICT: dict[str, list[Buff]] = field(default_factory=dict) + sim_instance: "Simulator | None" = None def __post_init__(self): for name in self.name_box + ["enemy"]: diff --git a/zsim/utils/process_buff_result.py b/zsim/utils/process_buff_result.py index 2defd8ea..b90b8660 100644 --- a/zsim/utils/process_buff_result.py +++ b/zsim/utils/process_buff_result.py @@ -6,9 +6,7 @@ from typing import Any import polars as pl -import plotly.graph_objects as go from zsim.define import results_dir -from .constants import BUFF_EFFECT_MAPPING def _prepare_buff_timeline_data(df: pl.DataFrame) -> list[dict[str, Any]]: diff --git a/zsim/utils/process_dmg_result.py b/zsim/utils/process_dmg_result.py index 30fe575e..53f05950 100644 --- a/zsim/utils/process_dmg_result.py +++ b/zsim/utils/process_dmg_result.py @@ -1,12 +1,11 @@ import json import os -import plotly.express as px import polars as pl from zsim.define import ANOMALY_MAPPING from zsim.sim_progress.Character.skill_class import lookup_name_or_cid -from .constants import element_mapping, results_dir, SKILL_TAG_MAPPING +from .constants import results_dir, SKILL_TAG_MAPPING def _load_dmg_data(rid: int | str) -> pl.DataFrame | None: