├── .github └── workflows │ └── build.yaml ├── .gitignore ├── LICENSE ├── README.md ├── SECURITY.md ├── common ├── __init__.py ├── file.py ├── globle.py ├── helper.py ├── logger.py └── memory.py ├── example ├── assemble_asm.py └── drive_button.py ├── game ├── __init__.py ├── address.py ├── auto.py ├── call.py ├── fast_call.py ├── game_map.py ├── init.py ├── map_data.py ├── pack.py ├── screen.py ├── task.py └── traversal.py ├── gui.py ├── log.txt ├── main.py ├── plugins ├── api │ ├── __init__.py │ ├── advapi32.py │ └── kernel32.py ├── driver │ ├── __init__.py │ ├── button.py │ ├── derive.py │ └── keyboard.py └── logger │ ├── console.py │ ├── file.py │ ├── gui.py │ └── interface.py ├── requirements.txt └── static ├── WeGame.ico ├── helper.ini ├── mapId.txt ├── qq.png ├── wePay.png └── wechat.png /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python application 5 | 6 | on: 7 | push: 8 | branches: 9 | - "master" 10 | tags: 11 | - 'v*' 12 | pull_request: 13 | branches: 14 | - "master" 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: windows-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Set up Python 3.10 25 | uses: actions/setup-python@v3 26 | with: 27 | python-version: "3.10" 28 | 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install -r requirements.txt 33 | 34 | - name: Build 35 | run: | 36 | pyinstaller -F --icon=static/WeGame.ico -n DnfHelper-Console main.py 37 | pyinstaller -F --icon=static/WeGame.ico -n DnfHelper-Gui gui.py 38 | 39 | - name: Run UPX 40 | uses: crazy-max/ghaction-upx@v2 41 | with: 42 | version: latest 43 | files: | 44 | ./dist/*.exe 45 | args: -fq 46 | 47 | - name: Upload Release 48 | uses: softprops/action-gh-release@v1 49 | if: startsWith(github.ref, 'refs/tags/') 50 | with: 51 | token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} 52 | files: | 53 | dist/*.exe 54 | static/helper.ini 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .idea 132 | output 133 | helper.ini 134 | test.* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 如何启动 2 | 3 | ```markdown 4 | 1. 下载并安装python3.10.8 5 | https://www.python.org/ftp/python/3.10.8/python-3.10.8-amd64.exe) 6 | 2. git clone https://ghproxy.com/https://github.com/SunnyEmbrace/DnfHelper-Python.git 7 | 3. pip install -i https://mirrors.cloud.tencent.com/pypi/simple -r requirements.txt 8 | 4. 复制根目录static下的helper.ini和main.py放同一级目录 9 | 5. *** 管理员方式运行 python main.py 然后登录游戏*** 10 | 6. 功能热键 F1.全屏打怪 End.自动刷图 11 | ``` 12 | ### 点击链接加入群聊【毒奶粉研究院】:https://t.me/+D3V4dfGWE_JjYzU1 13 | 14 | ## Stargazers over time 15 | 16 | [![Stargazers over time](https://starchart.cc/SunnyEmbrace/DnfHelper-Python.svg)](https://starchart.cc/SunnyEmbrace/DnfHelper-Python) 17 | 18 | ## 免责声明 19 | 20 | 程序是免费开源的产品,仅用于学习交流使用! 21 | 不可用于任何违反`中华人民共和国(含台湾省)`或`使用者所在地区`法律法规的用途。 22 | 因为作者即本人仅完成代码的开发和开源活动`(开源即任何人都可以下载使用)`,从未参与用户的任何运营和盈利活动。 23 | 且不知晓用户后续将`程序源代码`用于何种用途,故用户使用过程中所带来的任何法律责任即由用户自己承担。 24 | 25 | ## JetBrains 支持的项目 26 | 27 | 非常感谢 Jetbrains 友好地为我提供了一个许可,让我可以从事这个项目和其他开源项目。 28 | 29 | [![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/qiuapeng921) 30 | 31 | ## License 32 | 33 | MIT 34 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- 1 | from configparser import ConfigParser 2 | 3 | 4 | def config(): 5 | conf = ConfigParser() 6 | conf.read('helper.ini', encoding="utf-8-sig") 7 | return conf 8 | -------------------------------------------------------------------------------- /common/file.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import os 3 | import time 4 | from configparser import ConfigParser 5 | 6 | 7 | def path_exists(path): 8 | return os.path.exists(path) 9 | 10 | 11 | def write_ini(filename, section, key, value): 12 | exists = os.path.exists(filename) 13 | cfg = ConfigParser() 14 | 15 | if exists: 16 | cfg.read(filename, encoding="utf-8-sig") 17 | 18 | if not cfg.has_section(section) and not cfg.has_option(section, key): 19 | cfg.add_section(section) 20 | 21 | cfg.set(section, key, str(value)) 22 | 23 | with open(filename, 'w') as configfile: 24 | cfg.write(configfile) 25 | 26 | 27 | def read_ini(filename, section, key) -> str: 28 | cfg = ConfigParser() 29 | cfg.read(filename, encoding="utf-8-sig") 30 | 31 | if cfg.has_section(section) and cfg.has_option(section, key): 32 | return cfg.get(section, key) 33 | 34 | return "" 35 | 36 | 37 | def force_delete_file(file_path: str): 38 | """ 39 | 强制删除文件 40 | :param file_path: 文件路径 41 | :return: 42 | """ 43 | kernel32 = ctypes.windll.kernel32 44 | 45 | # 使用unicode字符串 46 | CreateDirectory = kernel32.CreateDirectoryW 47 | MoveFile = kernel32.MoveFileW 48 | DeleteFile = kernel32.DeleteFileW 49 | RemoveDirectory = kernel32.RemoveDirectoryW 50 | 51 | class SECURITY_ATTRIBUTES(ctypes.Structure): 52 | _fields_ = [ 53 | ("Length", ctypes.c_ulong), 54 | ("SecurityDescriptor", ctypes.c_void_p), 55 | ("InheritHandle", ctypes.c_ulong), 56 | ] 57 | 58 | temp_dir = os.path.join(os.environ["TEMP"], str(int(time.time()))) 59 | temp_dir_wchar = ctypes.c_wchar_p(temp_dir) 60 | 61 | sa = SECURITY_ATTRIBUTES() 62 | sa.Length = ctypes.sizeof(SECURITY_ATTRIBUTES) 63 | sa.SecurityDescriptor = None 64 | sa.InheritHandle = 0 65 | 66 | res1 = CreateDirectory(temp_dir_wchar, ctypes.pointer(sa)) 67 | res2 = CreateDirectory(ctypes.c_wchar_p(temp_dir + "\\....\\"), ctypes.pointer(sa)) 68 | res3 = MoveFile(ctypes.c_wchar_p(file_path), ctypes.c_wchar_p(temp_dir + "\\....\\TemporaryFile")) 69 | res4 = MoveFile(ctypes.c_wchar_p(temp_dir + "\\....\\"), ctypes.c_wchar_p(temp_dir + "\\TemporaryFile")) 70 | res5 = DeleteFile(ctypes.c_wchar_p(temp_dir + "\\TemporaryFile\\TemporaryFile")) 71 | res6 = RemoveDirectory(ctypes.c_wchar_p(temp_dir + "\\TemporaryFile")) 72 | res7 = RemoveDirectory(temp_dir_wchar) 73 | 74 | print(res1, res2, res3, res4, res5, res6, res7) 75 | 76 | 77 | def main(): 78 | # file_path = "C:\\TPqd640.sys" # 更改为实际的文件路径 79 | # force_delete_file(file_path) 80 | # print("文件已成功删除") 81 | 82 | cfg_name = "C:\\666.ini" 83 | conf_exists = path_exists(cfg_name) 84 | if not conf_exists: 85 | write_ini(cfg_name, "default", "count", "1") 86 | 87 | complete_number = read_ini(cfg_name, "default", "count") 88 | print(complete_number) 89 | 90 | 91 | if __name__ == "__main__": 92 | main() 93 | -------------------------------------------------------------------------------- /common/globle.py: -------------------------------------------------------------------------------- 1 | cmd = "cmd" 2 | 3 | win_app = None 4 | 5 | 6 | # 全局数据 7 | class GlobalData: 8 | # 自动开关 9 | auto_switch = False 10 | # 任务编号 11 | task_id = 0 12 | # 副本编号 13 | map_id = 0 14 | # 副本难度 15 | map_level = 0 16 | # 角色总数 17 | role_count = 0 18 | # 完成角色 19 | completed_role = 0 20 | 21 | 22 | # 坐标结构 23 | class CoordinateType: 24 | x, y, z = (0, 0, 0) 25 | 26 | def __init__(self): 27 | self.x = 0 28 | self.y = 0 29 | self.z = 0 30 | 31 | 32 | # 地图数据 33 | class MapDataType: 34 | map_name = "" # 地图名称 35 | map_um = 0 # 地图编号 36 | map_channel = [int] # 地图通道 37 | start_zb = CoordinateType() # 起始坐标 38 | end_zb = CoordinateType() # 终点坐标 39 | width = 0 # 宽 40 | height = 0 # 高 41 | map_route = [] # 地图走法 42 | consume_fatigue = 0 # 消耗疲劳 43 | channel_num = 0 # 通道数量 44 | tmp = 0 # 临时变量 45 | 46 | def __init__(self): 47 | self.map_name = "" 48 | self.map_um = 0 49 | self.map_channel = [] 50 | self.start_zb = CoordinateType() 51 | self.end_zb = CoordinateType() 52 | self.width = 0 53 | self.height = 0 54 | self.map_route = [] 55 | self.consume_fatigue = 0 56 | self.channel_num = 0 57 | self.tmp = 0 58 | 59 | 60 | # 游戏地图 61 | class GameMapType: 62 | map_coordinates = CoordinateType() # 地图坐标 63 | left = False # 地图左边 64 | right = False # 地图右边 65 | up = False # 地图上边 66 | down = False # 地图下边 67 | map_channel = 0 # 地图通道 68 | background_color = 0 # 背景颜色 69 | 70 | def __init__(self): 71 | self.map_coordinates = CoordinateType() 72 | self.left = False 73 | self.right = False 74 | self.up = False 75 | self.down = False 76 | self.map_channel = 0 77 | self.background_color = 0 78 | 79 | 80 | # 地图节点 81 | class MapNodeType: 82 | f = 0 # 地图F点 83 | g = 0 # 地图G点 84 | h = 0 # 地图H点 85 | current_coordinates = CoordinateType() # 当前坐标 86 | final_coordinates = CoordinateType() # 最终坐标 87 | 88 | def __init__(self): 89 | self.f = 0 90 | self.g = 0 91 | self.h = 0 92 | self.current_coordinates = CoordinateType() 93 | self.final_coordinates = CoordinateType() 94 | 95 | 96 | class MapTraversalType: 97 | rw_addr = 0 # 地图地址 98 | map_data = 0 # 地图数据 99 | start = 0 # 起始位置 100 | end = 0 # 结束位置 101 | obj_num = 0 # 物体数量 102 | obj_tmp = 0 # 物体临时变量 103 | obj_ptr = 0 # 物体指针 104 | obj_camp = 0 # 物体阵营 105 | obj_blood = 0 # 物体血量 106 | obj_type_a = 0 # 物体类型A 107 | obj_type_b = 0 # 物体类型B 108 | obj_code = 0 # 物体代码 109 | obj_name_a = "" # 物体名称A 110 | obj_name_b = "" # 物体名称B 111 | rw_coordinate = CoordinateType() # 地图坐标类型 112 | gw_coordinate = CoordinateType() # 游戏坐标类型 113 | wp_coordinate = CoordinateType() # 终点坐标类型 114 | 115 | def __init__(self): 116 | # 地图遍历类型的属性 117 | self.rw_addr = 0 118 | self.map_data = 0 119 | self.start = 0 120 | self.end = 0 121 | self.obj_num = 0 122 | self.obj_tmp = 0 123 | self.obj_ptr = 0 124 | self.obj_camp = 0 125 | self.obj_blood = 0 126 | self.obj_type_a = 0 127 | self.obj_type_b = 0 128 | self.obj_code = 0 129 | self.obj_name_a = "" 130 | self.obj_name_b = "" 131 | self.rw_coordinate = CoordinateType() 132 | self.gw_coordinate = CoordinateType() 133 | self.wp_coordinate = CoordinateType() 134 | -------------------------------------------------------------------------------- /common/helper.py: -------------------------------------------------------------------------------- 1 | import random 2 | import struct 3 | import sys 4 | import time 5 | import traceback 6 | from datetime import datetime 7 | 8 | import psutil 9 | import win32api 10 | import win32gui 11 | 12 | 13 | def get_process_name(): 14 | # 获取当前活动窗口句柄 15 | hwnd = win32gui.GetForegroundWindow() 16 | 17 | # 获取窗口标题 18 | window_title = win32gui.GetWindowText(hwnd) 19 | 20 | return window_title 21 | 22 | 23 | def get_process_id_by_name(name: str) -> int: 24 | pid = 0 25 | ps = psutil.process_iter() 26 | for p in ps: 27 | if p.name() == name: 28 | pid = p.pid 29 | break 30 | 31 | return pid 32 | 33 | 34 | def find_window(lp_class_name, lp_window_name): 35 | """ 36 | 获取指定窗口句柄 37 | """ 38 | h_wnd = win32gui.FindWindow(lp_class_name, lp_window_name) 39 | if h_wnd == 0: 40 | return None 41 | return h_wnd 42 | 43 | 44 | def get_module_handle(pid: int, name: str) -> int: 45 | """ 46 | 获取模块句柄 47 | :param pid: 进程id 48 | :param name: 模块名称 49 | :return: 50 | """ 51 | process = psutil.Process(pid) 52 | # 获取进程的所有模块句柄 53 | modules = process.memory_maps() 54 | # 遍历所有模块句柄并打印 55 | for module in modules: 56 | module_name = module.path.split("\\") 57 | if module_name[3] == name: 58 | return module.rss 59 | 60 | 61 | start_time = datetime.now() # 记录程序启动时间 62 | 63 | 64 | def get_app_run_time(): 65 | """返回程序运行时间的格式化字符串""" 66 | current_time = datetime.now() # 获取当前时间 67 | duration = current_time - start_time # 计算时间差 68 | 69 | hours, remainder = divmod(duration.total_seconds(), 3600) 70 | minutes, seconds = divmod(remainder, 60) 71 | 72 | # 格式化时间字符串 73 | time_string = f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}" 74 | return time_string 75 | 76 | 77 | def get_now_date(): 78 | """ 79 | get_now_date 获取系统当前日期 80 | :return: string 81 | """ 82 | now = datetime.now() 83 | current_time = now.strftime("%Y-%m-%d %H:%M:%S") 84 | return current_time 85 | 86 | 87 | def int_to_bytes(int_val, int_type): 88 | """ 89 | int转bytes 90 | :param int_val: int 91 | :param int_type: 92 | :return: bytes 93 | """ 94 | if int_type == 2: 95 | return struct.pack(' b'\x83\x84\xec\x00\x01\x00\x00\x01\x02\x03\x04' 122 | """ 123 | ret_bytes = add_list(list(old_bytes), *new_bytes_arr) 124 | return bytes(ret_bytes) 125 | 126 | 127 | def add_list(old_list: list, *new_list_arr: list) -> list: 128 | """ 129 | 追加list 130 | :param old_list: list 131 | :param new_list_arr: 132 | :return: bytes 133 | # Example: add_byte([72], [129], [236, 0, 1, 0, 0], [1, 2, 3, 4]) -> [72, 129, 236, 0, 1, 0, 0, 1, 2, 3, 4] 134 | """ 135 | if len(new_list_arr) == 0: 136 | return old_list 137 | for list_arr in new_list_arr: 138 | old_list += list_arr 139 | return old_list 140 | 141 | 142 | def get_empty_bytes(count: int) -> bytes: 143 | result = list() 144 | for i in range(count): 145 | result.append(0) 146 | 147 | return bytes(result) 148 | 149 | 150 | def message_box(msg): 151 | win32api.MessageBoxEx(0, msg, "Helper") 152 | 153 | 154 | def ascii_to_unicode(string: str) -> list: 155 | bytes_arr = bytes() 156 | for c in string: 157 | hex_int = ord(c) 158 | bytes_arr = bytes_arr + hex_int.to_bytes(2, byteorder='little') 159 | 160 | return list(bytes_arr) 161 | 162 | 163 | def unicode_to_ascii(ls: list) -> str: 164 | if isinstance(ls, bytes): 165 | ls = list(ls) 166 | 167 | text = "" 168 | for i in range(0, len(ls), 2): 169 | if ls[i] == 0 and ls[i + 1] == 0: 170 | break 171 | a = ls[i + 1] << 8 172 | b = ls[i] 173 | text += chr(a + b) 174 | 175 | return text 176 | 177 | 178 | def sleep(timer: int): 179 | time.sleep(timer / 1000.0) 180 | 181 | 182 | def array_rand(data: list): 183 | index = random.randint(0, len(data) - 1) 184 | return data[index] 185 | 186 | 187 | def print_trace(title: str, err): 188 | print("-----------{}:出错-----------".format(title)) 189 | except_type, _, except_traceback = sys.exc_info() 190 | err_str = ','.join(str(i) for i in err.args) 191 | print(except_type) 192 | print(err_str) 193 | for i in traceback.extract_tb(except_traceback): 194 | print("函数{},文件:{},行:{}".format(i.name, i.filename, i.lineno)) 195 | -------------------------------------------------------------------------------- /common/logger.py: -------------------------------------------------------------------------------- 1 | from common import globle 2 | from plugins.logger.console import ConsoleLog 3 | from plugins.logger.file import FileLog 4 | from plugins.logger.gui import GuiLog 5 | 6 | console_log = ConsoleLog() 7 | gui_log = GuiLog() 8 | file_log = FileLog() 9 | 10 | 11 | def info(msg: str, t: int): 12 | """ 13 | :param msg: 14 | :param t: 1 系统 2 普通 15 | :return: 16 | """ 17 | if globle.cmd == "cmd": 18 | if t == 1: 19 | console_log.info(msg) 20 | else: 21 | console_log.debug(msg) 22 | if globle.cmd == "gui": 23 | if t == 1: 24 | gui_log.info(msg) 25 | else: 26 | gui_log.debug(msg) 27 | -------------------------------------------------------------------------------- /common/memory.py: -------------------------------------------------------------------------------- 1 | from pymem import Pymem 2 | 3 | from common import logger 4 | 5 | 6 | class Memory: 7 | # 全局进程id 8 | processId = int 9 | # 读写对象 10 | pm = Pymem 11 | 12 | def __int__(self): 13 | pass 14 | 15 | def set_process_id(self, process_id): 16 | self.pm = Pymem(process_id) 17 | self.processId = process_id 18 | 19 | def read_int(self, address: int) -> int: 20 | try: 21 | return self.pm.read_int(address) 22 | except Exception as e: 23 | logger.file_log.error("read_int 地址:{},错误:{}".format(address, e.args)) 24 | 25 | def read_long(self, address: int) -> int: 26 | try: 27 | return self.pm.read_longlong(address) 28 | except Exception as e: 29 | logger.file_log.error("read_longlong 地址:{},错误:{}".format(address, e.args)) 30 | 31 | def read_float(self, address: int) -> float: 32 | try: 33 | return self.pm.read_float(address) 34 | except Exception as e: 35 | logger.file_log.error("read_float 地址:{},错误:{}".format(address, e.args)) 36 | 37 | def read_bytes(self, address: int, length: int) -> bytes: 38 | try: 39 | return self.pm.read_bytes(address, length) 40 | except Exception as e: 41 | logger.file_log.error("read_bytes 地址:{},错误:{}".format(address, e.args)) 42 | 43 | def write_int(self, address: int, value: int): 44 | try: 45 | return self.pm.write_int(address, value) 46 | except Exception as e: 47 | logger.file_log.error("write_int 地址:{} 值:{},错误:{}".format(address, value, e.args)) 48 | 49 | def write_long(self, address: int, value: int): 50 | try: 51 | return self.pm.write_longlong(address, value) 52 | except Exception as e: 53 | logger.file_log.error("write_longlong 地址:{} 值:{},错误:{}".format(address, value, e.args)) 54 | 55 | def write_float(self, address: int, value: float): 56 | try: 57 | return self.pm.write_float(address, value) 58 | except Exception as e: 59 | logger.file_log.error("write_float 地址:{} 值:{},错误:{}".format(address, value, e.args)) 60 | 61 | def write_bytes(self, address: int, value: bytes): 62 | try: 63 | return self.pm.write_bytes(address, value, len(value)) 64 | except Exception as e: 65 | logger.file_log.error("write_bytes 地址:{} 值:{},错误:{}".format(address, value, e.args)) 66 | 67 | def allocate(self, length) -> int: 68 | return self.pm.allocate(length) 69 | -------------------------------------------------------------------------------- /example/assemble_asm.py: -------------------------------------------------------------------------------- 1 | from keystone import * 2 | 3 | 4 | def assemble_asm(asm_code): 5 | try: 6 | ks = Ks(KS_ARCH_X86, KS_MODE_64) # 设置目标架构为 x86_64 7 | encoding, _ = ks.asm(asm_code) 8 | return bytes(encoding) 9 | except KsError as e: 10 | print(f"Assembly failed: {e}") 11 | return None 12 | 13 | 14 | def main(): 15 | asm_code = "mov rax, 0x1234567890ABCDEF" 16 | bytecode = assemble_asm(asm_code) 17 | 18 | if bytecode: 19 | print("Assembly succeeded:") 20 | print(bytecode) 21 | print(list(bytecode)) 22 | list(bytecode) 23 | hex_bytecode = ' '.join([hex(byte) for byte in bytecode]) 24 | print(hex_bytecode) 25 | 26 | 27 | if __name__ == '__main__': 28 | main() 29 | -------------------------------------------------------------------------------- /example/drive_button.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from common import helper 4 | from plugins.driver.button import drive_button 5 | from plugins.driver.keyboard import * 6 | 7 | 8 | def main(): 9 | print("卧槽") 10 | 11 | 12 | if __name__ == "__main__": 13 | time.sleep(3) 14 | drive_button(VK_LEFT, 0, True) 15 | drive_button(VK_LEFT, 1, True) 16 | drive_button(VK_UP, 1, True) 17 | time.sleep(3) 18 | drive_button(VK_LEFT, 2, True) 19 | drive_button(VK_UP, 2, True) 20 | 21 | time.sleep(3) 22 | drive_button(VK_X, 0, False) 23 | time.sleep(3) 24 | drive_button(VK_C, 0, False) 25 | time.sleep(3) 26 | drive_button(VK_X, 1, False) 27 | helper.sleep(800) 28 | drive_button(VK_X, 2, False) 29 | helper.sleep(100) 30 | -------------------------------------------------------------------------------- /game/__init__.py: -------------------------------------------------------------------------------- 1 | from common import memory 2 | from common.helper import array_rand 3 | from plugins.driver.keyboard import * 4 | 5 | mem = memory.Memory() 6 | 7 | 8 | def rand_skill() -> int: 9 | code = [ 10 | VK_A, VK_S, VK_D, VK_F, VK_G, VK_H, 11 | VK_Q, VK_W, VK_E, VK_R, VK_T, VK_Y, 12 | ] 13 | return array_rand(code) 14 | -------------------------------------------------------------------------------- /game/address.py: -------------------------------------------------------------------------------- 1 | RwKbAddr = 0 # 人物基址 2 | BuffKbAddr = 0 # buff地址 3 | NcBhKbAddr = 0 # 内存汇编 4 | PtGgKbAddr = 0 # 普通公告 5 | JnKbAddr = 0 # 技能Call 6 | GtKbAddr = 0 # 过图Call 7 | CoolDownKbAddr = 0 # 冷却判断call 8 | 9 | RwAddr = 0x14C2503F8 # 新人物基址 10 | RwAddr1 = 0x14C2503F0 # 人物基址 11 | RwAddr2 = 0x14B8E1D08 # 人物基址B 12 | RWCallAddr = 0x144CA54B0 # 人物CALL 13 | JSDjAddr = 0x14B957BE0 # 角色等级 14 | PFAddr = 0x14B971380 # 评分基址 15 | GGCsAddr = 0x14C251758 # 公告参数 16 | GGCallAddr = 0x144D712C0 # 公告CALL 17 | BbJzAddr = 0x14B972668 # 背包基址 18 | JSPtrAddr = 0x14B9723B0 # 角色指针 19 | CzDqyAddr = 0x14B92F7AC # 城镇大区域 20 | CzXqyAddr = 0x14B92F7B0 # 城镇小区域 21 | YXZTAddr = 0x14B4B3F20 # 游戏状态 22 | SNBBAddr = 0x14B9726C0 # 司南背包 23 | YrBbAddr = 0x14B9726B8 # 玉荣背包 24 | BxrBbAddr = 0x14B9726B8 # 辟邪玉背包 25 | SnAddCallAddr = 0x141DDF8D0 # 司南添加CALL 26 | SnJtRcxAddr = 0x14B920068 # 司南进图_Rcx 27 | SnJtCallAddr = 0x141DC2C00 # 司南进图CALL 28 | SnAddRcxAddr = 0x145460CF0 # 取司南添加RCX 29 | YrlPyAddr = 0x600 # 玉荣力偏移 30 | JsYrlAddr = 0x5408 # 角色玉荣力 31 | HBCallAddr = 0x13FDC0000 # 汇编CALL 32 | TranslateMessage = 0x1477C6CC0 # TranslateMessage 33 | GameTimeGetTime = 0x1477C70F8 # GameTimeGetTime 34 | JNCallAddr = 0x14480BB70 # 技能CALL 35 | JwCallAddr = 0x144AA4D10 # 聚物CALL 36 | JwXyAddr = 0xFE24 # 聚物校验 37 | TaskAddr = 0x14B972750 # 任务基址 38 | JsCallAddr = 0x1440FCD40 # 接受CALL 39 | WcCallAddr = 0x1440FD350 # 完成CALL 40 | TjCallAddr = 0x1440FCE30 # 提交CALL 41 | TgCallAddr = 0x143E98F50 # 跳过CALL 42 | AjAddr = 0x14C70EC10 # 按键基址 43 | DHAddr = 0x14C2A42E8 # 对话基址 44 | DHAddrB = 0x14B7A84C0 # 对话基址B 45 | EscDHAddr = 0x14B7A84E0 # Esc对话基址 46 | FpAddr = 0x14B972660 # 翻牌基址 47 | FbBhAddr = 0x14B957B70 # 副本编号 48 | SJAddr = 0x20A050 # 时间基址 49 | FJBHAddr = 0x14B972650 # 房间编号 50 | MaxPlAddr = 0x14C25032C # 最大疲劳 51 | CutPlAddr = 0x14C25039C # 当前疲劳 52 | QyParamAddr = 0x14C2A9830 # 区域参数 53 | QyCallAddr = 0x145AB95C0 # 区域CALL 54 | QyPyAddr = 0xA9FA8 # 区域偏移 55 | XTuCallAddr = 0x145AF90E0 # 选图CALL 56 | JTuCallAddr = 0x145B398F0 # 进图CALL 57 | HChengCallAddr = 0x14589EE90 # 回城CALL 58 | GtCallAddr = 0x143C32B80 # 过图CALL 59 | PyCall1Addr = 0x143A84420 # 漂移CALL 60 | PyCall2Addr = 0x145C531D0 # 漂移CALL2 61 | BpCallAddr = 0x14403E760 # 奔跑CALL 62 | XrNcCallAddr = 0x144CE04F0 # 写入内存 63 | BpPyAddr1 = 0x1208 # 奔跑偏移_1 64 | BpPyAddr2 = 0x11F0 # 奔跑偏移_2 65 | CzSyRdxAddr = 0x14B9435E8 # 城镇瞬移_Rdx 66 | CzSyCallAddr = 0x145B001A0 # 城镇瞬移CALL 67 | XzJsCallAddr = 0x1404FD580 # 选择角色CALL 68 | FhJsCallAddr = 0x144513860 # 返回角色CALL 69 | LqCallJudgeAddr = 0x144C91AE0 # 冷却判断CALL 70 | CdResetCallAddr = 0x144AF47E0 # CD重置CALL 71 | FjCallAddr = 0x1448F0FB0 # 分解CALL 72 | ZlCallAddr = 0x1448E73A0 # 整理CALL 73 | DqFzAddr = 0x14C2A5B38 # 当前负重 74 | ZdFzAddr = 0x2DB8 # 最大负重 75 | FbAddr = 0x14C2AA440 # 发包基址 76 | HcCallAddr = 0x145B64E70 # 缓冲CALL 77 | FbCallAddr = 0x145B65B60 # 发包CALL 78 | JmB1CallAddr = 0x145B65CD0 # 加密包CALL 79 | JmB2CallAddr = 0x145B66050 # 加密包CALL2 80 | JmB3CallAddr = 0x145B65CF0 # 加密包CALL4 81 | JmB4CallAddr = 0x145B65D10 # 加密包CALL8 82 | SqNcCallAddr = 0x143A59560 # 申请内存 83 | BUffMemRcxAddr = 0x14B9725A8 # BUFF内存_RCX 84 | BUffMemCallAddr = 0x145B81B80 # BUFF内存CALL 85 | DyBuffCall = 0x144CDC830 # 调用BUFFCALL 86 | TakeEffectCallAddr = 0x144A19C20 # 生效CALL 87 | PutOnCallAddr = 0x144AB7A70 # 穿上CALL 88 | TmCallAddr = 0x145B93BA0 # 透明CALL 89 | CreateCallAddr = 0x144DBCF00 # 创建CALL 90 | WpYdCallAddr = 0x1448DDA40 # 物品移动CALL 91 | JnSwAddr = 0x144A605C1 # 技能三无 92 | RwMwAddr = 0x11E54 # 人物名望 93 | WpMcAddr = 0x40 # 物品名称 94 | WpJyLxAddr = 0xA8 # 物品交易类型 95 | DzIDAddr = 0x436C # 动作ID 96 | DtKs2 = 0x1B8 # 地图开始2 97 | DtJs2 = 0x1C0 # 地图结束2 98 | DtPyAddr = 0x168 # 地图偏移 99 | LxPyAddr = 0x134 # 类型偏移 100 | FxPyAddr = 0x148 # 方向偏移 101 | CEPfAddr = 0x88 # 评分偏移 102 | FbSqAddr = 0x13C # 发包拾取 103 | GwXlAddr = 0x4F78 # 怪物血量 104 | ZyPyAddr = 0xEB8 # 阵营偏移 105 | DmWpAddr = 0x2B70 # 地面物品 106 | JxWpAddr = 0xF950 # 脚下物品 107 | DmPyAddr = 0x868 # 代码偏移 108 | McPyAddr = 0x870 # 名称偏移 109 | ZbPjAddr = 0x2B8 # 装备品级 110 | DtCtAddr = 0x878 # 地图穿透 111 | JzCtAddr = 0x87C # 建筑穿透 112 | DqZbAddr = 0x328 # 读取坐标 113 | YjRwStartAddr = 0x10 # 已接任务首地址 114 | YjRwEndAddr = 0x18 # 已接任务尾地址 115 | QbRwStartAddr = 0xA8 # 全部任务首地址 116 | QbRwEndAddr = 0xB0 # 全部任务尾地址 117 | RwLxAddr = 0x218 # 任务类型 118 | RwDxAddr = 0x28 # 任务大小 119 | RwTjAddr = 0x4D0 # 任务条件 120 | RwDjAddr = 0x328 # 任务等级 121 | RwFbAddr = 0x488 # 任务副本 122 | SfKmAddr = 0x27C # 是否开门 123 | CutRoomXAddr = 0x1C98 # 当前房间X 124 | CutRoomYAddr = 0x1C9C # 当前房间Y 125 | BOSSRoomXAddr = 0x1D98 # BOSS房间X 126 | BOSSRoomYAddr = 0x1D9C # BOSS房间Y 127 | GouHuoAddr = 0x1E28 # 篝火判断 128 | SyPyAddr = 0x1D8C # 索引偏移 129 | MxPyAddr = 0x128 # 门型偏移 130 | KgPyAddr = 0x890 # 宽高偏移 131 | SzPyAddr = 0x8B0 # 数组偏移 132 | DtMcAddr = 0x418 # 地图名称 133 | StPyAddr = 0xC0 # 顺图偏移 134 | ZbStPyAddr = 0x3848 # 坐标顺图 135 | FxIdAddr = 0xE8 # 方向ID 136 | WplAddr = 0xFD98 # 物品栏 137 | WplPyAddr = 0xA8 # 物品栏偏移 138 | JnlAddr = 0xFD10 # 技能栏 139 | JnlPyAddr = 0x90 # 技能栏偏移 140 | -------------------------------------------------------------------------------- /game/auto.py: -------------------------------------------------------------------------------- 1 | """ 2 | 自动刷图主线程 3 | """ 4 | import _thread 5 | import random 6 | import time 7 | 8 | from common import config, file 9 | from common import helper, logger 10 | from game import call, init, address 11 | from game import mem 12 | from plugins.driver.button import drive_button 13 | from plugins.driver.keyboard import * 14 | 15 | 16 | class Auto: 17 | # 首次进图 18 | firstEnterMap = False 19 | # 已完成刷图次数 20 | completedNum = 0 21 | # 线程开关 22 | thread_switch = False 23 | # 线程句柄 24 | threadHande = None 25 | 26 | task = None 27 | traversal = None 28 | map_data = None 29 | pack = None 30 | game_map = None 31 | 32 | @classmethod 33 | def __init__(cls, task, traversal, map_data, pack, game_map): 34 | cls.task = task 35 | cls.traversal = traversal 36 | cls.map_data = map_data 37 | cls.pack = pack 38 | cls.game_map = game_map 39 | 40 | @classmethod 41 | def switch(cls): 42 | """自动开关""" 43 | init.global_data.auto_switch = not init.global_data.auto_switch 44 | cls.thread_switch = init.global_data.auto_switch 45 | if cls.thread_switch: 46 | cls.threadHande = _thread.start_new_thread(cls.auto_thread, ()) 47 | logger.info("自动刷图 [ √ ]", 1) 48 | else: 49 | init.global_data.auto_switch = False 50 | cls.thread_switch = False 51 | logger.info("自动刷图 [ x ]", 1) 52 | 53 | @classmethod 54 | def auto_thread(cls): 55 | """自动线程""" 56 | while cls.thread_switch: 57 | try: 58 | time.sleep(0.3) 59 | if cls.map_data.is_dialog_esc() or cls.map_data.is_dialog_a() or cls.map_data.is_dialog_b(): 60 | drive_button(VK_ESC, 0, False) 61 | time.sleep(0.2) 62 | drive_button(VK_SPACE, 0, False) 63 | continue 64 | 65 | # 进入城镇 66 | if cls.map_data.get_stat() == 0: 67 | time.sleep(0.2) 68 | cls.enter_town() 69 | continue 70 | 71 | # 城镇处理 72 | if cls.map_data.get_stat() == 1 and cls.map_data.is_town() is True: 73 | cls.town_handle() 74 | continue 75 | 76 | # 进入副本 77 | if cls.map_data.get_stat() == 2: 78 | cls.enter_map(init.global_data.map_id, init.global_data.map_level) 79 | continue 80 | 81 | # 在地图内 82 | if cls.map_data.get_stat() == 3: 83 | if cls.firstEnterMap is False and cls.map_data.is_town() is False: 84 | # 透明call 85 | call.hide_call(call.person_ptr()) 86 | # sss评分 87 | sss_score = random.randint(5201314, 9999999) 88 | mem.write_long(mem.read_long(address.PFAddr) + address.CEPfAddr, sss_score) 89 | # 无视建筑 90 | cls.traversal.ignore_building(True) 91 | # 进图开启功能 92 | cls.start_func() 93 | cls.firstEnterMap = True 94 | continue 95 | 96 | # 跟随怪物 97 | if config().getint("自动配置", "跟随打怪") > 0: 98 | cls.traversal.follow_monster() 99 | 100 | # 过图 101 | if cls.map_data.is_open_door() is True and cls.map_data.is_boss_room() is False: 102 | if cls.traversal.is_exists_item() is True: 103 | # 捡物品 104 | cls.traversal.pickup() 105 | continue 106 | 107 | # 过图 108 | cls.pass_map() 109 | continue 110 | 111 | # 通关 112 | if cls.map_data.is_boss_room() and cls.map_data.is_pass(): 113 | if cls.traversal.is_exists_item() is True: 114 | # 捡物品 115 | cls.traversal.pickup() 116 | continue 117 | # 关闭功能 118 | cls.start_func() 119 | # 关闭穿透 120 | cls.traversal.ignore_building(False) 121 | # 退出副本 122 | cls.quit_map() 123 | cls.firstEnterMap = False 124 | except Exception as err: 125 | helper.print_trace("自动线程开始", err) 126 | 127 | @classmethod 128 | def start_func(cls): 129 | func_mod = config().getint("自动配置", "开启功能") 130 | if func_mod == 1: 131 | print("功能1为实现") 132 | if func_mod == 2: 133 | print("功能2为实现") 134 | if func_mod == 3: 135 | print("功能3为实现") 136 | 137 | @classmethod 138 | def enter_town(cls): 139 | """进入城镇""" 140 | role_num = config().getint("自动配置", "角色数量") 141 | init.global_data.completed_role = init.global_data.completed_role + 1 142 | if init.global_data.completed_role > role_num: 143 | logger.info("指定角色完成所有角色", 2) 144 | logger.info("自动刷图 [ x ]", 2) 145 | cls.thread_switch = False 146 | init.global_data.auto_switch = False 147 | return 148 | 149 | time.sleep(0.2) 150 | cls.pack.select_role(init.global_data.completed_role) 151 | time.sleep(0.5) 152 | logger.info("进入角色 {} ".format(init.global_data.completed_role), 2) 153 | logger.info("开始第 {} 个角色,剩余疲劳 {}".format(init.global_data.completed_role + 1, cls.map_data.get_pl()), 154 | 2) 155 | while cls.thread_switch: 156 | time.sleep(0.2) 157 | # 进入城镇跳出循环 158 | if cls.map_data.get_stat() == 1: 159 | break 160 | 161 | @classmethod 162 | def town_handle(cls): 163 | """城镇处理""" 164 | if cls.map_data.get_pl() <= 8: 165 | cls.return_role() 166 | return 167 | 168 | time.sleep(0.5) 169 | # 分解装备 170 | cls.traversal.handle_equip() 171 | 172 | # 1 剧情 2 搬砖 173 | auto_model = config().getint("自动配置", "自动模式") 174 | if auto_model == 1 and cls.map_data.get_role_level() < 110: 175 | init.global_data.map_id = cls.task.handle_main() 176 | init.global_data.map_level = 0 177 | if auto_model == 2 and cls.map_data.get_role_level() == 110: 178 | map_ids = list(map(int, config().get("自动配置", "普通地图").split(","))) 179 | random_number = random.randint(0, len(map_ids) - 1) 180 | init.global_data.map_id = map_ids[random_number] 181 | init.global_data.map_level = config().getint("自动配置", "地图难度") 182 | 183 | if init.global_data.map_id == 0: 184 | logger.info("地图编号为空,无法切换区域", 2) 185 | return 186 | 187 | # 区域发包 188 | max_region = call.area_call(init.global_data.map_id) 189 | time.sleep(0.5) 190 | if cls.map_data.get_max_region() != max_region: 191 | logger.info("未切换到区域,检查是否完成该地图区域任务", 2) 192 | return 193 | 194 | time.sleep(0.5) 195 | cls.select_map() 196 | 197 | @classmethod 198 | def select_map(cls): 199 | """选图""" 200 | while cls.thread_switch: 201 | time.sleep(0.2) 202 | # 选图 203 | cls.pack.select_map() 204 | # 不在选图界面跳出循环 205 | if cls.map_data.get_stat() == 2: 206 | break 207 | 208 | @classmethod 209 | def return_role(cls): 210 | """返回角色""" 211 | logger.info("疲劳值不足 · 即将切换角色", 2) 212 | time.sleep(0.2) 213 | cls.pack.return_role() 214 | while cls.thread_switch: 215 | time.sleep(0.2) 216 | if cls.map_data.get_stat() == 0: 217 | break 218 | 219 | @classmethod 220 | def enter_map(cls, map_id: int, map_level: int): 221 | """进图""" 222 | if map_level == 5: 223 | # drive_button(VK_SPACE, 0, False) 224 | for i in range(4, -1, -1): 225 | if cls.map_data.get_stat() == 3: 226 | break 227 | if cls.map_data.get_stat() == 2: 228 | cls.pack.go_map(map_id, i, 0, 0) 229 | time.sleep(1) 230 | if cls.map_data.get_stat() == 1: 231 | cls.select_map() 232 | else: 233 | # drive_button(VK_SPACE, 0, False) 234 | cls.pack.go_map(map_id, map_level, 0, 0) 235 | 236 | while cls.thread_switch: 237 | time.sleep(0.2) 238 | # 进图副本跳出循环 239 | if cls.map_data.get_stat() == 3: 240 | break 241 | 242 | @classmethod 243 | def pass_map(cls): 244 | """过图""" 245 | if cls.map_data.is_open_door() is False or cls.map_data.is_boss_room() is True: 246 | return 247 | 248 | over_map = config().getint("自动配置", "过图方式") 249 | if over_map > 0: 250 | # 寻路过图 251 | map_data = cls.game_map.map_data() 252 | if len(map_data.map_route) >= 2: 253 | direction = cls.game_map.get_direction(map_data.map_route[0], map_data.map_route[1]) 254 | if over_map == 1: 255 | call.over_map_call(direction) 256 | if over_map == 2: 257 | call.drift_over_map(direction) 258 | 259 | @classmethod 260 | def pass_boss(cls): 261 | """ 刷图次数处理 """ 262 | cfg_name = "C:\\config.ini" 263 | complete_number = file.read_ini(cfg_name, "default", "count") 264 | complete_number = int(complete_number) + 1 265 | file.write_ini(cfg_name, "default", "count", complete_number) 266 | 267 | map_data = cls.map_data 268 | logger.info("{} [ {} ] 剩余疲劳 [ {} ]".format(map_data.get_map_name(), complete_number, map_data.get_pl()), 2) 269 | 270 | @classmethod 271 | def quit_map(cls): 272 | """出图""" 273 | cls.pass_boss() 274 | 275 | time.sleep(0.2) 276 | # 翻牌 277 | cls.pack.get_income(0, random.randint(0, 3)) 278 | 279 | out_type = config().getint("自动配置", "出图方式") 280 | if out_type == 0: 281 | time.sleep(5) 282 | 283 | while cls.thread_switch: 284 | time.sleep(0.2) 285 | # 出图 286 | cls.pack.leave_map() 287 | if cls.map_data.get_stat() == 1 or cls.map_data.is_town(): 288 | break 289 | -------------------------------------------------------------------------------- /game/call.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from common import helper 4 | from game import init, address 5 | from game import mem, fast_call as fc 6 | 7 | fast_call = fc.FastCall 8 | 9 | 10 | def init_call(): 11 | global fast_call 12 | fast_call = fc.FastCall(mem) 13 | fast_call.init_code() 14 | 15 | 16 | # 是否执行完成 17 | run_status = False 18 | 19 | 20 | def compile_call(byte_arr: list): 21 | # 汇编中转, 空白地址, 跳转地址 22 | assembly_transit = address.NcBhKbAddr + 300 23 | blank_address = address.NcBhKbAddr + 500 24 | jump_address = blank_address - 100 25 | global run_status 26 | if run_status: 27 | return 28 | 29 | run_status = True 30 | hook_shell = address.HBCallAddr 31 | hook_shell = hook_shell + 144 32 | hook_jump = hook_shell + 19 33 | hook_data = mem.read_bytes(hook_shell, 19) 34 | hook_old_data = hook_data 35 | 36 | hook_data = helper.add_bytes(hook_data, [72, 184], helper.int_to_bytes(jump_address, 8)) 37 | hook_data = helper.add_bytes(hook_data, [131, 56, 1, 117, 42, 72, 129, 236, 0, 3, 0, 0]) 38 | hook_data = helper.add_bytes(hook_data, [72, 187], helper.int_to_bytes(blank_address, 8)) 39 | hook_data = helper.add_bytes(hook_data, [255, 211]) 40 | hook_data = helper.add_bytes(hook_data, [72, 184], helper.int_to_bytes(jump_address, 8)) 41 | hook_data = helper.add_bytes(hook_data, [199, 0, 3, 0, 0, 0]) 42 | hook_data = helper.add_bytes(hook_data, [72, 129, 196, 0, 3, 0, 0]) 43 | hook_data = helper.add_bytes(hook_data, [255, 37, 0, 0, 0, 0], helper.int_to_bytes(hook_jump, 8)) 44 | 45 | if mem.read_int(assembly_transit) == 0: 46 | mem.write_bytes(assembly_transit, hook_data) 47 | 48 | mem.write_bytes(blank_address, helper.add_bytes(bytes(byte_arr), [195])) 49 | hook_shell_value = helper.add_list([255, 37, 0, 0, 0, 0], helper.int_to_bytes(assembly_transit, 8), 50 | [144, 144, 144, 144, 144]) 51 | 52 | mem.write_bytes(hook_shell, bytes(hook_shell_value)) 53 | 54 | mem.write_int(jump_address, 1) 55 | while mem.read_int(jump_address) == 1: 56 | time.sleep(0.01) 57 | 58 | mem.write_bytes(hook_shell, hook_old_data) 59 | mem.write_bytes(blank_address, helper.get_empty_bytes(len(byte_arr) + 16)) 60 | run_status = False 61 | 62 | 63 | def sub_rsp(i): 64 | """ 65 | :param i: int 66 | :return: [int] 67 | """ 68 | if i > 127: 69 | return helper.add_list([72, 129, 236], helper.int_to_bytes(i, 4)) 70 | return helper.add_list([72, 131, 236], [i]) 71 | 72 | 73 | def add_rsp(i): 74 | """ 75 | :param i: int 76 | :return: [int] 77 | """ 78 | if i > 127: 79 | return helper.add_list([72, 129, 196], helper.int_to_bytes(i, 4)) 80 | return helper.add_list([72, 131, 196], [i]) 81 | 82 | 83 | def call(addr): 84 | """ 85 | :param addr: int 86 | :return: [int] 87 | """ 88 | shell_code = [255, 21, 2, 0, 0, 0, 235, 8] 89 | return helper.add_list(shell_code, helper.int_to_bytes(addr, 8)) 90 | 91 | 92 | def get_per_ptr_call(addr: int): 93 | """ 94 | 取人物指针Call 95 | :param addr: 96 | :return: 97 | """ 98 | shell_code = helper.add_list(sub_rsp(100), call(address.RWCallAddr), [72, 163]) 99 | shell_code = helper.add_list(shell_code, helper.int_to_bytes(addr, 8)) 100 | shell_code = helper.add_list(shell_code, add_rsp(100)) 101 | compile_call(shell_code) 102 | return mem.read_long(addr) 103 | 104 | 105 | def person_ptr(): 106 | """人物指针""" 107 | return get_per_ptr_call(address.RwKbAddr) 108 | 109 | 110 | def skill_call(addr: int, code: int, harm: int, x: int, y: int, z: int, size: float): 111 | """ 112 | 技能call 113 | :param addr:int 触发地址 114 | :param code:int 技能代码 115 | :param harm:int 技能伤害 116 | :param x:int 117 | :param y:int 118 | :param z:int 119 | :param size: float 技能大小 120 | :return: 121 | """ 122 | empty_addr = address.JnKbAddr 123 | mem.write_long(empty_addr, addr) 124 | mem.write_int(empty_addr + 16, code) 125 | mem.write_int(empty_addr + 20, harm) 126 | mem.write_int(empty_addr + 32, x) 127 | mem.write_int(empty_addr + 36, y) 128 | mem.write_int(empty_addr + 40, z) 129 | mem.write_float(empty_addr + 140, size) 130 | mem.write_int(empty_addr + 144, 65535) 131 | mem.write_int(empty_addr + 148, 65535) 132 | shell_code = [72, 129, 236, 0, 2, 0, 0] 133 | shell_code = helper.add_list(shell_code, [72, 185], helper.int_to_bytes(empty_addr, 8)) 134 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.JNCallAddr, 8)) 135 | shell_code = helper.add_list(shell_code, [255, 208, 72, 129, 196, 0, 2, 0, 0]) 136 | compile_call(shell_code) 137 | 138 | 139 | def hide_call(obj_ptr: int): 140 | """透明call""" 141 | shell_code = [72, 129, 236, 0, 2, 0, 0] 142 | shell_code = helper.add_list(shell_code, [65, 191, 255, 255, 255, 255]) 143 | shell_code = helper.add_list(shell_code, [199, 68, 36, 32, 255, 255, 0, 0]) 144 | shell_code = helper.add_list(shell_code, [65, 185, 1, 0, 0, 0]) 145 | shell_code = helper.add_list(shell_code, [73, 184, 1, 0, 0, 0, 0, 0, 0, 0]) 146 | shell_code = helper.add_list(shell_code, [186, 1, 0, 0, 0]) 147 | shell_code = helper.add_list(shell_code, [72, 185], helper.int_to_bytes(obj_ptr, 8)) 148 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.TmCallAddr, 8)) 149 | shell_code = helper.add_list(shell_code, [255, 208, 72, 129, 196, 0, 2, 0, 0]) 150 | compile_call(shell_code) 151 | 152 | 153 | def drift_call(ptr, x, y, z, speed): 154 | """ 155 | 漂移call 156 | :param ptr: int 157 | :param x: int 158 | :param y: int 159 | :param z: int 160 | :param speed: int 速度 161 | :return: 162 | """ 163 | coordinate_move(x, y) 164 | return 165 | shell_code = [72, 129, 236, 0, 8, 0, 0] 166 | shell_code = helper.add_list(shell_code, [185, 240, 0, 0, 0]) 167 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.SqNcCallAddr, 8)) 168 | shell_code = helper.add_list(shell_code, [255, 208]) 169 | shell_code = helper.add_list(shell_code, [72, 139, 240, 72, 139, 200]) 170 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.PyCall1Addr, 8)) 171 | shell_code = helper.add_list(shell_code, [255, 208]) 172 | shell_code = helper.add_list(shell_code, [185], helper.int_to_bytes(x, 4)) 173 | shell_code = helper.add_list(shell_code, [137, 8]) 174 | shell_code = helper.add_list(shell_code, [185], helper.int_to_bytes(y, 4)) 175 | shell_code = helper.add_list(shell_code, [137, 72, 4]) 176 | shell_code = helper.add_list(shell_code, [185], helper.int_to_bytes(z, 4)) 177 | shell_code = helper.add_list(shell_code, [137, 72, 8, 72, 141, 72, 24]) 178 | shell_code = helper.add_list(shell_code, [186], helper.int_to_bytes(speed, 4)) 179 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.PyCall2Addr, 8)) 180 | shell_code = helper.add_list(shell_code, [255, 208]) 181 | shell_code = helper.add_list(shell_code, 182 | [51, 219, 137, 95, 48, 199, 135, 224, 0, 0, 0, 2, 0, 0, 0, 72, 141, 69, 136, 72, 137, 183 | 68, 36, 96, 72, 137, 93, 136, 72, 137, 93, 144, 51, 210, 72, 141, 77, 136]) 184 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.XrNcCallAddr, 8)) 185 | shell_code = helper.add_list(shell_code, [72, 139, 206, 72, 139, 1]) 186 | shell_code = helper.add_list(shell_code, 187 | [72, 139, 6, 137, 92, 36, 64, 72, 137, 92, 36, 56, 72, 137, 92, 36, 48, 137, 92, 36, 188 | 40, 72, 141, 77, 136, 72, 137, 76, 36, 32, 69, 51, 201]) 189 | shell_code = helper.add_list(shell_code, [72, 186], helper.int_to_bytes(ptr, 8)) 190 | shell_code = helper.add_list(shell_code, [73, 184], helper.int_to_bytes(ptr, 8)) 191 | shell_code = helper.add_list(shell_code, [72, 139, 206]) 192 | shell_code = helper.add_list(shell_code, [255, 144], helper.int_to_bytes(312, 4)) 193 | shell_code = helper.add_list(shell_code, [72, 129, 196, 0, 8, 0, 0]) 194 | compile_call(shell_code) 195 | 196 | 197 | def move_call(max_map, mix_map, x, y): 198 | """移动Call""" 199 | role_ptr = mem.read_long(address.JSPtrAddr) 200 | mem.write_int(address.CzSyRdxAddr, max_map) 201 | mem.write_int(address.CzSyRdxAddr + 4, mix_map) 202 | mem.write_int(address.CzSyRdxAddr + 8, x) 203 | mem.write_int(address.CzSyRdxAddr + 12, y) 204 | shell_code = helper.add_list(sub_rsp(256), [72, 186], helper.int_to_bytes(address.CzSyRdxAddr, 8)) 205 | shell_code = helper.add_list(shell_code, [72, 185], helper.int_to_bytes(role_ptr, 8)) 206 | shell_code = helper.add_list(shell_code, call(address.CzSyCallAddr)) 207 | shell_code = helper.add_list(shell_code, add_rsp(256)) 208 | compile_call(shell_code) 209 | 210 | 211 | def area_call(map_num) -> int: 212 | """区域Call""" 213 | region_addr = mem.read_long(address.QyParamAddr) 214 | tmp_region_call = address.QyCallAddr 215 | shell_code = [72, 131, 236, 48] 216 | shell_code = helper.add_list(shell_code, helper.add_list([65, 184], helper.int_to_bytes(map_num, 4))) 217 | shell_code = helper.add_list(shell_code, [186, 174, 12, 0, 0]) 218 | shell_code = helper.add_list(shell_code, [72, 184, 255, 255, 255, 255, 0, 0, 0, 0]) 219 | shell_code = helper.add_list(shell_code, helper.add_list([72, 185], helper.int_to_bytes(address.QyParamAddr, 8))) 220 | shell_code = helper.add_list(shell_code, [72, 139, 9], [76, 139, 201, 73, 129, 193]) 221 | shell_code = helper.add_list(shell_code, helper.int_to_bytes(address.QyPyAddr, 4), [73, 131, 233, 64]) 222 | shell_code = helper.add_list(shell_code, helper.add_list([72, 184], helper.int_to_bytes(tmp_region_call, 8))) 223 | shell_code = helper.add_list(shell_code, [255, 208, 72, 131, 196, 48]) 224 | compile_call(shell_code) 225 | max_region = mem.read_int(region_addr + address.QyPyAddr + 0) 226 | min_region = mem.read_int(region_addr + address.QyPyAddr + 4) 227 | town_x = mem.read_int(region_addr + address.QyPyAddr + 8) 228 | town_y = mem.read_int(region_addr + address.QyPyAddr + 12) 229 | move_call(max_region, min_region, town_x, town_y) 230 | return max_region 231 | 232 | 233 | def over_map_call(fx): 234 | """ 235 | 过图call 236 | :param fx: 0左 1右 2上 3下 237 | """ 238 | if init.map_data.is_town(): 239 | return 240 | if not init.map_data.is_open_door(): 241 | return 242 | empty_addr = address.GtKbAddr 243 | room_data = mem.read_long(mem.read_long(mem.read_long(address.FJBHAddr) + address.SJAddr) + address.StPyAddr) 244 | shell_code = [65, 185, 255, 255, 255, 255] 245 | shell_code = helper.add_list(shell_code, [73, 184], helper.int_to_bytes(empty_addr, 8)) 246 | shell_code = helper.add_list(shell_code, [186], helper.int_to_bytes(fx, 4)) 247 | shell_code = helper.add_list(shell_code, [72, 185], helper.int_to_bytes(room_data, 8)) 248 | shell_code = helper.add_list(shell_code, [72, 184], helper.int_to_bytes(address.GtCallAddr, 8)) 249 | shell_code = helper.add_list(shell_code, [255, 208]) 250 | compile_call(shell_code) 251 | 252 | 253 | def drift_over_map(fx): 254 | """ 255 | 漂移顺图 256 | :param fx: 0左 1右 2上 3下 257 | """ 258 | if init.map_data.is_town(): 259 | return 260 | if not init.map_data.is_open_door(): 261 | return 262 | 263 | addr = person_ptr() 264 | map_offset = mem.read_long(addr + address.DtPyAddr) 265 | if map_offset == 0: 266 | return 267 | 268 | room_data = mem.read_long(mem.read_long(mem.read_long(address.FJBHAddr) + address.SJAddr) + address.StPyAddr) 269 | coordinate_structure = room_data + fx * address.FxIdAddr + address.ZbStPyAddr 270 | start_x = mem.read_int(coordinate_structure + 0) 271 | start_y = mem.read_int(coordinate_structure + 4) 272 | end_x = mem.read_int(coordinate_structure + 8) 273 | end_y = mem.read_int(coordinate_structure + 12) 274 | x, y = (int(0), int(0)) 275 | if fx == 0: 276 | x = int(start_x + end_x + 20) 277 | y = int(start_y + end_y / 2) 278 | 279 | if fx == 1: 280 | x = int(start_x - 20) 281 | y = int(start_y + end_y / 2) 282 | if fx == 2: 283 | x = int(start_x + end_x / 2) 284 | y = int(start_y + end_y + 20) 285 | if fx == 3: 286 | x = int(start_x + end_x / 2) 287 | y = int(start_y - 20) 288 | 289 | drift_call(addr, x, y, 0, 50) 290 | time.sleep(0.1) 291 | drift_call(addr, int(start_x + end_x / 2), start_y, 0, 50) 292 | 293 | 294 | def jump_over_task_call(): 295 | # 跳过任务Call 296 | shell_code = sub_rsp(512) 297 | shell_code = helper.add_list(shell_code, [65, 131, 201, 255]) 298 | shell_code = helper.add_list(shell_code, [69, 9, 200]) 299 | shell_code = helper.add_list(shell_code, [186, 1, 0, 0, 0]) 300 | shell_code = helper.add_list(shell_code, [72, 185], helper.int_to_bytes(address.TaskAddr, 8)) 301 | shell_code = helper.add_list(shell_code, [72, 139, 9]) 302 | shell_code = helper.add_list(shell_code, call(address.TgCallAddr)) 303 | shell_code = helper.add_list(shell_code, add_rsp(512)) 304 | compile_call(shell_code) 305 | 306 | 307 | def accept_task_call(task_id): 308 | # 接受任务Call 309 | shell_code = sub_rsp(40) 310 | helper.add_list(shell_code, [186], helper.int_to_bytes(task_id, 4)) 311 | helper.add_list(shell_code, call(address.JsCallAddr)) 312 | helper.add_list(shell_code, add_rsp(40)) 313 | compile_call(shell_code) 314 | 315 | 316 | def finish_task_call(task_id): 317 | # 完成任务Call 318 | shell_code = sub_rsp(512) 319 | helper.add_list(shell_code, [179, 255]) 320 | helper.add_list(shell_code, [68, 15, 182, 203]) 321 | helper.add_list(shell_code, [65, 176, 255]) 322 | helper.add_list(shell_code, [186], helper.int_to_bytes(task_id, 4)) 323 | helper.add_list(shell_code, call(address.WcCallAddr)) 324 | helper.add_list(shell_code, add_rsp(512)) 325 | compile_call(shell_code) 326 | 327 | 328 | def submit_task_call(task_id): 329 | # 提交任务Call 330 | shell_code = sub_rsp(48) 331 | helper.add_list(shell_code, [65, 189, 1, 0, 0, 0]) 332 | helper.add_list(shell_code, [65, 190, 255, 255, 255, 255]) 333 | helper.add_list(shell_code, [69, 139, 205]) 334 | helper.add_list(shell_code, [69, 139, 198]) 335 | helper.add_list(shell_code, [72, 185], helper.int_to_bytes(address.TaskAddr, 8)) 336 | helper.add_list(shell_code, [72, 139, 9]) 337 | helper.add_list(shell_code, [186], helper.int_to_bytes(task_id, 4)) 338 | helper.add_list(shell_code, call(address.TjCallAddr)) 339 | helper.add_list(shell_code, add_rsp(48)) 340 | compile_call(shell_code) 341 | 342 | 343 | def cool_down_call(skill_addr): 344 | if skill_addr < 0: 345 | return False 346 | empty_addr = address.CoolDownKbAddr 347 | mem.write_int(empty_addr, 0) 348 | shell_code = [72, 131, 236, 32] 349 | helper.add_list(shell_code, [49, 210]) 350 | helper.add_list(shell_code, [72, 185], helper.int_to_bytes(skill_addr, 8)) 351 | helper.add_list(shell_code, [255, 21, 2, 0, 0, 0, 235, 8]) 352 | helper.add_list(shell_code, helper.int_to_bytes(address.LqCallJudgeAddr, 8)) 353 | helper.add_list(shell_code, [72, 162], helper.int_to_bytes(empty_addr, 8)) 354 | helper.add_list(shell_code, [72, 131, 196, 32]) 355 | compile_call(shell_code) 356 | return mem.read_int(empty_addr) < 1 357 | 358 | 359 | # 坐标移动 360 | def coordinate_move(x: int, y: int): 361 | # .常量 人物坐标_1, "328", , , 0x148 创新中心获取 362 | # .常量 人物坐标_2, "69", , , 0x45 创新中心获取 363 | 364 | # 获取人物指针 365 | addr = person_ptr() 366 | # 读取坐标指针 367 | coordinate_ptr = mem.read_long(mem.read_long(addr + 0x148) + 8) 368 | # 计算坐标偏移量 369 | offset = coordinate_ptr + 0x45 370 | 371 | # 写入坐标值 372 | mem.write_float(offset, float(x)) 373 | mem.write_float(offset + 4, float(y)) 374 | -------------------------------------------------------------------------------- /game/fast_call.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from common import helper 4 | from game import address 5 | 6 | 7 | class FastCall: 8 | g_hook_interface = None 9 | g_call_max_len = None 10 | g_RSP = None 11 | g_timeout_call_settings = None 12 | g_allocate_space = None 13 | g_last_space = None 14 | g_hook_address = None 15 | g_old_data = None 16 | g_old_data_save = None 17 | g_transit_framework_memory = None 18 | g_execute_func_code = None 19 | g_execute_func_result = None 20 | g_execute_func_control = None 21 | g_execute_func_refresh_time = None 22 | g_execute_func_last_time = None 23 | g_execute_func_data = None 24 | g_hook_framework = None 25 | 26 | mem = None 27 | 28 | def __init__(self, mem): 29 | self.mem = mem 30 | 31 | def init_code(self): 32 | self.g_hook_interface = 1 33 | self.g_call_max_len = 6666 34 | self.g_RSP = 584 35 | self.g_allocate_space = self.mem.allocate(4906 * 1024) 36 | self.g_timeout_call_settings = 1000 * 60 37 | self.g_last_space = self.g_allocate_space 38 | 39 | code = [72, 137, 116, 36, 8, 72, 137, 124, 36, 16, 65, 86, 72, 131, 236, 32, 72, 190, 0, 0, 0, 64, 1, 0, 0, 0, 40 | 72, 191, 118, 11, 204, 63, 1, 0, 0, 0, 73, 190, 126, 11, 204, 63, 1, 0, 0, 0, 255, 214, 72, 163, 142, 41 | 11, 204, 63, 1, 0, 0, 0, 131, 63, 1, 117, 57, 73, 199, 6, 0, 0, 0, 0, 72, 199, 7, 2, 0, 0, 0, 255, 214, 42 | 72, 163, 134, 11, 204, 63, 1, 0, 0, 0, 65, 86, 87, 83, 86, 72, 184, 0, 0, 0, 80, 1, 0, 0, 0, 255, 208, 43 | 94, 91, 95, 65, 94, 73, 137, 6, 199, 7, 0, 0, 0, 0, 72, 139, 116, 36, 48, 72, 139, 124, 36, 56, 72, 131, 44 | 196, 32, 65, 94, 195] 45 | 46 | self.g_execute_func_code = self.allocate_space(len(code)) 47 | self.g_execute_func_data = self.allocate_space(self.g_call_max_len) 48 | self.g_execute_func_control = self.allocate_space(8) 49 | self.g_execute_func_refresh_time = self.allocate_space(8) 50 | self.g_execute_func_result = self.allocate_space(8) 51 | self.g_execute_func_last_time = self.allocate_space(8) 52 | 53 | self.mem.write_bytes(self.g_execute_func_code, bytes(code)) 54 | self.mem.write_long(self.g_execute_func_code + 0x10 + 2, self.mem.read_long(address.GameTimeGetTime)) 55 | self.mem.write_long(self.g_execute_func_code + 0x1A + 2, self.g_execute_func_control) 56 | self.mem.write_long(self.g_execute_func_code + 0x24 + 2, self.g_execute_func_result) 57 | self.mem.write_long(self.g_execute_func_code + 0x30 + 2, self.g_execute_func_refresh_time) 58 | self.mem.write_long(self.g_execute_func_code + 0x4F + 2, self.g_execute_func_last_time) 59 | self.mem.write_long(self.g_execute_func_code + 0x5E + 2, self.g_execute_func_data) 60 | 61 | code = [72, 137, 92, 36, 8, 72, 137, 116, 36, 16, 87, 72, 131, 236, 32] 62 | code = helper.add_list(code, [72, 184], helper.int_to_bytes(self.g_execute_func_code, 8), [255, 208]) 63 | code = helper.add_list(code, [72, 139, 92, 36, 48, 72, 139, 116, 36, 56, 72, 131, 196, 32, 95, 195]) 64 | self.g_transit_framework_memory = self.allocate_space(len(code)) 65 | self.mem.write_bytes(self.g_transit_framework_memory, bytes(code)) 66 | self.init_hook_type(self.g_hook_interface) 67 | 68 | code = [80, 83, 81, 82, 87, 86, 85, 65, 80, 65, 81, 65, 82, 65, 83, 65, 84, 65, 85, 65, 86, 65, 87, 156, 72, 69 | 131, 236, 40] 70 | code = helper.add_list(code, [72, 184], helper.int_to_bytes(self.g_transit_framework_memory, 8), [255, 208]) 71 | code = helper.add_list(code, 72 | [72, 131, 196, 40, 157, 65, 95, 65, 94, 65, 93, 65, 92, 65, 91, 65, 90, 65, 89, 65, 88, 73 | 93, 94, 95, 90, 89, 91, 88]) 74 | code = helper.add_list(code, [255, 37, 0, 0, 0, 0], helper.int_to_bytes(self.g_old_data, 8)) 75 | 76 | self.g_hook_framework = self.allocate_space(len(code)) 77 | 78 | self.mem.write_bytes(self.g_hook_framework, bytes(code)) 79 | self.mem.write_long(self.g_old_data_save, self.g_old_data) 80 | self.mem.write_long(self.g_hook_address, self.g_hook_framework) 81 | 82 | def free_code(self): 83 | """释放内存""" 84 | self.mem.write_long(self.g_hook_address, self.g_old_data) 85 | self.mem.write_bytes(self.g_transit_framework_memory, 86 | helper.get_empty_bytes(self.g_last_space - self.g_allocate_space)) 87 | 88 | def allocate_space(self, length): 89 | """分配空间""" 90 | result = self.g_last_space 91 | self.g_last_space = self.g_last_space + length 92 | return result 93 | 94 | def init_hook_type(self, interface_selection: int): 95 | self.g_old_data_save = self.allocate_space(8) 96 | if interface_selection == 1: 97 | hook_address = self.mem.read_long(address.TranslateMessage) 98 | hook_address = hook_address + self.mem.read_int(hook_address + 2) + 6 99 | self.g_hook_address = hook_address 100 | 101 | if self.mem.read_long(self.g_old_data_save) == 0: 102 | self.g_old_data = self.mem.read_long(self.g_hook_address) 103 | else: 104 | self.g_old_data = self.mem.read_long(self.g_old_data_save) 105 | 106 | def call_wait(self): 107 | """调用等待""" 108 | while self.mem.read_int(self.g_execute_func_control) == 1: 109 | time.sleep(0.001) 110 | 111 | while self.mem.read_int(self.g_execute_func_control) == 2: 112 | refresh_time = self.mem.read_int(self.g_execute_func_refresh_time) - self.mem.read_int( 113 | self.g_execute_func_last_time) 114 | if self.g_timeout_call_settings != 0 and refresh_time > self.g_timeout_call_settings: 115 | break 116 | time.sleep(0.001) 117 | 118 | def call_function_auto_find_stack(self, call_data: list, rsp: int = None) -> int: 119 | """调用函数_自动找堆栈""" 120 | if rsp is None: 121 | rsp = self.g_RSP 122 | if call_data[-1] == 195: 123 | call_data[-1] = 144 124 | 125 | call_data = helper.add_list([72, 129, 236], helper.int_to_bytes(rsp, 4), call_data, [72, 129, 196], 126 | helper.int_to_bytes(rsp, 4)) 127 | 128 | return self.memory_compilation(call_data) 129 | 130 | def memory_compilation(self, call_data: list) -> int: 131 | """内存汇编""" 132 | self.call_wait() 133 | call_data = helper.add_list(call_data, [195]) 134 | if len(call_data) > self.g_call_max_len: 135 | helper.message_box("调用数过长") 136 | return 0 137 | 138 | self.mem.write_bytes(self.g_execute_func_data, bytes(call_data)) 139 | self.mem.write_int(self.g_execute_func_control, 1) 140 | self.call_wait() 141 | self.mem.write_bytes(self.g_execute_func_data, helper.get_empty_bytes(len(call_data))) 142 | result = self.mem.read_long(self.g_execute_func_result) 143 | return result 144 | 145 | def call(self, func, *args) -> int: 146 | """远程调用call""" 147 | if len(args) > 16: 148 | return 0 149 | 150 | params_array = list() 151 | for i in range(len(args)): 152 | if args[i] is not None: 153 | params_array.append(args[i]) 154 | 155 | instruction_array = [47432, 47688, 47177, 47433] 156 | 157 | code = list() 158 | for i in range(len(params_array)): 159 | if i < 4: 160 | code = helper.add_list(code, helper.int_to_bytes(instruction_array[i], 2)) 161 | code = helper.add_list(code, helper.int_to_bytes(params_array[i], 8)) 162 | else: 163 | code = helper.add_list(code, [72, 184], helper.int_to_bytes(params_array[i], 8)) 164 | code = helper.add_list(code, [72, 137, 132, 36], helper.int_to_bytes(i * 8, 4)) 165 | 166 | code = helper.add_list(code, [72, 184], helper.int_to_bytes(func, 8), [255, 208]) 167 | 168 | if len(params_array) < 4: 169 | rsp = 4 * 8 + 8 170 | else: 171 | rsp = len(params_array) * 8 + 8 172 | 173 | if rsp / 8 % 2 == 0: 174 | rsp = rsp + 8 175 | 176 | code = helper.add_list([72, 129, 236], helper.int_to_bytes(rsp, 4), code, [72, 129, 196], 177 | helper.int_to_bytes(rsp, 4)) 178 | 179 | return self.memory_compilation(code) 180 | -------------------------------------------------------------------------------- /game/game_map.py: -------------------------------------------------------------------------------- 1 | from common import globle 2 | from game import mem 3 | from game import address 4 | from game.map_data import MapData 5 | 6 | 7 | class GameMap: 8 | 9 | @classmethod 10 | def __init__(cls): 11 | pass 12 | 13 | @classmethod 14 | def get_direction(cls, cut_room, next_room): 15 | """ 16 | 获取方向 17 | :param cut_room 18 | :param next_room 19 | :return: int 20 | """ 21 | direction = 0 22 | x = cut_room.x - next_room.x 23 | y = cut_room.y - next_room.y 24 | if x == 0 and y == 0: 25 | return 4 26 | if x == 0: 27 | if y == 1: 28 | direction = 2 29 | else: 30 | direction = 3 31 | elif y == 0: 32 | if x == 1: 33 | direction = 0 34 | else: 35 | direction = 1 36 | return direction 37 | 38 | @classmethod 39 | def judge_direction(cls, tx, fx): 40 | """ 41 | 寻路_判断方向 42 | :param tx: int 43 | :param fx: int 44 | :return: bool 45 | """ 46 | # 方向数组 47 | direction_arr = [] 48 | # 方向集合 49 | direction_set = [ 50 | [0, 0, 0, 0], 51 | [0, 1, 0, 0], 52 | [0, 0, 1, 0], 53 | [0, 1, 1, 0], 54 | [1, 0, 0, 0], 55 | [1, 1, 0, 0], 56 | [1, 0, 1, 0], 57 | [1, 1, 1, 0], 58 | [0, 0, 0, 1], 59 | [0, 1, 0, 1], 60 | [0, 0, 1, 1], 61 | [0, 1, 1, 1], 62 | [1, 0, 0, 1], 63 | [1, 1, 0, 1], 64 | [1, 0, 1, 1], 65 | [1, 1, 1, 1], 66 | ] 67 | if fx <= 15: 68 | for i in range(4): 69 | direction_arr.insert(i, direction_set[tx][i]) 70 | else: 71 | for i in range(4): 72 | direction_arr.insert(i, 0) 73 | 74 | if direction_arr[fx] == 1: 75 | return True 76 | 77 | return False 78 | 79 | @classmethod 80 | def tidy_coordinate(cls, simulation_route, reality_route): 81 | """ 82 | 整理坐标 83 | :param simulation_route: [CoordinateType] 84 | :param reality_route: [CoordinateType] 85 | :return: (int, [CoordinateType]) 86 | """ 87 | x, y, k = (0, 0, 0) 88 | for i in range(len(simulation_route)): 89 | temp_coordinates = globle.CoordinateType() 90 | x = (simulation_route[i].x + 2) % 3 91 | y = (simulation_route[i].y + 2) % 3 92 | if x == 0 and y == 0: 93 | temp_coordinates.x = int((simulation_route[i].x + 2) / 3 - 1) 94 | temp_coordinates.y = int((simulation_route[i].y + 2) / 3 - 1) 95 | reality_route.insert(0 + k, temp_coordinates) 96 | k = k + 1 97 | 98 | return k, reality_route 99 | 100 | @classmethod 101 | def gen_map(cls, width, height, map_channel): 102 | """ 103 | 生成地图 104 | :param width: int 105 | :param height: int 106 | :param map_channel: [int] 107 | :return: [[GameMapType]] 108 | """ 109 | game_map = [[globle.GameMapType()] for _ in range(width)] 110 | for x in range(width): 111 | game_map[x] = [globle.GameMapType() for _ in range(height)] 112 | 113 | i = 0 114 | for y in range(height): 115 | for x in range(width): 116 | game_map[x][y].map_coordinates.x = x 117 | game_map[x][y].map_coordinates.y = y 118 | game_map[x][y].map_channel = map_channel[i] 119 | game_map[x][y].left = cls.judge_direction(map_channel[i], 0) 120 | game_map[x][y].right = cls.judge_direction(map_channel[i], 1) 121 | game_map[x][y].up = cls.judge_direction(map_channel[i], 2) 122 | game_map[x][y].down = cls.judge_direction(map_channel[i], 3) 123 | game_map[x][y].background_color = 0xFFFFFF 124 | i = i + 1 125 | if game_map[x][y].map_channel == 0: 126 | game_map[x][y].background_color = 0x000000 127 | 128 | return game_map 129 | 130 | @classmethod 131 | def map_data(cls) -> globle.MapDataType: 132 | """地图数据""" 133 | map_obj = MapData(mem) 134 | data = globle.MapDataType() 135 | 136 | room_data = mem.read_long(mem.read_long(mem.read_long(address.FJBHAddr) + address.SJAddr) + address.MxPyAddr) 137 | room_index = map_obj.decode(room_data + address.SyPyAddr) 138 | 139 | data.width = mem.read_int(mem.read_long(room_data + address.KgPyAddr) + room_index * 8 + 0) 140 | data.height = mem.read_int(mem.read_long(room_data + address.KgPyAddr) + room_index * 8 + 4) 141 | data.tmp = mem.read_long(mem.read_long(room_data + address.SzPyAddr) + 32 * room_index + 8) 142 | data.channel_num = data.width * data.height 143 | 144 | for i in range(data.channel_num): 145 | data.map_channel.insert(0 + i, mem.read_int(data.tmp + i * 4)) 146 | 147 | data.start_zb.x = map_obj.get_cut_room().x + 1 148 | data.start_zb.y = map_obj.get_cut_room().y + 1 149 | data.end_zb.x = map_obj.get_boss_room().x + 1 150 | data.end_zb.y = map_obj.get_boss_room().y + 1 151 | 152 | if data.start_zb.x == data.end_zb.x and data.start_zb.y == data.end_zb.y: 153 | return data 154 | 155 | data.consume_fatigue = cls.get_route(data.map_channel, data.width, data.height, data.start_zb, data.end_zb, 156 | data.map_route) 157 | return data 158 | 159 | @classmethod 160 | def get_route(cls, map_channel, width, height, map_start, map_end, reality_route): 161 | """ 162 | 获取走法 163 | :param map_channel: [int] 164 | :param width: int 165 | :param height: int 166 | :param map_start: CoordinateType 167 | :param map_end: CoordinateType 168 | :param reality_route: [CoordinateType] 169 | :return: (int, [[CoordinateType]]) 170 | """ 171 | start_coordinate = globle.CoordinateType() 172 | end_coordinate = globle.CoordinateType() 173 | 174 | if map_start.x == map_end.x and map_start.y == map_end.y: 175 | return 0, None 176 | 177 | map_array = cls.gen_map(width, height, map_channel) 178 | map_flag = cls.display_map(map_array, width, height) 179 | start_coordinate.x = map_start.x * 3 - 2 180 | start_coordinate.y = map_start.y * 3 - 2 181 | end_coordinate.x = map_end.x * 3 - 2 182 | end_coordinate.y = map_end.y * 3 - 2 183 | cross_way = cls.route_calculate(map_flag, start_coordinate, end_coordinate, width * 3, height * 3) 184 | return cls.tidy_coordinate(cross_way, reality_route) 185 | 186 | @classmethod 187 | def display_map(cls, map_arr, width, height): 188 | """ 189 | 显示地图 190 | :param map_arr: [[GameMapType]] 191 | :param width: int 192 | :param height: int 193 | :return: [[GameMapType]] 194 | """ 195 | map_label = [[globle.GameMapType()] for _ in range(width * 3)] 196 | for x in range(width * 3): 197 | map_label[x] = [globle.GameMapType() for _ in range(height * 3)] 198 | 199 | for y in range(height): 200 | for x in range(width): 201 | map_label[(x + 1) * 3 - 2][(y + 1) * 3 - 2].background_color = 0xFFFFFF 202 | if map_arr[x][y].left: 203 | map_label[(x + 1) * 3 - 3][(y + 1) * 3 - 2].background_color = 0xFFFFFF 204 | if map_arr[x][y].right: 205 | map_label[(x + 1) * 3 - 1][(y + 1) * 3 - 2].background_color = 0xFFFFFF 206 | if map_arr[x][y].up: 207 | map_label[(x + 1) * 3 - 2][(y + 1) * 3 - 3].background_color = 0xFFFFFF 208 | if map_arr[x][y].down: 209 | map_label[(x + 1) * 3 - 2][(y + 1) * 3 - 1].background_color = 0xFFFFFF 210 | 211 | return map_label 212 | 213 | @classmethod 214 | def route_calculate(cls, map_label, map_start, map_end, width, height): 215 | """ 216 | 路径计算 217 | :param map_label: [[GameMapType]] 218 | :param map_start: CoordinateType 219 | :param map_end: CoordinateType 220 | :param width: int 221 | :param height: int 222 | :return: [CoordinateType] 223 | """ 224 | tmp_node = globle.MapNodeType() # 待检测节点, 临时节点 225 | open_list = list() # 开放列表 226 | close_list = list() # 关闭列表 227 | 228 | short_est_num = 0 # 最短编号 229 | 230 | tmp_node.current_coordinates.x = map_start.x 231 | tmp_node.current_coordinates.y = map_start.y 232 | 233 | map_label[map_start.x][map_start.y].background_color = 0x00FF00 234 | map_label[map_start.x][map_start.y].background_color = 0x0000FF 235 | open_list.insert(0, tmp_node) 236 | 237 | move_arr = [] 238 | 239 | while True: 240 | min_f = 0 241 | for y in range(len(open_list)): 242 | if min_f == 0: 243 | min_f = open_list[0].f 244 | short_est_num = y 245 | if open_list[y].f < min_f: 246 | min_f = open_list[y].f 247 | short_est_num = y 248 | 249 | tmp_node = open_list[short_est_num] 250 | open_list.pop(0 + short_est_num) 251 | close_list.insert(0, tmp_node) 252 | 253 | if tmp_node.current_coordinates.x != map_start.x or tmp_node.current_coordinates.y != map_start.y: 254 | if tmp_node.current_coordinates.x != map_end.x or tmp_node.current_coordinates.y != map_end.y: 255 | map_label[tmp_node.current_coordinates.x][ 256 | tmp_node.current_coordinates.y].background_color = 0x0080FF 257 | 258 | for y in range(len(close_list)): 259 | if close_list[y].current_coordinates.x == map_end.x and close_list[ 260 | y].current_coordinates.y == map_end.y: 261 | wait_handle_node = close_list[y] 262 | while True: 263 | for x in range(len(close_list)): 264 | if close_list[x].current_coordinates.x == wait_handle_node.final_coordinates.x and \ 265 | close_list[x].current_coordinates.y == wait_handle_node.final_coordinates.y: 266 | wait_handle_node = close_list[x] 267 | break 268 | if wait_handle_node.current_coordinates.x != map_start.x or wait_handle_node.current_coordinates.y != map_start.y: 269 | map_label[wait_handle_node.current_coordinates.x][ 270 | wait_handle_node.current_coordinates.y].background_color = 0x00D8D8 271 | move_arr.insert(0, wait_handle_node.current_coordinates) 272 | 273 | if wait_handle_node.current_coordinates.x == map_start.x and wait_handle_node.current_coordinates.y == map_start.y: 274 | break 275 | move_arr.insert(0, map_start) 276 | move_arr.append(map_end) 277 | return move_arr 278 | for y in range(4): 279 | wait_handle_coordinate = globle.CoordinateType() # 待检测坐标 280 | if y == 0: 281 | wait_handle_coordinate.x = tmp_node.current_coordinates.x 282 | wait_handle_coordinate.y = tmp_node.current_coordinates.y - 1 283 | elif y == 1: 284 | wait_handle_coordinate.x = tmp_node.current_coordinates.x - 1 285 | wait_handle_coordinate.y = tmp_node.current_coordinates.y 286 | elif y == 2: 287 | wait_handle_coordinate.x = tmp_node.current_coordinates.x + 1 288 | wait_handle_coordinate.y = tmp_node.current_coordinates.y 289 | else: 290 | wait_handle_coordinate.x = tmp_node.current_coordinates.x 291 | wait_handle_coordinate.y = tmp_node.current_coordinates.y + 1 292 | if wait_handle_coordinate.x < 0 or wait_handle_coordinate.x > ( 293 | width - 1) or wait_handle_coordinate.y < 0 or wait_handle_coordinate.y > (height - 1): 294 | continue 295 | if map_label[wait_handle_coordinate.x][wait_handle_coordinate.y].background_color == 0x000000: 296 | continue 297 | exist_close_list = False 298 | for x in range(len(close_list)): 299 | if close_list[x].current_coordinates.x == wait_handle_coordinate.x and close_list[ 300 | x].current_coordinates.y == wait_handle_coordinate.y: 301 | exist_close_list = True 302 | break 303 | if exist_close_list: 304 | continue 305 | exist_open_list = False 306 | for x in range(len(open_list)): 307 | if open_list[x].current_coordinates.x == wait_handle_coordinate.x and open_list[ 308 | x].current_coordinates.y == wait_handle_coordinate.y: 309 | if wait_handle_coordinate.x != tmp_node.current_coordinates.x or wait_handle_coordinate.y != tmp_node.current_coordinates.y: 310 | guess_g = 14 311 | else: 312 | guess_g = 10 313 | 314 | if tmp_node.g + guess_g < open_list[x].g: 315 | open_list[x].final_coordinates = tmp_node.current_coordinates 316 | 317 | exist_open_list = True 318 | break 319 | if not exist_open_list: 320 | if wait_handle_coordinate.x == tmp_node.current_coordinates.x or wait_handle_coordinate.y == tmp_node.current_coordinates.y: 321 | guess_g = 10 322 | else: 323 | guess_g = 14 324 | wait_handle_node = globle.MapNodeType() 325 | wait_handle_node.g = tmp_node.g + guess_g 326 | wait_handle_node.h = map_end.x - wait_handle_coordinate.x * 10 + map_end.y - wait_handle_coordinate.y * 10 327 | wait_handle_node.f = wait_handle_node.g + wait_handle_node.h 328 | wait_handle_node.current_coordinates = wait_handle_coordinate 329 | wait_handle_node.final_coordinates = tmp_node.current_coordinates 330 | open_list.insert(0, wait_handle_node) 331 | if len(open_list) == 0: 332 | break 333 | -------------------------------------------------------------------------------- /game/init.py: -------------------------------------------------------------------------------- 1 | """ 2 | 初始化全局变量 3 | """ 4 | import time 5 | 6 | import keyboard 7 | 8 | from common import globle 9 | from game import auto as a, game_map as gm, pack as p 10 | from game import call, traversal as tr, task as ts, screen as ac, address 11 | from game import map_data as md 12 | from game import mem 13 | 14 | map_data = md.MapData(mem) 15 | game_map = gm.GameMap() 16 | global_data = globle.GlobalData() 17 | pack = p.Pack() 18 | task = ts.Task(mem, pack, map_data) 19 | traversal = tr.Traversal(mem, pack, map_data) 20 | screen = ac.Screen(mem, map_data) 21 | auto = a.Auto(task, traversal, map_data, pack, game_map) 22 | 23 | 24 | def init_empty_addr(): 25 | """ 26 | 初始化全局空白 27 | """ 28 | address.RwKbAddr = mem.allocate(2048) 29 | address.BuffKbAddr = mem.allocate(2048) 30 | address.NcBhKbAddr = mem.allocate(2048) 31 | address.PtGgKbAddr = mem.allocate(2048) 32 | address.JnKbAddr = mem.allocate(2048) 33 | address.GtKbAddr = mem.allocate(2048) 34 | address.CoolDownKbAddr = mem.allocate(2048) 35 | time.sleep(2) 36 | 37 | 38 | def hotkey2(): 39 | keyboard.add_hotkey('f1', screen.screen_switch) 40 | keyboard.add_hotkey('`', screen.screen_kill) 41 | keyboard.add_hotkey('end', auto.switch) 42 | keyboard.add_hotkey('ctrl+up', call.over_map_call, args=(2,)) 43 | keyboard.add_hotkey('ctrl+down', call.over_map_call, args=(3,)) 44 | keyboard.add_hotkey('ctrl+left', call.over_map_call, args=(0,)) 45 | keyboard.add_hotkey('ctrl+right', call.over_map_call, args=(1,)) 46 | # 保持程序运行 47 | keyboard.wait() 48 | 49 | 50 | def hotkey(): 51 | # 加载user32.dll 52 | import ctypes.wintypes 53 | import win32con 54 | import win32api 55 | user32 = ctypes.windll.user32 56 | user32.RegisterHotKey(None, 0, 0, win32con.VK_F1) 57 | user32.RegisterHotKey(None, 0, 0, win32con.VK_F2) 58 | user32.RegisterHotKey(None, 0, 0, win32con.VK_F3) 59 | user32.RegisterHotKey(None, 0, 0, win32con.VK_F4) 60 | user32.RegisterHotKey(None, 0, 0, win32con.VK_END) 61 | user32.RegisterHotKey(None, 0, 0, 192) # 波浪 62 | 63 | user32.RegisterHotKey(None, 0, win32con.MOD_CONTROL, win32con.VK_UP) 64 | user32.RegisterHotKey(None, 0, win32con.MOD_CONTROL, win32con.VK_DOWN) 65 | user32.RegisterHotKey(None, 0, win32con.MOD_CONTROL, win32con.VK_LEFT) 66 | user32.RegisterHotKey(None, 0, win32con.MOD_CONTROL, win32con.VK_RIGHT) 67 | # user32.RegisterHotKey(None, 0, win32con.MOD_ALT, win32con.VK_UP) 68 | # user32.RegisterHotKey(None, 0, win32con.MOD_ALT, win32con.VK_DOWN) 69 | # user32.RegisterHotKey(None, 0, win32con.MOD_ALT, win32con.VK_LEFT) 70 | # user32.RegisterHotKey(None, 0, win32con.MOD_ALT, win32con.VK_RIGHT) 71 | 72 | # 以下为检测热键是否被按下,并在最后释放快捷键 73 | msg = ctypes.wintypes.MSG() 74 | while user32.GetMessageA(ctypes.byref(msg), None, 0, 0) > 0: 75 | if msg.message == win32con.WM_HOTKEY: 76 | if win32api.HIWORD(msg.lParam) == win32con.VK_F1: 77 | screen.screen_switch() 78 | if win32api.HIWORD(msg.lParam) == win32con.VK_END: 79 | auto.switch() 80 | if win32api.HIWORD(msg.lParam) == 192: 81 | screen.screen_kill() 82 | if win32api.HIWORD(msg.lParam) == win32con.VK_UP: 83 | call.over_map_call(2) 84 | if win32api.HIWORD(msg.lParam) == win32con.VK_DOWN: 85 | call.over_map_call(3) 86 | if win32api.HIWORD(msg.lParam) == win32con.VK_LEFT: 87 | call.over_map_call(0) 88 | if win32api.HIWORD(msg.lParam) == win32con.VK_RIGHT: 89 | call.over_map_call(1) 90 | else: 91 | user32.TranslateMessage(ctypes.byref(msg)) 92 | user32.DispatchMessageA(ctypes.byref(msg)) 93 | -------------------------------------------------------------------------------- /game/map_data.py: -------------------------------------------------------------------------------- 1 | from common import helper, globle 2 | from game import address as addr 3 | from game import call, address 4 | 5 | 6 | class MapData: 7 | mem = None 8 | 9 | def __init__(self, mem): 10 | self.mem = mem 11 | 12 | def encode(self, data_ptr: int, value: int): 13 | """加密""" 14 | # data_ptr += 4 15 | # data_ptr = data_ptr ^ 0x1F2A025C 16 | return self.mem.write_int(data_ptr, value) 17 | 18 | def decode(self, data_ptr: int) -> int: 19 | """解密""" 20 | value = self.mem.read_int(data_ptr) 21 | # value = value ^ 0x1F2A025C 22 | # value -= 4 23 | return value 24 | 25 | def get_stat(self) -> int: 26 | """0选角 1城镇 2选图 3图内 5选择频道""" 27 | return self.mem.read_int(addr.YXZTAddr) 28 | 29 | def is_town(self) -> bool: 30 | """是否城镇""" 31 | person_ptr = call.person_ptr() 32 | return self.mem.read_int(person_ptr + addr.DtPyAddr) == 0 33 | 34 | def is_open_door(self) -> bool: 35 | """是否开门""" 36 | person_ptr = call.person_ptr() 37 | encode_data = self.mem.read_long(self.mem.read_long(person_ptr + addr.DtPyAddr - 8) + 16) 38 | return self.decode(encode_data + addr.SfKmAddr) == 0 39 | 40 | def is_boss_room(self): 41 | """是否boss房""" 42 | cut = self.get_cut_room() 43 | boss = self.get_boss_room() 44 | return cut.x == boss.x and cut.y == boss.y 45 | 46 | def is_pass(self): 47 | """是否通关""" 48 | rw = self.mem 49 | room_data = rw.read_long(rw.read_long(rw.read_long(addr.FJBHAddr) + addr.SJAddr) + addr.MxPyAddr) 50 | data_val = rw.read_int(room_data + addr.GouHuoAddr) 51 | return data_val == 2 or data_val == 0 52 | 53 | def get_boss_room(self) -> globle.CoordinateType: 54 | """获取boss房间坐标""" 55 | result = globle.CoordinateType() 56 | rw = self.mem 57 | room_data = rw.read_long(rw.read_long(rw.read_long(addr.FJBHAddr) + addr.SJAddr) + addr.MxPyAddr) 58 | result.x = self.decode(room_data + addr.BOSSRoomXAddr) 59 | result.y = self.decode(room_data + addr.BOSSRoomYAddr) 60 | return result 61 | 62 | def get_cut_room(self) -> globle.CoordinateType: 63 | """获取当前房间坐标""" 64 | result = globle.CoordinateType() 65 | rw = self.mem 66 | room_data = rw.read_long(rw.read_long(rw.read_long(addr.FJBHAddr) + addr.SJAddr) + addr.MxPyAddr) 67 | result.x = self.mem.read_int(room_data + addr.CutRoomXAddr) 68 | result.y = self.mem.read_int(room_data + addr.CutRoomYAddr) 69 | return result 70 | 71 | def get_pl(self) -> int: 72 | """获取当前pl值""" 73 | return self.decode(addr.MaxPlAddr) - self.decode(addr.CutPlAddr) 74 | 75 | def get_role_level(self) -> int: 76 | """获取角色等级""" 77 | return self.mem.read_int(addr.JSDjAddr) 78 | 79 | def get_map_name(self) -> str: 80 | """获取地图名称""" 81 | room_data = self.mem.read_long(self.mem.read_long(self.mem.read_long( 82 | address.FJBHAddr) + address.SJAddr) + address.MxPyAddr) 83 | map_byte = self.mem.read_bytes(self.mem.read_long(room_data + address.DtMcAddr), 52) 84 | return helper.unicode_to_ascii(map_byte) 85 | 86 | def read_coordinate(self, param: int) -> globle.CoordinateType: 87 | """读取坐标""" 88 | coordinate = globle.CoordinateType() 89 | if self.mem.read_int(param + addr.LxPyAddr) == 273: 90 | ptr = self.mem.read_long(param + addr.DqZbAddr) 91 | coordinate.x = int(self.mem.read_float(ptr + 0)) 92 | coordinate.y = int(self.mem.read_float(ptr + 4)) 93 | coordinate.z = int(self.mem.read_float(ptr + 8)) 94 | else: 95 | ptr = self.mem.read_long(param + addr.FxPyAddr) 96 | coordinate.x = int(self.mem.read_float(ptr + 32)) 97 | coordinate.y = int(self.mem.read_float(ptr + 36)) 98 | coordinate.z = int(self.mem.read_float(ptr + 40)) 99 | 100 | return coordinate 101 | 102 | def is_dialog_a(self): 103 | return self.mem.read_int(addr.DHAddr) == 1 104 | 105 | def is_dialog_b(self): 106 | return self.mem.read_int(addr.DHAddrB) == 1 107 | 108 | def is_dialog_esc(self): 109 | return self.mem.read_int(addr.EscDHAddr) == 1 110 | 111 | def back_pack_weight(self) -> int: 112 | """取背包负重""" 113 | rw_addr = call.person_ptr() 114 | back_pack_ptr = self.mem.read_long(rw_addr + address.WplAddr) # 物品栏 115 | cut_weigh = self.decode(back_pack_ptr + 0x58) # 当前负重 116 | max_weigh = self.decode(rw_addr + address.ZdFzAddr) # 最大负重 117 | result = float(cut_weigh) / float(max_weigh) * 100 118 | return int(result) 119 | 120 | def get_fame(self) -> int: 121 | """获取名望""" 122 | rw_addr = call.person_ptr() 123 | return self.mem.read_long(rw_addr + address.RwMwAddr) 124 | 125 | def get_traversal_ptr(self, ptr: int, offset: int, t: int) -> int: 126 | """ 127 | 取遍历指针 128 | t 1 物品 2 地图 129 | """ 130 | result = 0 131 | 132 | if t == 1: 133 | one = self.mem.read_long(ptr + (offset - 1) * 8) 134 | two = self.mem.read_long(one - 72) 135 | result = self.mem.read_long(two + 16) 136 | if t == 2: 137 | one = self.mem.read_long(ptr + (offset - 1) * 24) 138 | result = self.mem.read_long(one + 16) - 48 139 | 140 | return result 141 | 142 | def get_map_data(self) -> globle.MapTraversalType: 143 | """ 144 | 地图遍历数据 145 | :return: 146 | """ 147 | data = globle.MapTraversalType() 148 | data.rw_addr = call.person_ptr() 149 | data.map_data = self.mem.read_long(self.mem.read_long(data.rw_addr + address.DtPyAddr - 8) + 16) 150 | data.start = self.mem.read_long(data.map_data + address.DtKs2) 151 | data.end = self.mem.read_long(data.map_data + address.DtJs2) 152 | data.obj_num = int((data.end - data.start) / 24) 153 | return data 154 | 155 | def get_max_region(self) -> int: 156 | return self.mem.read_int(address.CzDqyAddr) 157 | -------------------------------------------------------------------------------- /game/pack.py: -------------------------------------------------------------------------------- 1 | from common import helper 2 | from game import call, address 3 | 4 | 5 | class Pack: 6 | data = [] 7 | 8 | @classmethod 9 | def __init__(cls): 10 | cls.data = [] 11 | 12 | @classmethod 13 | def hc_call(cls, params): 14 | """ 15 | 缓冲call 16 | :param params: 17 | :return: 18 | """ 19 | cls.data = call.sub_rsp(256) 20 | cls.data = helper.add_list(cls.data, [72, 185], helper.int_to_bytes(address.FbAddr, 8)) 21 | cls.data = helper.add_list(cls.data, [186], helper.int_to_bytes(params, 4)) 22 | cls.data = helper.add_list(cls.data, [72, 184], helper.int_to_bytes(address.HcCallAddr, 8)) 23 | cls.data = helper.add_list(cls.data, [255, 208]) 24 | cls.data = helper.add_list(cls.data, call.add_rsp(256)) 25 | 26 | @classmethod 27 | def jm_call(cls, params, length): 28 | """加密call 29 | :param params: int 30 | :param length: int 31 | :return: 32 | """ 33 | cls.data = helper.add_list(cls.data, call.sub_rsp(256)) 34 | cls.data = helper.add_list(cls.data, [72, 185], helper.int_to_bytes(address.FbAddr, 8)) 35 | cls.data = helper.add_list(cls.data, [72, 186], helper.int_to_bytes(params, 8)) 36 | if length == 1: 37 | cls.data = helper.add_list(cls.data, [72, 184], helper.int_to_bytes(address.JmB1CallAddr, 8)) 38 | if length == 2: 39 | cls.data = helper.add_list(cls.data, [72, 184], helper.int_to_bytes(address.JmB2CallAddr, 8)) 40 | if length == 4: 41 | cls.data = helper.add_list(cls.data, [72, 184], helper.int_to_bytes(address.JmB3CallAddr, 8)) 42 | if length == 8: 43 | cls.data = helper.add_list(cls.data, [72, 184], helper.int_to_bytes(address.JmB4CallAddr, 8)) 44 | cls.data = helper.add_list(cls.data, [255, 208]) 45 | cls.data = helper.add_list(cls.data, call.add_rsp(256)) 46 | 47 | @classmethod 48 | def fb_call(cls): 49 | """ 50 | 发包call 51 | :return: 52 | """ 53 | cls.data = helper.add_list(cls.data, call.sub_rsp(256)) 54 | cls.data = helper.add_list(cls.data, [72, 184], helper.int_to_bytes(address.FbCallAddr, 8)) 55 | cls.data = helper.add_list(cls.data, [255, 208]) 56 | cls.data = helper.add_list(cls.data, call.add_rsp(256)) 57 | call.compile_call(cls.data) 58 | cls.data.clear() 59 | 60 | @classmethod 61 | def return_role(cls): 62 | """组包返回角色""" 63 | cls.hc_call(7) 64 | cls.fb_call() 65 | 66 | @classmethod 67 | def select_role(cls, index): 68 | """组包选择角色""" 69 | if index == 0: 70 | return 71 | cls.hc_call(4) 72 | cls.jm_call(index, 2) 73 | cls.fb_call() 74 | 75 | @classmethod 76 | def select_map(cls): 77 | """组包选图""" 78 | cls.hc_call(15) 79 | cls.jm_call(0, 4) 80 | cls.fb_call() 81 | 82 | @classmethod 83 | def go_map(cls, bh, nd, sy, lx): 84 | """组包进图""" 85 | cls.hc_call(16) 86 | cls.jm_call(bh, 4) 87 | cls.jm_call(nd, 1) 88 | cls.jm_call(0, 2) 89 | cls.jm_call(sy, 1) 90 | cls.jm_call(lx, 1) 91 | cls.jm_call(65535, 2) 92 | cls.jm_call(0, 4) 93 | cls.jm_call(0, 1) 94 | cls.jm_call(0, 4) 95 | cls.jm_call(0, 1) 96 | cls.jm_call(0, 4) 97 | cls.fb_call() 98 | 99 | @classmethod 100 | def get_income(cls, h: int, l: int): 101 | """组包翻牌""" 102 | cls.hc_call(69) 103 | cls.fb_call() 104 | cls.hc_call(70) 105 | cls.fb_call() 106 | cls.hc_call(71) 107 | cls.jm_call(h, 1) 108 | cls.jm_call(l, 1) 109 | cls.fb_call() 110 | cls.hc_call(1426) 111 | cls.fb_call() 112 | 113 | @classmethod 114 | def leave_map(cls): 115 | """组包出图""" 116 | cls.hc_call(42) 117 | cls.fb_call() 118 | 119 | @classmethod 120 | def move_map(cls, max_map, mix_map, x, y): 121 | """组包移动""" 122 | if max_map < 0 or mix_map < 0 or x < 0 or y < 0: 123 | return 124 | cls.hc_call(36) 125 | cls.jm_call(max_map, 4) 126 | cls.jm_call(mix_map, 4) 127 | cls.jm_call(x, 2) 128 | cls.jm_call(y, 2) 129 | cls.jm_call(5, 1) 130 | cls.jm_call(78, 4) 131 | cls.jm_call(1, 2) 132 | cls.jm_call(0, 4) 133 | cls.jm_call(0, 1) 134 | cls.jm_call(0, 1) 135 | cls.fb_call() 136 | 137 | @classmethod 138 | def pick_up(cls, addr): 139 | """组包拾取""" 140 | if addr < 0: 141 | return 142 | cls.hc_call(43) 143 | cls.jm_call(addr, 4) 144 | cls.jm_call(0, 1) 145 | cls.jm_call(1, 1) 146 | cls.jm_call(420, 2) 147 | cls.jm_call(254, 2) 148 | cls.jm_call(4501, 2) 149 | cls.jm_call(435, 2) 150 | cls.jm_call(271, 2) 151 | cls.jm_call(22624, 2) 152 | cls.jm_call(28402, 2) 153 | cls.jm_call(0, 1) 154 | cls.fb_call() 155 | 156 | @classmethod 157 | def decomposition(cls, addr): 158 | """组包分解""" 159 | if addr < 0: 160 | return 161 | cls.hc_call(26) 162 | cls.jm_call(0, 1) 163 | cls.jm_call(65535, 2) 164 | cls.jm_call(317, 4) 165 | cls.jm_call(1, 1) 166 | cls.jm_call(addr, 2) 167 | cls.fb_call() 168 | 169 | @classmethod 170 | def tidy_backpack(cls, pack_type, pack_addr): 171 | """整理背包""" 172 | cls.hc_call(20) 173 | cls.jm_call(6, 4) 174 | cls.jm_call(16, 1) 175 | cls.jm_call(pack_type, 1) # 1 装备 2消耗品 3材料 4任务 10副职业 176 | cls.jm_call(24, 1) 177 | cls.jm_call(pack_addr, 1) # 0 背包 2个人仓库 12账号金库 178 | cls.jm_call(32, 1) 179 | cls.jm_call(0, 1) # 0 栏位排序 1品级排序 2Lv排序 3部位排序 180 | cls.fb_call() 181 | 182 | @classmethod 183 | def accept_task(cls, task_id): 184 | """接受任务""" 185 | cls.hc_call(31) 186 | cls.jm_call(31, 2) 187 | cls.jm_call(task_id, 2) 188 | cls.fb_call() 189 | 190 | @classmethod 191 | def give_up_task(cls, task_id): 192 | """放弃任务""" 193 | cls.hc_call(32) 194 | cls.jm_call(32, 2) 195 | cls.jm_call(task_id, 2) 196 | cls.fb_call() 197 | 198 | @classmethod 199 | def finish_task(cls, task_id): 200 | """完成任务""" 201 | cls.hc_call(33) 202 | cls.jm_call(33, 2) 203 | cls.jm_call(task_id, 2) 204 | cls.jm_call(0, 1) 205 | cls.jm_call(0, 1) 206 | cls.fb_call() 207 | 208 | @classmethod 209 | def submit_task(cls, task_id): 210 | """提交任务""" 211 | cls.hc_call(34) 212 | cls.jm_call(34, 2) 213 | cls.jm_call(task_id, 2) 214 | cls.jm_call(65535, 2) 215 | cls.jm_call(1, 2) 216 | cls.jm_call(65535, 2) 217 | cls.fb_call() 218 | -------------------------------------------------------------------------------- /game/screen.py: -------------------------------------------------------------------------------- 1 | import _thread 2 | import time 3 | 4 | from common import config 5 | from common import logger 6 | from game import call, init, address 7 | 8 | 9 | class Screen: 10 | def __init__(self, mem, map_data): 11 | self.thread = None 12 | self._switch = False 13 | self.mem = mem 14 | self.map_data = map_data 15 | 16 | def screen_switch(self): 17 | self._switch = not self._switch 18 | if self._switch: 19 | self.thread = _thread.start_new_thread(self.screen_thread, ()) 20 | logger.info("技能全屏 [ √ ]", 1) 21 | else: 22 | self._switch = False 23 | logger.info("技能全屏 [ x ]", 1) 24 | 25 | def screen_thread(self): 26 | while self._switch: 27 | self.full_screen() 28 | time.sleep(0.3) 29 | 30 | @classmethod 31 | def screen_kill(cls): 32 | """秒杀完毕""" 33 | call.skill_call(0, 54141, 0, 0, 0, 0, 1.0) 34 | logger.info("秒杀完毕 [ √ ]", 1) 35 | 36 | def full_screen(self): 37 | """全屏遍历""" 38 | mem = self.mem 39 | map_obj = init.map_data 40 | if map_obj.get_stat() != 3: 41 | return 42 | 43 | data = self.map_data.get_map_data() 44 | num = 0 45 | # 遍历地图数据 46 | for data.obj_tmp in range(1, data.obj_num): 47 | obj_ptr = map_obj.get_traversal_ptr(data.start, data.obj_tmp, 2) 48 | if obj_ptr is not None and obj_ptr > 0: 49 | obj_type_a = mem.read_int(obj_ptr + address.LxPyAddr) 50 | obj_camp = mem.read_int(obj_ptr + address.ZyPyAddr) 51 | obj_code = mem.read_int(obj_ptr + address.DmPyAddr) 52 | if obj_type_a == 529 or obj_type_a == 545 or obj_type_a == 273 or obj_type_a == 61440: 53 | obj_blood = mem.read_long(obj_ptr + address.GwXlAddr) 54 | if obj_camp > 0 and obj_code > 0 and obj_blood > 0 and obj_ptr != data.rw_addr: 55 | monster = map_obj.read_coordinate(obj_ptr) 56 | code = config().getint("自动配置", "技能代码") 57 | harm = config().getint("自动配置", "技能伤害") 58 | size = config().getint("自动配置", "技能大小") 59 | number = config().getint("自动配置", "技能个数") 60 | call.skill_call(data.rw_addr, code, harm, monster.x, monster.y, 0, size) 61 | num = num + 1 62 | if num >= number: 63 | break 64 | -------------------------------------------------------------------------------- /game/task.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from typing import Tuple 4 | 5 | from common import helper, logger 6 | from game import init, address, call 7 | 8 | 9 | class Task: 10 | def __init__(self, mem, pack, map_data): 11 | # 无任务刷新角色 12 | self.refreshTask = False 13 | self.mem = mem 14 | self.pack = pack 15 | self.map_data = map_data 16 | 17 | def handle_main(self): 18 | """ 19 | 处理主线 20 | :return: 21 | """ 22 | map_id = 0 23 | next_task_id = 0 24 | 25 | # 提交主线 26 | self.submit_task() 27 | while init.global_data.auto_switch: 28 | time.sleep(0.3) 29 | task_name, task_condition, task_id = self.main_line_task() 30 | # 处理相同任务输出 31 | if task_id != next_task_id and task_id > 0: 32 | next_task_id = task_id 33 | logger.info("主线任务->任务名称 {}".format(task_name), 1) 34 | # logger.info("主线任务->任务条件 {}".format(task_condition), 1) 35 | # logger.info("主线任务->任务ID {}".format(task_id), 1) 36 | 37 | # 无任务,刷新角色 38 | if task_id == 0: 39 | if not self.refreshTask: 40 | time.sleep(0.2) 41 | logger.info("暂无任务或卡任务,重新选择角色", 1) 42 | self.pack.return_role() 43 | time.sleep(2) 44 | self.pack.select_role(init.global_data.completed_role) 45 | time.sleep(0.5) 46 | self.refreshTask = True 47 | continue 48 | else: 49 | map_id = self.highest_map() 50 | logger.info("暂无任务,执行适应等级地图", 1) 51 | return map_id 52 | 53 | self.refreshTask = False 54 | 55 | # 跳过部分无法完成任务,取最高等级执行 56 | # 任务名称[返回赫顿玛尔],任务条件[[seek n meet npc]],任务ID[3509] 材料不足无法完成任务 57 | # 任务名称[黑市的商人],任务条件[[seek n meet npc]],任务ID[5943] 蛇肉任务 58 | task_ids = [3509, 5943] 59 | if task_id in task_ids: 60 | map_id = self.highest_map() 61 | logger.info("无法完成任务,执行适应等级地图", 1) 62 | return map_id 63 | 64 | # 是否可以跳过任务 65 | # 86级寂静城任务无法跳过 任务id{3850, 3851, 3858, 3859, 3860, 3861, 3864, 3865, 3866, 3867, 3868} 66 | ok, task_level = self.can_skip(task_id) 67 | if ok and task_level not in [85, 86]: 68 | # 跳过任务 69 | call.jump_over_task_call() 70 | continue 71 | 72 | # 任务未接,执行接取任务 73 | if self.finish_status(task_id) == -1: 74 | # self.pack.accept_task(task_id) 75 | call.accept_task_call(task_id) 76 | 77 | # 任务完成,执行提交任务 78 | if self.finish_status(task_id) == 0: 79 | # self.pack.submit_task(task_id) 80 | call.submit_task_call(task_id) 81 | continue 82 | 83 | # 剧情条件判断 84 | if self.conditional(task_condition) == 1: 85 | # self.pack.finish_task(task_id) 86 | call.finish_task_call(task_id) 87 | 88 | # 刷图任务 89 | if self.conditional(task_condition) == 2: 90 | map_id = self.task_map(task_id) 91 | if map_id > 0: 92 | return map_id 93 | 94 | # 材料任务 95 | if self.conditional(task_condition) == 3: 96 | map_id = self.highest_map() 97 | logger.info("材料任务无法自动完成,执行最高等级地图", 1) 98 | return map_id 99 | 100 | return map_id 101 | 102 | def main_line_task(self) -> Tuple[str, str, int]: 103 | mem = self.mem 104 | task_addr = mem.read_long(address.TaskAddr) 105 | start = mem.read_long(task_addr + address.QbRwStartAddr) 106 | end = mem.read_long(task_addr + address.QbRwEndAddr) 107 | num = int((end - start) / 8) 108 | 109 | for i in range(num): 110 | task_ptr = mem.read_long(start + i * 8) 111 | task_type = mem.read_int(task_ptr + address.RwLxAddr) 112 | if task_type == 0: 113 | task_length = mem.read_int(task_ptr + address.RwDxAddr) 114 | if task_length > 7: 115 | tmp = mem.read_long(task_ptr + 16) 116 | task_name_byte = list(mem.read_bytes(tmp, 100)) 117 | task_name = helper.unicode_to_ascii(task_name_byte) 118 | else: 119 | task_name_byte = list(mem.read_bytes(task_ptr + 16, 100)) 120 | task_name = helper.unicode_to_ascii(task_name_byte) 121 | # 任务条件 122 | task_conditional = helper.unicode_to_ascii( 123 | list(mem.read_bytes(mem.read_long(task_ptr + address.RwTjAddr), 100))) 124 | # 任务编号 125 | task_id = mem.read_int(task_ptr) 126 | return task_name, task_conditional, task_id 127 | 128 | return "", "", 0 129 | 130 | def can_skip(self, task_id) -> [bool, int]: 131 | """是否跳过""" 132 | mem = self.mem 133 | task_addr = mem.read_long(address.TaskAddr) 134 | start = mem.read_long(task_addr + address.YjRwStartAddr) 135 | end = mem.read_long(task_addr + address.YjRwEndAddr) 136 | num = int((end - start) / 16) 137 | 138 | for i in range(num): 139 | task_ptr = mem.read_long(start + i * 16) 140 | if mem.read_int(task_ptr) == task_id: 141 | task_level = mem.read_int(task_ptr + address.RwDjAddr) 142 | if task_level < self.map_data.get_role_level(): 143 | return True, task_level 144 | 145 | return False, 0 146 | 147 | def conditional(self, conditional) -> int: 148 | """ 149 | conditional_judgment 条件判断 150 | 1=城镇完成 比如:对话任务 2=刷图任务,需要进图 3=材料任务 151 | """ 152 | brush_conditions = [ 153 | "[meet npc]", 154 | "[seek n meet npc]", 155 | "[reach the range]", 156 | "[look cinematic]", 157 | "[question]", 158 | "[quest clear]", 159 | ] 160 | if conditional in brush_conditions: 161 | return 1 162 | 163 | brush_conditions = [ 164 | "[hunt monster]", 165 | "[hunt enemy]", 166 | "[condition under clear]", 167 | "[clear map]", 168 | "[question]", 169 | "[seeking]", 170 | "[clear dungeon index]", 171 | ] 172 | if conditional in brush_conditions: 173 | return 2 174 | 175 | return 0 176 | 177 | def task_map(self, task_id: int) -> int: 178 | """任务地图""" 179 | mem = self.mem 180 | task_addr = mem.read_long(address.TaskAddr) 181 | start = mem.read_long(task_addr + address.YjRwStartAddr) 182 | end = mem.read_long(task_addr + address.YjRwEndAddr) 183 | num = int((end - start) / 16) 184 | 185 | for i in range(num): 186 | task_ptr = mem.read_long(start + i * 16) 187 | if mem.read_int(task_ptr) == task_id: 188 | task_data = mem.read_long(task_ptr + address.RwFbAddr) 189 | return mem.read_int(task_data) 190 | return 0 191 | 192 | def submit_task(self): 193 | """提交任务""" 194 | mem = self.mem 195 | task_addr = mem.read_long(address.TaskAddr) 196 | start = mem.read_long(task_addr + address.QbRwStartAddr) 197 | end = mem.read_long(task_addr + address.QbRwEndAddr) 198 | num = int((end - start) / 8) 199 | 200 | for i in range(num): 201 | task_ptr = mem.read_long(start + i * 8) 202 | task_type = mem.read_int(task_ptr + address.RwLxAddr) 203 | if task_type == 0: 204 | task_id = mem.read_int(task_ptr) 205 | # self.pack.submit_task(task_id) 206 | call.submit_task_call(task_id) 207 | 208 | start = mem.read_long(task_addr + address.YjRwStartAddr) 209 | end = mem.read_long(task_addr + address.YjRwEndAddr) 210 | num = int((end - start) / 16) 211 | for i in range(num): 212 | task_ptr = mem.read_long(start + i * 16) 213 | task_type = mem.read_int(task_ptr + address.RwLxAddr) 214 | if task_type == 0: 215 | task_id = mem.read_int(task_ptr) 216 | # self.pack.submit_task(task_id) 217 | call.submit_task_call(task_id) 218 | 219 | def finish_status(self, task_id: int): 220 | """ 221 | 完成状态 222 | -1 任务未接 0 任务完成 1 已接任务 223 | """ 224 | mem = self.mem 225 | task_addr = mem.read_long(address.TaskAddr) 226 | start = mem.read_long(task_addr + address.YjRwStartAddr) 227 | end = mem.read_long(task_addr + address.YjRwEndAddr) 228 | num = int((end - start) / 16) 229 | 230 | tmp_arr = [] 231 | for i in range(num): 232 | pointer = mem.read_long(start + i * 16) 233 | if mem.read_int(pointer) == task_id: 234 | frequency = self.map_data.decode(start + i * 16 + 8) 235 | if frequency < 512: 236 | return frequency 237 | elif frequency == 512: 238 | return 1 239 | 240 | tmp_arr[0] = int(frequency % 512) 241 | the_rest = int(frequency) - tmp_arr[0] 242 | if the_rest < 262144: 243 | tmp_arr[1] = int(the_rest / 512) 244 | tmp_arr[1] = int(the_rest % 262144 / 512) 245 | the_rest = int(the_rest - tmp_arr[0] * 512) 246 | if the_rest < 262144: 247 | tmp_arr[2] = 0 248 | tmp_arr[2] = int(the_rest % 262144) 249 | # 数组排序 从大到小 250 | tmp_arr.sort(reverse=True) 251 | if tmp_arr[0] == 0: 252 | tmp_arr[0] = 1 253 | return tmp_arr[0] 254 | return -1 255 | 256 | def highest_map(self): 257 | """最高等级""" 258 | role_level = self.map_data.get_role_level() 259 | if role_level <= 17: 260 | if role_level <= 3: 261 | return 3 # 雷鸣废墟 262 | if role_level <= 5: 263 | return 5 # 雷鸣废墟 264 | if role_level <= 8: 265 | return 6 # 猛毒雷鸣废墟 266 | if role_level <= 11: 267 | return 9 # 冰霜幽暗密林 268 | if role_level <= 13: 269 | return 7 # 格拉卡 270 | if role_level <= 15: 271 | return 8 # 烈焰格拉卡 272 | if role_level <= 17: 273 | return 1000 # 暗黑雷鸣废墟 274 | return 0 275 | 276 | # 天空之城 277 | if role_level <= 23: 278 | if role_level <= 18: 279 | return 1000 # 10 龙人之塔 280 | if role_level <= 19: 281 | return 12 # 人偶玄关 282 | if role_level <= 20: 283 | return 13 # 石巨人塔 284 | if role_level <= 21: 285 | return 14 # 黑暗玄廊 286 | if role_level <= 22: 287 | return 17 # 悬空城 288 | if role_level <= 23: 289 | return 15 # 城主宫殿 290 | return 0 291 | 292 | # 神殿脊椎 293 | if role_level <= 29: 294 | if role_level <= 24: 295 | return 15 # 21 神殿外围 296 | if role_level <= 25: 297 | return 22 # 树精丛林 298 | if role_level <= 26: 299 | return 23 # 炼狱 300 | if role_level <= 27: 301 | return 24 # 极昼 302 | if role_level <= 28: 303 | return 25 # 第一脊椎 304 | if role_level <= 29: 305 | return 26 # 第二脊椎 306 | return 0 307 | 308 | # 暗精灵地区 309 | if role_level <= 35: 310 | if role_level <= 30: 311 | return 26 # 31 浅栖之地 312 | if role_level <= 31: 313 | return 32 # 蜘蛛洞穴 314 | if role_level <= 32: 315 | return 150 # 蜘蛛王国 316 | if role_level <= 33: 317 | return 151 # 英雄冢 318 | if role_level <= 34: 319 | return 35 # 暗精灵墓地 320 | if role_level <= 35: 321 | return 34 # 熔岩穴 322 | return 0 323 | 324 | # 祭坛 325 | if role_level <= 39: 326 | if role_level <= 36: 327 | return 34 # 152 暴君的祭坛 328 | if role_level <= 37: 329 | return 153 # 黄金矿洞 330 | if role_level <= 38: 331 | return 154 # 远古墓穴深处 332 | if role_level <= 39: 333 | return 154 # 远古墓穴深处 334 | return 0 335 | 336 | # 雪山 337 | if role_level <= 45: 338 | if role_level <= 40: 339 | return 154 # 40 山脊 340 | if role_level <= 41: 341 | return 41 # 冰心少年 342 | if role_level <= 42: 343 | return 42 # 利库天井 344 | if role_level <= 44: 345 | return 141 # 布万加的修炼场 346 | if role_level <= 45: 347 | return 141 # 布万加的修炼场 348 | return 0 349 | 350 | # 绿都 351 | if role_level <= 49: 352 | if role_level <= 46: 353 | return 141 # 61 绿都格罗兹尼 354 | if role_level <= 47: 355 | return 50 # 堕落的盗贼 356 | if role_level <= 48: 357 | return 51 # 迷乱之村哈穆林 358 | if role_level <= 49: 359 | return 53 # 疑惑之村 360 | return 0 361 | 362 | if role_level <= 53: 363 | if role_level <= 50: 364 | return 53 # 144 炽晶森林 365 | if role_level <= 51: 366 | return 145 # 冰晶森林 367 | if role_level <= 52: 368 | return 146 # 水晶矿脉 369 | if role_level <= 53: 370 | return 148 # 幽冥监狱 371 | return 0 372 | 373 | if role_level <= 58: 374 | if role_level <= 54: 375 | return 148 # 156 蘑菇庄园 376 | if role_level <= 55: 377 | return 157 # 蚁后的巢穴 378 | if role_level <= 56: 379 | return 158 # 腐烂之地 380 | if role_level <= 57: 381 | return 159 # 赫顿玛尔旧街区 382 | if role_level <= 58: 383 | return 160 # 鲨鱼栖息地 384 | return 0 385 | 386 | if role_level <= 62: 387 | if role_level <= 59: 388 | return 160 # 162 '人鱼国度 389 | if role_level <= 60: 390 | return 163 # GBL女神殿 391 | if role_level <= 61: 392 | return 164 # 树精繁殖地 393 | if role_level <= 62: 394 | return 164 # 树精繁殖地 395 | return 0 396 | 397 | if role_level <= 70: 398 | if role_level <= 63: 399 | return 164 # 80 '根特外围 400 | if role_level <= 64: 401 | return 81 # 根特东门 402 | if role_level <= 65: 403 | return 82 # 根特南门 404 | if role_level <= 66: 405 | return 88 # 根特北门 406 | if role_level <= 67: 407 | return 88 # 根特北门 408 | if role_level <= 68: 409 | return 83 # 夜间袭击战 410 | if role_level <= 69: 411 | return 84 # 补给阻断站 412 | if role_level <= 70: 413 | return 85 # 追击歼灭战 414 | return 0 415 | 416 | # 海上列车 417 | if role_level <= 74: 418 | if role_level <= 71: 419 | return 85 # 86 列车上的海贼 420 | if role_level <= 71: 421 | return 87 # 夺回西部线 422 | if role_level <= 73: 423 | return 92 # 雾都赫伊斯 424 | if role_level <= 74: 425 | return 93 # 阿登高地 426 | return 0 427 | 428 | if role_level <= 80: 429 | if role_level <= 75: 430 | return 93 # 70 格兰之火 431 | if role_level <= 76: 432 | return 71 # 瘟疫之源 433 | if role_level <= 77: 434 | return 72 # 卡勒特之刃 435 | if role_level <= 78: 436 | return 74 # 绝密区域 437 | if role_level <= 79: 438 | return 75 # 昔日悲鸣 439 | if role_level <= 80: 440 | return 76 # 凛冬 441 | return 0 442 | 443 | if role_level <= 85: 444 | if role_level <= 81: 445 | return 76 # 102 普鲁兹发电站 446 | if role_level <= 82: 447 | return 103 # 特伦斯发电站 448 | if role_level <= 85: 449 | return 104 # 格蓝迪发电站 450 | return 0 451 | 452 | if role_level <= 86: 453 | return 192 # 钢铁之臂 454 | if role_level <= 90: 455 | if role_level <= 87: 456 | return 310 # 时间广场 457 | if role_level <= 88: 458 | return 312 # 恐怖的栖息地 459 | if role_level <= 89: 460 | return 314 # 红色魔女之森 461 | if role_level <= 90: 462 | return 314 # 红色魔女之森 463 | return 0 464 | 465 | if role_level <= 100: 466 | if role_level <= 95: 467 | return 291100293 # 全蚀市场 468 | if role_level <= 98: 469 | return 291100293 # 搏击俱乐部 470 | 471 | if role_level <= 109: 472 | if role_level <= 100: 473 | return 100002975 # 圣域外围 474 | if role_level <= 102: 475 | return 100002976 # 圣域中心 476 | if role_level <= 103: 477 | return 100002977 # 泽尔峡谷 478 | if role_level <= 104: 479 | return 100002978 # 洛仑山 480 | if role_level <= 105: 481 | return 100002979 # 白色雪原 482 | if role_level <= 106: 483 | return 100002980 # 贝奇的空间 484 | if role_level <= 107: 485 | return 100002981 486 | if role_level <= 108: 487 | return 100002982 488 | if role_level <= 109: 489 | return 100002983 490 | 491 | return 0 492 | -------------------------------------------------------------------------------- /game/traversal.py: -------------------------------------------------------------------------------- 1 | from common import config, helper 2 | from common import logger 3 | from game import call, address, rand_skill 4 | from plugins.driver.button import drive_button 5 | from plugins.driver.keyboard import VK_X 6 | 7 | 8 | class Traversal: 9 | def __init__(self, mem, pack, map_data): 10 | self.mem = mem 11 | self.pack = pack 12 | self.map_data = map_data 13 | 14 | def is_exists_item(self) -> bool: 15 | """ 16 | 是否存在物品 17 | :return: bool 18 | """ 19 | if self.map_data.get_stat() != 3: 20 | return False 21 | 22 | mem = self.mem 23 | item_config = config().get("自动配置", "过滤物品").split(",") 24 | data = self.map_data.get_map_data() 25 | 26 | for data.obj_tmp in range(1, data.obj_num): 27 | obj_ptr = self.map_data.get_traversal_ptr(data.start, data.obj_tmp, 2) 28 | obj_type_a = mem.read_int(obj_ptr + address.LxPyAddr) 29 | obj_type_b = mem.read_int(obj_ptr + address.LxPyAddr + 4) 30 | obj_camp = mem.read_int(obj_ptr + address.ZyPyAddr) 31 | 32 | if (obj_type_a == 289 or obj_type_b == 289) and obj_camp == 200: 33 | goods_name_ptr = mem.read_long(mem.read_long(obj_ptr + address.DmWpAddr) + address.WpMcAddr) 34 | goods_name_byte = mem.read_bytes(goods_name_ptr, 100) 35 | obj_type_b_name = helper.unicode_to_ascii(list(goods_name_byte)) 36 | if obj_type_b_name in item_config: 37 | continue 38 | 39 | if obj_ptr != data.rw_addr: 40 | return True 41 | 42 | return False 43 | 44 | def pickup(self): 45 | """ 46 | 组包捡物 47 | :return: 48 | """ 49 | mem = self.mem 50 | item_config = config().get("自动配置", "过滤物品").split(",") 51 | data = self.map_data.get_map_data() 52 | # 遍历地图数据 53 | for data.obj_tmp in range(1, data.obj_num): 54 | obj_ptr = self.map_data.get_traversal_ptr(data.start, data.obj_tmp, 2) 55 | obj_type_a = mem.read_int(obj_ptr + address.LxPyAddr) 56 | obj_type_b = mem.read_int(obj_ptr + address.LxPyAddr + 4) 57 | obj_camp = mem.read_int(obj_ptr + address.ZyPyAddr) 58 | if (obj_type_a == 289 or obj_type_b == 289) and obj_camp == 200: 59 | goods_name_ptr = mem.read_long(mem.read_long(obj_ptr + address.DmWpAddr) + address.WpMcAddr) 60 | goods_name_byte = mem.read_bytes(goods_name_ptr, 100) 61 | obj_type_b_name = helper.unicode_to_ascii(list(goods_name_byte)) 62 | if obj_type_b_name in item_config: 63 | continue 64 | 65 | if obj_ptr != data.rw_addr: 66 | res_addr = self.map_data.decode(obj_ptr + address.FbSqAddr) 67 | self.pack.pick_up(res_addr) 68 | 69 | def follow_monster(self): 70 | """跟随怪物""" 71 | if self.map_data.get_stat() != 3: 72 | return 73 | 74 | mem = self.mem 75 | data = self.map_data.get_map_data() 76 | map_obj = self.map_data 77 | # 遍历地图数据 78 | for data.obj_tmp in range(1, data.obj_num): 79 | obj_ptr = map_obj.get_traversal_ptr(data.start, data.obj_tmp, 2) 80 | if obj_ptr is None or obj_ptr <= 0: 81 | continue 82 | 83 | obj_type_a = mem.read_int(obj_ptr + address.LxPyAddr) 84 | if obj_type_a == 529 or obj_type_a == 545 or obj_type_a == 273 or obj_type_a == 61440: 85 | obj_camp = mem.read_int(obj_ptr + address.ZyPyAddr) 86 | obj_code = mem.read_int(obj_ptr + address.DmPyAddr) 87 | obj_blood = mem.read_long(obj_ptr + address.GwXlAddr) 88 | if obj_camp > 0 and obj_ptr != data.rw_addr: 89 | monster = map_obj.read_coordinate(obj_ptr) 90 | if obj_blood > 0: 91 | call.drift_call(data.rw_addr, monster.x, monster.y, 0, 2) 92 | helper.sleep(200) 93 | if config().getint("自动配置", "跟随打怪") == 2: 94 | title = helper.get_process_name() 95 | if title == "地下城与勇士:创新世纪": 96 | drive_button(VK_X, 1, False) 97 | helper.sleep(800) 98 | drive_button(VK_X, 2, False) 99 | helper.sleep(100) 100 | drive_button(rand_skill(), 0, False) 101 | if config().getint("自动配置", "跟随打怪") == 3: 102 | call.skill_call(data.rw_addr, 70231, 99999, monster.x, monster.y, 0, 1.0) 103 | 104 | def ignore_building(self, ok: bool): 105 | """无视建筑""" 106 | rd_addr = call.person_ptr() 107 | if ok: 108 | self.mem.write_int(rd_addr + address.JzCtAddr, 0) 109 | self.mem.write_int(rd_addr + address.DtCtAddr, 0) 110 | else: 111 | self.mem.write_int(rd_addr + address.JzCtAddr, 40) 112 | self.mem.write_int(rd_addr + address.DtCtAddr, 10) 113 | 114 | def handle_equip(self): 115 | """处理装备""" 116 | handle_type = config().getint("自动配置", "处理装备") 117 | if handle_type == 0: 118 | return 119 | 120 | back_pack_weight = self.map_data.back_pack_weight() 121 | if back_pack_weight < 60: 122 | return 123 | 124 | self.pack.tidy_backpack(1, 0) 125 | num = 0 126 | mem = self.mem 127 | addr = mem.read_long(mem.read_long(address.BbJzAddr) + address.WplPyAddr) + 0x48 # 装备栏偏移 128 | for i in range(1, 56): 129 | equip = self.map_data.get_traversal_ptr(addr, i, 1) 130 | if equip is not None and equip > 0: 131 | equip_level = mem.read_int(equip + address.ZbPjAddr) 132 | name_addr = mem.read_long(equip + address.WpMcAddr) # 装备名称 133 | name_bytes = list(mem.read_bytes(name_addr, 100)) 134 | equip_name = helper.unicode_to_ascii(name_bytes) 135 | if equip_level in [0, 1, 2]: 136 | logger.info("处理装备 {}".format(equip_name), 1) 137 | self.pack.decomposition(i + 9) 138 | helper.sleep(200) 139 | num += 1 140 | 141 | self.pack.tidy_backpack(1, 0) 142 | logger.info("处理装备 {} 件".format(num), 1) 143 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | import _thread 2 | import ctypes 3 | import time 4 | 5 | import xcgui._xcgui as gui 6 | from xcgui import XApp 7 | from xcgui import XWindow, XButton, XEdit, XShapeText 8 | 9 | from common import helper, logger, globle 10 | from game import init 11 | from game import mem 12 | from plugins.driver import init_driver 13 | 14 | svgIcon = '' 27 | 28 | version = '1.0.0' 29 | 30 | 31 | class AppWindow(XWindow): 32 | 33 | def __init__(self): 34 | super(AppWindow, self).__init__(0, 0, 302, 400, "", 0, gui.window_style_modal) 35 | self.setTitle("情歌 √ Lang [ Python ]") 36 | # 关闭窗口事件 37 | self.regEvent(16, self.close_win) 38 | # 线程开关 39 | self.run = True 40 | # 定时设置标题 41 | # _thread.start_new_thread(self.title_time, ()) 42 | # 设置窗口图标 43 | self.setIcon(gui.XImage.loadSvgString(svgIcon)) 44 | # 禁止改变大小 45 | self.enableDragBorder(False) 46 | # 设置边框 47 | self.setBorderSize(0, 30, 0, 0) 48 | self.setBorderSize(0, 30, 0, 0) 49 | # 设置窗口置顶 50 | # self.setTop() 51 | 52 | XShapeText(0, 35, 60, 30, "卡号:", self) 53 | self.card_edit = XEdit(35, 35, 200, 30, self) 54 | self.card_edit.setText("19930921") 55 | self.card_edit.enablePassword(True) 56 | self.card_edit.setTextAlign(gui.edit_textAlign_flag_center) 57 | 58 | self.activation_but = XButton(244, 35, 50, 30, "激活", self) 59 | self.activation_but.regEvent(gui.XE_BNCLICK, self.activation) 60 | 61 | self.func_content = XEdit(1, 70, 300, 100, self) 62 | self.func_content.enableMultiLine(True) 63 | self.func_content.enableReadOnly(True) 64 | self.func_content.autoScroll() 65 | self.func_content.showSBarV(True) 66 | # self.func_content.showSBarH(True) 67 | self.func_content.scrollBottom() 68 | 69 | self.edit_content = XEdit(1, 175, 300, 200, self) 70 | self.edit_content.enableMultiLine(True) 71 | self.edit_content.enableReadOnly(True) 72 | self.edit_content.autoScroll() 73 | self.edit_content.showSBarV(True) 74 | # self.edit_content.showSBarH(True) 75 | self.edit_content.scrollBottom() 76 | 77 | self.run_time_label = XShapeText(1, 375, 60, 30, "运行时间:", self) 78 | self.run_time_value = XShapeText(56, 375, 60, 30, "00:00:00", self) 79 | _thread.start_new_thread(self.app_run_time, ()) 80 | 81 | self.version_label = XShapeText(220, 375, 60, 30, "版本号:", self) 82 | self.version_value = XShapeText(260, 375, 60, 30, version, self) 83 | 84 | def close_win(self, event, userdata) -> bool: 85 | self.run = False 86 | print('推出') 87 | self.closeWindow() 88 | return False 89 | 90 | def activation(self, event, userdata) -> bool: 91 | process_id = helper.get_process_id_by_name("DNF.exe") 92 | if process_id == 0: 93 | helper.message_box("请打开dnf后运行") 94 | return False 95 | card_edit_val = self.card_edit.getText() 96 | if card_edit_val != "19930921": 97 | helper.message_box("卡密错误") 98 | 99 | mem.set_process_id(process_id) 100 | init.init_empty_addr() 101 | self.add_edit_content("加载成功-欢迎使用") 102 | self.add_edit_content("当前时间:{}".format(helper.get_now_date())) 103 | 104 | self.activation_but.enable(False) 105 | # 初始化fastcall 106 | # init.call.init_call() 107 | 108 | init.hotkey() 109 | return True 110 | 111 | def app_run_time(self): 112 | while self.run: 113 | time.sleep(1) 114 | self.run_time_value.setText(helper.get_app_run_time()) 115 | self.run_time_value.redraw() 116 | 117 | def title_time(self): 118 | while self.run: 119 | time.sleep(1) 120 | self.setTitle("情歌 √ 当前时间 {}".format(helper.get_now_date())) 121 | self.redraw() 122 | 123 | def add_edit_content(self, msg): 124 | self.edit_content.addTextUser("{}\n".format(msg)) 125 | self.redraw() 126 | 127 | def add_func_content(self, msg): 128 | self.func_content.addTextUser("{}\n".format(msg)) 129 | self.redraw() 130 | 131 | 132 | if __name__ == '__main__': 133 | try: 134 | globle.cmd = "gui" 135 | # init_driver("LoveRw") 136 | app = XApp() 137 | win = AppWindow() 138 | globle.win_app = win 139 | logger.info("驱动加载成功", 1) 140 | win.showWindow() 141 | 142 | hwnd = win.getHWND() 143 | ctypes.windll.user32.SetWindowPos(hwnd, -1, 0, 0, 0, 0, 3) 144 | app.run() 145 | app.exit() 146 | except KeyboardInterrupt as e: 147 | print("信道推出") 148 | except Exception as err: 149 | helper.print_trace("gui", err) 150 | -------------------------------------------------------------------------------- /log.txt: -------------------------------------------------------------------------------- 1 | Mon Feb 17 01:01:44 PM CST 2025: Daily auto commit 2 | Mon Feb 17 01:05:22 PM CST 2025: Daily auto commit 3 | Mon Feb 17 01:07:20 PM CST 2025: Daily auto commit 4 | Mon Feb 17 01:08:10 PM CST 2025: Daily auto commit 5 | Tue Feb 18 01:30:01 AM CST 2025: Daily auto commit 6 | Wed Feb 19 01:30:01 AM CST 2025: Daily auto commit 7 | Thu Feb 20 01:30:01 AM CST 2025: Daily auto commit 8 | Fri Feb 21 01:30:01 AM CST 2025: Daily auto commit 9 | Sat Feb 22 01:30:01 AM CST 2025: Daily auto commit 10 | Sun Feb 23 01:30:01 AM CST 2025: Daily auto commit 11 | Mon Feb 24 01:30:01 AM CST 2025: Daily auto commit 12 | Tue Feb 25 01:30:01 AM CST 2025: Daily auto commit 13 | Wed Feb 26 01:30:01 AM CST 2025: Daily auto commit 14 | Thu Feb 27 01:30:01 AM CST 2025: Daily auto commit 15 | Fri Feb 28 01:30:01 AM CST 2025: Daily auto commit 16 | Sat Mar 1 01:30:01 AM CST 2025: Daily auto commit 17 | Sun Mar 2 01:30:01 AM CST 2025: Daily auto commit 18 | Mon Mar 3 01:30:01 AM CST 2025: Daily auto commit 19 | Tue Mar 4 01:30:01 AM CST 2025: Daily auto commit 20 | Sat Mar 8 01:10:01 AM CST 2025: Daily auto commit 21 | Sat Mar 15 01:10:01 AM CST 2025: Daily auto commit 22 | Sat Mar 22 01:10:01 AM CST 2025: Daily auto commit 23 | Sat Mar 29 01:10:01 AM CST 2025: Daily auto commit 24 | Sat Apr 5 01:10:01 AM CST 2025: Daily auto commit 25 | Sat Apr 12 01:10:01 AM CST 2025: Daily auto commit 26 | Sat Apr 19 01:10:01 AM CST 2025: Daily auto commit 27 | Sat Apr 26 01:10:01 AM CST 2025: Daily auto commit 28 | Sat May 3 01:10:02 AM CST 2025: Daily auto commit 29 | Sat May 10 01:10:01 AM CST 2025: Daily auto commit 30 | Sat May 17 01:10:01 AM CST 2025: Daily auto commit 31 | Sat May 24 01:10:01 AM CST 2025: Daily auto commit 32 | Sat May 31 01:10:01 AM CST 2025: Daily auto commit 33 | Sat Jun 7 01:10:01 AM CST 2025: Daily auto commit 34 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from common import helper, logger, globle, file 4 | from game import init, mem 5 | 6 | 7 | def check_windows(model_name) -> int: 8 | """ 9 | 读窗口句柄 10 | """ 11 | while True: 12 | result = helper.find_window("地下城与勇士", "地下城与勇士:创新世纪") 13 | if result is not None: 14 | process_id = helper.get_process_id_by_name(model_name) 15 | if process_id != 0: 16 | logger.info("附加游戏成功", 1) 17 | return process_id 18 | time.sleep(0.2) 19 | 20 | 21 | def init_config(): 22 | """ 23 | 全局刷图计次初始化 24 | """ 25 | count_cfg_name = "C:\\config.ini" 26 | conf_exists = file.path_exists(count_cfg_name) 27 | if not conf_exists: 28 | file.write_ini(count_cfg_name, "default", "count", "0") 29 | 30 | 31 | def main(): 32 | globle.cmd = "cmd" 33 | model_name = "DNF.exe" 34 | process_id = helper.get_process_id_by_name(model_name) 35 | if process_id == 0: 36 | logger.info("等待游戏运行...", 1) 37 | process_id = check_windows(model_name) 38 | 39 | mem.set_process_id(process_id) 40 | # 判断是否有图标 41 | if mem.read_int(0x140000000) != 9460301: 42 | raise Exception("无读写权限") 43 | # 初始化全局空白地址 44 | init.init_empty_addr() 45 | logger.info("加载成功-欢迎使用", 1) 46 | logger.info("当前时间:{}".format(helper.get_now_date()), 1) 47 | init.hotkey() 48 | 49 | 50 | if __name__ == '__main__': 51 | try: 52 | main() 53 | except KeyboardInterrupt as e: 54 | print("信道推出") 55 | except Exception as err: 56 | helper.print_trace("main", err) 57 | -------------------------------------------------------------------------------- /plugins/api/__init__.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | pass 3 | -------------------------------------------------------------------------------- /plugins/api/advapi32.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/plugins/api/advapi32.py -------------------------------------------------------------------------------- /plugins/api/kernel32.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/plugins/api/kernel32.py -------------------------------------------------------------------------------- /plugins/driver/__init__.py: -------------------------------------------------------------------------------- 1 | from plugins.driver.derive import MemoryRw 2 | from common.file import force_delete_file 3 | 4 | driver = MemoryRw() 5 | 6 | 7 | def init_driver(driver_name: str): 8 | driver_path = "C:\\{}.sys".format(driver_name) 9 | import os 10 | if os.path.exists(driver_path) is False: 11 | raise Exception("驱动不存在") 12 | 13 | if not driver.load_driver(driver_path, driver_name, driver_name): 14 | raise Exception("驱动加载失败") 15 | 16 | force_delete_file(driver_path) -------------------------------------------------------------------------------- /plugins/driver/button.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/plugins/driver/button.py -------------------------------------------------------------------------------- /plugins/driver/derive.py: -------------------------------------------------------------------------------- 1 | import win32service as service 2 | import winerror as error 3 | 4 | 5 | class MemoryRw: 6 | # SCM句柄 7 | hSCManager = int 8 | # 服务句柄 9 | hService = None 10 | 11 | def __init__(self): 12 | pass 13 | 14 | def load_driver(self, driver_path: str, service_name: str, display_name: str) -> bool: 15 | """ 16 | 加载驱动 17 | :param driver_path 驱动路径 18 | :param service_name 驱动名称 19 | :param display_name 显示名称 20 | :return boolean 21 | """ 22 | result = False 23 | 24 | self.hSCManager = service.OpenSCManager(None, None, service.SC_MANAGER_ALL_ACCESS) 25 | if self.hSCManager == 0: 26 | return False 27 | 28 | try: 29 | self.hService = service.CreateService(self.hSCManager, service_name, display_name, 30 | service.SERVICE_ALL_ACCESS, service.SERVICE_KERNEL_DRIVER, 31 | service.SERVICE_DEMAND_START, service.SERVICE_ERROR_IGNORE, 32 | driver_path, None, 0, None, None, None) 33 | except service.error as create_err: 34 | # 1073 服务已经存在 35 | if create_err.winerror == error.ERROR_SERVICE_EXISTS: 36 | self.hService = service.OpenService(self.hSCManager, service_name, service.SC_MANAGER_ALL_ACCESS) 37 | finally: 38 | try: 39 | if int(self.hService) > 0: 40 | start_service_res = service.StartService(self.hService, None) 41 | if start_service_res is None: 42 | result = True 43 | except service.error as start_err: 44 | # 1056 服务运行,1072服务标记删除 45 | if start_err.winerror in [error.ERROR_SERVICE_ALREADY_RUNNING, error.ERROR_SERVICE_MARKED_FOR_DELETE]: 46 | result = True 47 | else: 48 | service.CloseServiceHandle(self.hService) 49 | 50 | if int(self.hService) <= 0: 51 | service.CloseServiceHandle(self.hSCManager) 52 | 53 | return result 54 | 55 | def un_load_driver(self) -> tuple: 56 | """驱动卸载""" 57 | try: 58 | status = service.ControlService(self.hService, service.SERVICE_CONTROL_STOP) 59 | service.DeleteService(self.hService) 60 | finally: 61 | service.CloseServiceHandle(self.hService) 62 | 63 | service.CloseServiceHandle(self.hSCManager) 64 | 65 | return status 66 | -------------------------------------------------------------------------------- /plugins/driver/keyboard.py: -------------------------------------------------------------------------------- 1 | VK_LBUTTON = 0x01 2 | VK_RBUTTON = 0x02 3 | VK_CANCEL = 0x03 4 | VK_MBUTTON = 0x04 5 | VK_XBUTTON1 = 0x05 6 | VK_XBUTTON2 = 0x06 7 | VK_BACK = 0x08 8 | VK_TAB = 0x09 9 | VK_CLEAR = 0x0C 10 | VK_ENTER = 0x0D 11 | VK_PAUSE = 0x13 12 | VK_CAPITAL = 0x14 13 | VK_KANA = 0x15 14 | VK_HANGUEL = 0x15 15 | VK_HANGUL = 0x15 16 | VK_JUNJA = 0x17 17 | VK_FINAL = 0x18 18 | VK_HANJA = 0x19 19 | VK_KANJI = 0x19 20 | VK_ESC = 0x1B 21 | VK_CONVERT = 0x1C 22 | VK_NONCONVERT = 0x1D 23 | VK_ACCEPT = 0x1E 24 | VK_MODECHANGE = 0x1F 25 | VK_SPACE = 0x20 26 | VK_PRIOR = 0x21 27 | VK_NEXT = 0x22 28 | VK_END = 0x23 29 | VK_HOME = 0x24 30 | VK_LEFT = 0x25 31 | VK_UP = 0x26 32 | VK_RIGHT = 0x27 33 | VK_DOWN = 0x28 34 | VK_SELECT = 0x29 35 | VK_PRINT = 0x2A 36 | VK_EXECUTE = 0x2B 37 | VK_SNAPSHOT = 0x2C 38 | VK_INSERT = 0x2D 39 | VK_DELETE = 0x2E 40 | VK_HELP = 0x2F 41 | VK_0 = 0x30 42 | VK_1 = 0x31 43 | VK_2 = 0x32 44 | VK_3 = 0x33 45 | VK_4 = 0x34 46 | VK_5 = 0x35 47 | VK_6 = 0x36 48 | VK_7 = 0x37 49 | VK_8 = 0x38 50 | VK_9 = 0x39 51 | VK_A = 0x41 52 | VK_B = 0x42 53 | VK_C = 0x43 54 | VK_D = 0x44 55 | VK_E = 0x45 56 | VK_F = 0x46 57 | VK_G = 0x47 58 | VK_H = 0x48 59 | VK_I = 0x49 60 | VK_J = 0x4A 61 | VK_K = 0x4B 62 | VK_L = 0x4C 63 | VK_M = 0x4D 64 | VK_N = 0x4E 65 | VK_O = 0x4F 66 | VK_P = 0x50 67 | VK_Q = 0x51 68 | VK_R = 0x52 69 | VK_S = 0x53 70 | VK_T = 0x54 71 | VK_U = 0x55 72 | VK_V = 0x56 73 | VK_W = 0x57 74 | VK_X = 0x58 75 | VK_Y = 0x59 76 | VK_Z = 0x5A 77 | VK_LWIN = 0x5B 78 | VK_RWIN = 0x5C 79 | VK_APPS = 0x5D 80 | VK_SLEEP = 0x5F 81 | VK_NUMPAD0 = 0x60 82 | VK_NUMPAD1 = 0x61 83 | VK_NUMPAD2 = 0x62 84 | VK_NUMPAD3 = 0x63 85 | VK_NUMPAD4 = 0x64 86 | VK_NUMPAD5 = 0x65 87 | VK_NUMPAD6 = 0x66 88 | VK_NUMPAD7 = 0x67 89 | VK_NUMPAD8 = 0x68 90 | VK_NUMPAD9 = 0x69 91 | VK_MULTIPLY = 0x6A 92 | VK_ADD = 0x6B 93 | VK_SEPARATOR = 0x6C 94 | VK_SUBTRACT = 0x6D 95 | VK_DECIMAL = 0x6E 96 | VK_DIVIDE = 0x6F 97 | VK_F1 = 0x70 98 | VK_F2 = 0x71 99 | VK_F3 = 0x72 100 | VK_F4 = 0x73 101 | VK_F5 = 0x74 102 | VK_F6 = 0x75 103 | VK_F7 = 0x76 104 | VK_F8 = 0x77 105 | VK_F9 = 0x78 106 | VK_F10 = 0x79 107 | VK_F11 = 0x7A 108 | VK_F12 = 0x7B 109 | VK_F13 = 0x7C 110 | VK_F14 = 0x7D 111 | VK_F15 = 0x7E 112 | VK_F16 = 0x7F 113 | VK_F17 = 0x80 114 | VK_F18 = 0x81 115 | VK_F19 = 0x82 116 | VK_F20 = 0x83 117 | VK_F21 = 0x84 118 | VK_F22 = 0x85 119 | VK_F23 = 0x86 120 | VK_F24 = 0x87 121 | VK_NUMLOCK = 0x90 122 | VK_SCROLL = 0x91 123 | VK_CTRL = 0xA2 124 | VK_LALT = 0xA4 125 | VK_RALT = 0xA5 126 | VK_BROWSER_BACK = 0xA6 127 | VK_BROWSER_FORWARD = 0xA7 128 | VK_BROWSER_REFRESH = 0xA8 129 | VK_BROWSER_STOP = 0xA9 130 | VK_BROWSER_SEARCH = 0xAA 131 | VK_BROWSER_FAVORITES = 0xAB 132 | VK_BROWSER_HOME = 0xAC 133 | VK_VOLUME_MUTE = 0xAD 134 | VK_VOLUME_DOWN = 0xAE 135 | VK_VOLUME_UP = 0xAF 136 | VK_MEDIA_NEXT_TRACK = 0xB0 137 | VK_MEDIA_PREV_TRACK = 0xB1 138 | VK_MEDIA_STOP = 0xB2 139 | VK_MEDIA_PLAY_PAUSE = 0xB3 140 | VK_LAUNCH_MAIL = 0xB4 141 | VK_LAUNCH_MEDIA_SELECT = 0xB5 142 | VK_LAUNCH_APP1 = 0xB6 143 | VK_LAUNCH_APP2 = 0xB7 144 | VK_OEM_1 = 0xBA 145 | VK_OEM_PLUS = 0xBB 146 | VK_OEM_COMMA = 0xBC 147 | VK_OEM_MINUS = 0xBD 148 | VK_OEM_PERIOD = 0xBE 149 | VK_OEM_2 = 0xBF 150 | VK_OEM_3 = 0xC0 151 | VK_OEM_4 = 0xDB 152 | VK_OEM_5 = 0xDC 153 | VK_OEM_6 = 0xDD 154 | VK_OEM_7 = 0xDE 155 | VK_OEM_8 = 0xDF 156 | VK_OEM_102 = 0xE2 157 | VK_PROCESSKEY = 0xE5 158 | VK_PACKET = 0xE7 159 | VK_ATTN = 0xF6 160 | VK_CRSEL = 0xF7 161 | VK_EXSEL = 0xF8 162 | VK_EREOF = 0xF9 163 | VK_PLAY = 0xFA 164 | VK_ZOOM = 0xFB 165 | VK_NONAME = 0xFC 166 | VK_PA1 = 0xFD 167 | VK_OEM_CLEAR = 0xFE 168 | 169 | SC_F1 = 0x3B 170 | SC_F2 = 0x3C 171 | SC_F3 = 0x3D 172 | SC_F4 = 0x3E 173 | SC_F5 = 0x3F 174 | SC_F6 = 0x40 175 | SC_F7 = 0x41 176 | SC_F8 = 0x42 177 | SC_F9 = 0x43 178 | SC_F10 = 0x44 179 | SC_F11 = 0x57 180 | SC_F12 = 0x58 181 | 182 | WH_KEYBOARD_LL = 13 183 | WH_MOUSE_LL = 14 184 | WH_KEYBOARD = 2 185 | WM_SYSKEYDOWN = 260 186 | WM_SYSKEYUP = 261 187 | WM_KEYFIRST = 256 188 | WM_KEYLAST = 264 189 | PM_NOREMOVE = 0x000 190 | PM_REMOVE = 0x001 191 | PM_NOYIELD = 0x002 192 | WM_LBUTTONDOWN = 513 193 | WM_RBUTTONDOWN = 516 194 | NULL = 0 195 | 196 | WM_KEYUP = 0x0101 197 | WM_KEYDOWN = 0x0100 198 | WM_CHAR = 0x0102 199 | -------------------------------------------------------------------------------- /plugins/logger/console.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from plugins.logger.interface import LogInterface 4 | 5 | 6 | class ColoredFormatter(logging.Formatter): 7 | def format(self, record): 8 | # 为不同的日志级别定义一些颜色 9 | colors = { 10 | 'DEBUG': '\033[94m', # blue 11 | 'INFO': '\033[92m', # green 12 | 'WARNING': '\033[93m', # yellow 13 | 'ERROR': '\033[91m', # red 14 | 'CRITICAL': '\033[95m' # magenta 15 | } 16 | # 从记录中获取原始消息 17 | message = super().format(record) 18 | # 如果日志级别定义了颜色,则添加颜色代码 19 | if record.levelname in colors: 20 | color_code = colors[record.levelname] 21 | message = f"{color_code}{message}\033[0m" 22 | return message 23 | 24 | 25 | class ConsoleLog(LogInterface): 26 | def __init__(self): 27 | self.logger = logging.getLogger(__name__) 28 | self.logger.setLevel(logging.DEBUG) 29 | 30 | # 使用彩色格式化程序创建控制台处理程序 31 | console_handler = logging.StreamHandler() 32 | console_handler.setFormatter(ColoredFormatter('%(asctime)s %(message)s')) 33 | 34 | # 将控制台处理程序添加到记录器 35 | self.logger.addHandler(console_handler) 36 | 37 | def info(self, arg): 38 | self.logger.info(arg) 39 | 40 | def debug(self, arg): 41 | self.logger.debug(arg) 42 | 43 | def warning(self, arg): 44 | self.logger.warning(arg) 45 | 46 | def error(self, arg): 47 | self.logger.error(arg) 48 | 49 | def critical(self, arg): 50 | self.logger.critical(arg) 51 | -------------------------------------------------------------------------------- /plugins/logger/file.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from plugins.logger.interface import LogInterface 4 | 5 | 6 | class FileLog(LogInterface): 7 | def __init__(self): 8 | # 创建 logger 对象 9 | self.logger = logging.getLogger(__name__) 10 | self.logger.setLevel(logging.DEBUG) 11 | 12 | # 创建 FileHandler 对象 13 | file_handler = logging.FileHandler('debug.log') 14 | file_handler.setLevel(logging.DEBUG) 15 | 16 | # 创建 Formatter 对象 17 | formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 18 | file_handler.setFormatter(formatter) 19 | 20 | # 添加 Formatter 对象到 FileHandler 对象中 21 | file_handler.setFormatter(formatter) 22 | 23 | # 添加 FileHandler 对象到 logger 对象中 24 | self.logger.addHandler(file_handler) 25 | 26 | def info(self, arg): 27 | self.logger.info(arg) 28 | 29 | def debug(self, arg): 30 | self.logger.debug(arg) 31 | 32 | def warning(self, arg): 33 | self.logger.warning(arg) 34 | 35 | def error(self, arg): 36 | self.logger.error(arg) 37 | 38 | def critical(self, arg): 39 | self.logger.critical(arg) 40 | -------------------------------------------------------------------------------- /plugins/logger/gui.py: -------------------------------------------------------------------------------- 1 | from common import globle 2 | from plugins.logger.interface import LogInterface 3 | 4 | 5 | class GuiLog(LogInterface): 6 | def info(self, arg): 7 | globle.win_app.add_func_content(arg) 8 | 9 | def debug(self, arg): 10 | globle.win_app.add_func_content(arg) 11 | 12 | def warning(self, arg): 13 | globle.win_app.add_func_content(arg) 14 | 15 | def error(self, arg): 16 | globle.win_app.add_func_content(arg) 17 | 18 | def critical(self, arg): 19 | globle.win_app.add_edit_content(arg) 20 | -------------------------------------------------------------------------------- /plugins/logger/interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class LogInterface(ABC): 5 | """ 6 | 定义一个名为 LogInterface 日志 的接口 7 | """ 8 | 9 | @abstractmethod 10 | def info(self, arg): 11 | pass 12 | 13 | @abstractmethod 14 | def debug(self, arg): 15 | pass 16 | 17 | @abstractmethod 18 | def warning(self, arg): 19 | pass 20 | 21 | @abstractmethod 22 | def error(self, arg): 23 | pass 24 | 25 | @abstractmethod 26 | def critical(self, arg): 27 | pass 28 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | keyboard==0.13.5 2 | psutil==5.9.5 3 | Pymem==1.12.0 4 | pywin32==306 5 | xcgui==0.1.7 6 | pyinstaller==5.13.2 7 | keystone_engine==0.9.2 -------------------------------------------------------------------------------- /static/WeGame.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/static/WeGame.ico -------------------------------------------------------------------------------- /static/helper.ini: -------------------------------------------------------------------------------- 1 | [自动配置] 2 | ; ------------------技能全屏---------------------------- 3 | 技能代码 = 70231 4 | 技能伤害 = 50123641 5 | 技能大小 = 5 6 | 技能个数 = 1 7 | ; ------------------装备buff装备代码---------------------------- 8 | 装备代码 = 100390328,100130702,100312841,100353408,100150762,100070718,100312845,100080744,100050897,101011025,101030890,100390337,102010681,106020676 9 | ; ------------------自动模式 1=剧情 2=搬砖------------------ 10 | 自动模式 = 2 11 | ; ------------------地图编号------------------ 12 | 普通地图 = 100002964,100002965,100002950,100002952,100002962,100002705,100002676,400001565 13 | 英豪地图 = 400001566,400001567,100002974,100002969,100002951,100002953,100002963,100002706 14 | ; ------------------地图难度 0=普通 1=冒险 2=勇士 3=王者 4=噩梦 5=自动取最高副本等级------------------ 15 | 地图难度 = 5 16 | ; ------------------角色数量 例如刷10个角色填写9,因为默认第一个角色不计入其中------------------ 17 | 角色数量 = 3 18 | ; ------------------跟随打怪 0=关闭 1=跟随 2=跟随打怪 3=技能call------------------ 19 | 跟随打怪 = 1 20 | ; ------------------过图方式 0=关闭 1=强制 2=漂移------------------ 21 | 过图方式 = 1 22 | ; ------------------处理装备 0=关闭 1=分解------------------ 23 | 处理装备 = 0 24 | ; ------------------开启功能 0=关闭 1=金身 2=装备buff 3=满技能------------------ 25 | 开启功能 = 0 26 | ; ------------------过滤物品------------------ 27 | 过滤物品 = 碎布片,金刚石,风化的碎骨,破旧的皮革,血滴石,金刚石,海蓝宝石,黑曜石,最下级砥石,最下级硬化剂,生锈的铁片,鸡腿,肉块,织女星的光辉,赫仑皇帝的印章,幸运兑换币,天才的地图碎片,柴火,玫,远古骑士的盔甲,使徒的气息,坚硬的龟壳,遗留的水晶碎片,[活动]闪亮的雷米援助礼袋 (10个),突变苎麻花叶,副船长的戒指,步枪零件,黑色龙舌兰酒,烤硬的黑面包,虚空魔石碎片,格林赛罗斯的果核,新手HP药剂,新手MP药剂,跃翔药剂,宠物饲料礼盒 (5个),突变草莓,暗黑城特产干酪,艾丽丝的香料,野草莓,卡勒特勋章,下级元素结晶,上级元素结晶,麻辣鲜香麻婆豆腐食盒,迷幻晶石,混沌魔石碎片,碳结晶体,数据芯片,甜蜜巧克力,沁凉雪球,神秘的胶囊碎片,阳光硬币,迷幻晶石,魔刹石,云霓碎片,克尔顿的印章,撒勒的印章,达人HP药剂,达人MP药剂,专家MP药剂,专家HP药剂,熟练MP药剂,熟练HP药剂,血滴石,黑曜石,紫玛瑙,金刚石,海蓝宝石,月饼硬币,暗黑倾向药剂,命运硬币,肉干,砂砾,天空树果实,燃烧瓶,军用回旋镖,裂空镖,甜瓜,飞镖,轰雷树果实,越桔,神圣葡萄酒,轰爆弹,爆弹,燃烧瓶,精灵香精,魔力之花,石头,苎麻花叶,怒海霸主银币,解密礼盒,无尽的永恒,风化的碎骨,破旧的皮革,最下级砥石,最下级硬化剂,生锈的铁片,碎布片,回旋镖,天界珍珠,朗姆酒,飞盘,魔力之花,卡勒特指令书,入门HP药剂,入门MP药剂,普通HP药剂,普通MP药剂,飞盘 2,邪恶药剂,圣杯,肉干,袖珍罐碎片 28 | ; ------------------出图方式 0 正常出图 1 快速出图------------------ 29 | 出图方式 = 1 -------------------------------------------------------------------------------- /static/mapId.txt: -------------------------------------------------------------------------------- 1 | 100002964 王之摇篮 2 | 100002965 海伯伦的预言所 3 | 100002950 白色大地 4 | 100002952 圣殿贝里科蒂斯 5 | 100002962 柯涅恩山 6 | 100002705 昆法特 7 | 100002676 纳瑟乌森林 8 | 400001565 永痕之光研究所 9 | 10 | 400001566 永痕之光研究所(英豪级) 11 | 400001567 纳瑟乌森林(英豪级) 12 | 100002974 王之摇篮 (英豪级) 13 | 100002969 海伯伦的预言所 (英豪级) 14 | 100002951 白色大地 (英豪级) 15 | 100002953 圣殿贝里科蒂斯 (英豪级) 16 | 100002963 柯涅恩山 (英豪级) 17 | 100002706 昆法特 (英豪级) 18 | 19 | 100002728 贝里科蒂斯 101 20 | 100002977 泽尔峡谷 103 21 | 100002786 洛仑山 104 22 | 贝奇的空间 ] [ 100002980 23 | 24 | 100000522 荆棘乐园 25 | 100000523 德洛斯矿山外围 26 | 100000524 远古墓地 27 | 100000525 绝望摇篮 28 | ------------------------------------------------------------------------------ 29 | 比较散乱的新副本:推荐等级:96~100级副本 30 | 31 | 100000002 = 根特皇宫 Lv.96(难度:普通、冒险 、无法进行深渊) 32 | 100000003 = 风暴航路 Lv.96(难度:普通、冒险、无法进行深渊) 33 | 100000151 = 智慧的引导 Lv.96(难度:普通、冒险)(深渊) 34 | 100000177 = 记忆之地 Lv.98(需完成前置任务才可以挑战此图,难度填写2、无法进行深渊) 35 | 100000176 = 无底坑道 Lv.98(需完成前置任务才可以挑战此图,难度填写2、无法进行深渊) 36 | 100000178 = 痛苦地下室 Lv.98(需完成前置任务才可以挑战此图,难度填写3、无法进行深渊) 37 | 100000179 = 暗黑神殿 Lv.98(需完成前置任务才可以挑战此图,难度填写3、无法进行深渊) 38 | 100000212 = 根特皇宫 Lv.98(【注意,这是挑战模式】需完成前置任务才可以挑战此图,难度填写4、无法进行深渊) 39 | 100000212 = 记忆之地 Lv.98(【注意,这是挑战模式】需完成前置任务才可以挑战此图,难度填写4、无法进行深渊) 40 | 100000215 = 无底坑道 Lv.98(【注意,这是挑战模式】需完成前置任务才可以挑战此图,难度填写4、无法进行深渊) 41 | 100000199 = 痛苦地下室 Lv.98(【注意,这是挑战模式】需完成前置任务才可以挑战此图,难度填写4、无法进行深渊) 42 | 100000209 = 暗黑神殿 Lv.98(【注意,这是挑战模式】需完成前置任务才可以挑战此图,难度填写4、无法进行深渊) 43 | 44 | 艾尔文防线:(不支持这个区域的深渊)推荐等级: 1 - 16级 45 | 46 | 3 = 幽暗密林 Lv. 1(难度:普通) 47 | 5 = 雷鸣废墟 Lv.3(难度:普通) 48 | 6 = 猛毒雷鸣废墟 Lv.5(难度:普通) 49 | 9 = 冰霜幽暗密林 Lv.8(难度:普通) 50 | 7 = 格拉卡 Lv.11(难度:普通) 51 | 8 = 烈焰格拉卡 Lv.13(难度:普通) 52 | 1000 = 暗黑雷鸣废墟 Lv.14 (需要8点疲劳才可以进图且难度必须是2才可以) 53 | 54 | 西海岸(天空之城):(不支持这个区域的深渊) 推荐等级: 17 - 23级 55 | 56 | 10 = 龙人之塔 Lv.17(难度:普通) 57 | 12 = 人偶玄关 Lv.19(难度:普通) 58 | 13 = 石巨人塔 Lv.20(难度:普通) 59 | 14 = 黑暗玄廊 Lv.21(难度:普通) 60 | 15 = 城主宫殿 Lv.23(难度:普通) 61 | 17 = 悬空城 Lv.22(难度:普通) 62 | 63 | 西海岸港口(天维巨兽):(不支持这个区域的深渊)推荐等级: 24 - 29级 64 | 65 | 21 = 神殿外围 Lv.24(难度:普通、冒险) 66 | 22 = 树精丛林 Lv.25(难度:普通、冒险) 67 | 23 = 炼狱 Lv.26(难度:普通、冒险) 68 | 24 = 极昼 Lv.27(难度:普通、冒险) 69 | 25 = 第一脊椎 Lv.28(难度:普通、冒险) 70 | 26 = 第二脊椎 Lv.29(难度:普通、冒险) 71 | 27 = 天帷禁地 Lv.29(难度:普通、冒险) 72 | 73 | 阿尔法利亚:(不支持这个区域的深渊) 推荐等级: 30 - 35级 74 | 75 | 31 = 浅栖之地 Lv.30(难度:普通、冒险) 76 | 32 = 蜘蛛洞穴 Lv.31(难度:普通、冒险) 77 | 150 = 蜘蛛王国 Lv.32(难度:普通、冒险) 78 | 151 = 英雄冢 Lv.33(难度:普通、冒险) 79 | 35 = 暗精灵墓地 Lv.34(难度:普通、冒险) 80 | 34 = 熔岩穴 Lv.35(难度:普通、冒险) 81 | 36 = 暗黑城入口 Lv.29(难度:普通、冒险) 82 | 83 | 诺伊佩拉:(不支持这个区域的深渊) 推荐等级: 36 - 39级 84 | 85 | 152 = 暴君的祭坛 Lv.36(难度:普通、冒险) 86 | 153 = 黄金矿洞 Lv.37(难度:普通、冒险) 87 | 154 = 远古墓穴深处 Lv.38(难度:普通、冒险) 88 | 89 | 斯顿雪域:(不支持这个区域的深渊) 推荐等级: 40 - 45级 90 | 91 | 40 = 山脊 Lv.40(难度:普通、冒险、勇士) 92 | 41 = 冰心少年 Lv.41(难度:普通、冒险、勇士) 93 | 42 = 利库天井 Lv.42(难度:普通、冒险、勇士) 94 | 43 = 白色废墟 Lv.43(难度:普通、冒险、勇士) 95 | 141 = 布万加的修炼场 Lv.44(难度:普通、冒险、勇士) 96 | 97 | 后街(诺斯玛尔):(不支持这个区域的深渊) 推荐等级: 46 - 49级 98 | 99 | 61 = 绿都格罗兹尼 Lv.46(难度:普通、冒险、勇士) 100 | 50 = 堕落的盗贼 Lv.47(难度:普通、冒险、勇士) 101 | 51 = 迷乱之村哈穆林 Lv.48(难度:普通、冒险、勇士) 102 | 52 = 血蝴蝶之舞 Lv.48(难度:普通、冒险、勇士) 103 | 53 = 疑惑之村 Lv.49(难度:普通、冒险、勇士) 104 | 7146 = 秘密村庄 Lv.47(需要8点疲劳才可以进图且难度必须是2才可以) 105 | 106 | 银色村庄(亚诺法深森林):(不支持这个区域的深渊) 推荐等级: 50 - 53级 107 | 108 | 144 = 冰晶森林 Lv.50(难度:普通、冒险、勇士) 109 | 145 = 炽晶森林 Lv.51(难度:普通、冒险、勇士) 110 | 146 = 幽冥监狱 Lv.52(难度:普通、冒险、勇士) 111 | 148 = 水晶矿脉 Lv.53(难度:普通、冒险、勇士) 112 | 7147 = 精灵之森 Lv.51(需要8点疲劳才可以进图且难度必须是2才可以) 113 | 114 | 赫顿马尔废墟(厄运之城):(不支持这个区域的深渊) 推荐等级: 54 - 57级 115 | 116 | 156 = 蘑菇庄园 Lv.54(难度:普通、冒险、勇士) 117 | 157 = 蚁后的巢穴 Lv.55(难度:普通、冒险、勇士) 118 | 158 = 腐烂之地 Lv.56(难度:普通、冒险、勇士) 119 | 159 = 赫顿玛尔旧街区 Lv.57(难度:普通、冒险、勇士) 120 | 160 = 绝望的棋局 Lv.57(难度:普通、冒险、勇士) 121 | 7148 = 精灵之森 Lv.55(需要8点疲劳才可以进图且难度必须是2才可以) 122 | 123 | 赫顿马尔废墟(厄运之城):(不支持这个区域的深渊) 推荐等级: 58 - 61级 124 | 125 | 161 = 鲨鱼栖息地 Lv.58(难度:普通、冒险、勇士) 126 | 162 = 人鱼国度 Lv.59(难度:普通、冒险、勇士) 127 | 163 = GBL女神殿 Lv.60(难度:普通、冒险、勇士) 128 | 164 = 树精繁殖地 Lv.61(难度:普通、冒险、勇士) 129 | 7149 = 天空岛 Lv.60(需要8点疲劳才可以进图且难度必须是2才可以) 130 | 131 | 天界(安特):(不支持这个区域的深渊) 推荐等级: 63 - 70级 132 | 133 | 80 = 根特外围 Lv.63(难度:普通、冒险、勇士、王者) 134 | 81 = 根特东门 Lv.64(难度:普通、冒险、勇士、王者) 135 | 82 = 根特南门 Lv.65(难度:普通、冒险、勇士、王者) 136 | 88 = 根特北门 Lv.66(难度:普通、冒险、勇士、王者、推荐此图搬砖、此图吃低保) 137 | 89 = 根特防御战 Lv.67(难度:普通、冒险、勇士、王者) 138 | 83 = 夜间袭击战 Lv.68(难度:普通、冒险、勇士、王者) 139 | 84 = 补给线阻断战 Lv.69(难度:普通、冒险、勇士、王者、推荐此图搬砖、此图吃低保) 140 | 85 = 追击歼灭战 Lv.70(难度:普通、冒险、勇士、王者) 141 | 7150 = 哈尔特山 Lv.65(需要8点疲劳才可以进图且难度必须是2才可以) 142 | 143 | 鲁夫特悬空海港(海上列车):(支持部分白图深渊) 推荐等级: 71 - 74级 144 | 145 | 86 = 列车上的海贼 Lv.71(可任意难度、支持深渊) 146 | 87 = 夺回西部线 Lv.72(可任意难度、支持深渊) 147 | 92 = 雾都赫伊斯 Lv.73(可任意难度、支持深渊) 148 | 93 = 阿登高地 Lv.74(可任意难度、支持深渊) 149 | 7151 = 海上航线 Lv.65(需要8点疲劳才可以进图且难度必须是2才可以) 150 | 151 | 时空之门:(支持白图深渊) 推荐等级: 71 - 74级 152 | 153 | 70 = 格兰之火 Lv.75(可任意难度、支持深渊、支持该图刷全图) 154 | 71 = 瘟疫之源 Lv.76(可任意难度、支持深渊、支持该图刷全图) 155 | 72 = 卡勒特之初 Lv.77(可任意难度、支持深渊、支持该图刷全图) 156 | 74 = 绝密区域 Lv.78(可任意难度、支持深渊、支持该图刷全图、推荐此图搬砖、此图吃低保) 157 | 75 = 昔日悲鸣 Lv.79(可任意难度、支持深渊、支持该图刷全图) 158 | 76 = 凛冬 Lv.80(可任意难度、支持深渊、支持该图刷全图、推荐此图搬砖) 159 | 77 = 谜之觉悟 Lv.80(可任意难度、支持深渊、支持该图刷全图) 160 | 7152 = 时间界限 Lv.77(需要8点疲劳才可以进图且难度必须是2才可以) 161 | 162 | 163 | 发电站:(支持白图深渊) 推荐等级: 81 - 83级 164 | 165 | 101 = 克雷发电站 Lv.81(可任意难度、支持深渊、支持该图刷全图) 166 | 102 = 普鲁兹发电站 Lv.81(可任意难度、支持深渊、支持该图刷全图) 167 | 103 = 特伦斯发电站 Lv.82(可任意难度、支持深渊、支持该图刷全图) 168 | 104 = 格兰迪发电站 Lv.83(可任意难度、支持深渊、支持该图刷全图、推荐此图搬砖) 169 | 7153 = 控制塔 Lv.81(需要8点疲劳才可以进图且难度必须是2才可以) 170 | 171 | 克洛斯岛:(支持白图深渊) 推荐等级: 84 - 85级 172 | 173 | 190 = 倒悬的瞭望台 Lv.85(可任意难度、支持深渊、支持该图刷全图) 174 | 191 = 卢克的聚光镜 Lv.85(可任意难度、支持深渊、支持该图刷全图) 175 | 192 = 钢铁之臂 Lv.85(可任意难度、支持深渊、支持该图刷全图) 176 | 193 = 能源熔炉 Lv.86(可任意难度、支持深渊、支持该图刷全图) 177 | 194 = 光之舞会 Lv.86(可任意难度、支持深渊、支持该图刷全图) 178 | 7154 = 不灭回廊 Lv.85(需要8点疲劳才可以进图且难度必须是2才可以) 179 | 180 | 魔界(地轨中心):(支持白图深渊) 推荐等级: 85 - 91级 181 | 182 | 310 = 时间广场 Lv.87(可任意难度、支持深渊、支持该图刷全图) 183 | 311 = 兽人峡谷 Lv.87(可任意难度、支持深渊、支持该图刷全图) 184 | 312 = 恐怖的栖息地 Lv.88(可任意难度、支持深渊、支持该图刷全图) 185 | 313 = 疾风地带 Lv.88(可任意难度、支持深渊、支持该图刷全图) 186 | 314 = 红色魔女之森 Lv.89(可任意难度、支持深渊、支持该图刷全图、推荐此图搬砖) 187 | 7155 = 血色防线 Lv.88(需要8点疲劳才可以进图且难度必须是2才可以、推荐此图搬砖) 188 | 189 | 黑市(哈林):(支持白图深渊) 推荐等级: 90 - 95级 190 | 191 | 291100293 = 全蚀市场 Lv.90(可任意难度、支持深渊、推荐此图搬砖) 192 | 291100268 = 亡命杀镇 Lv.90(可任意难度、支持深渊) 193 | 291100317 = 皇家娱乐 Lv.91(可任意难度、支持深渊、推荐此图搬砖) 194 | 291100308 = 黑暗都市 Lv.91(可任意难度、支持深渊) 195 | 291100309 = 第九隔离区 Lv.92(可任意难度、支持深渊) 196 | 291100319 = 搏击俱乐部 Lv.92(可任意难度、支持深渊) 197 | 7322 = 断头谷 Lv.94(需要8点疲劳才可以进图且难度必须是2才可以、推荐此图搬砖) 198 | 530 = 黎明裂缝 Lv.95(暂时不支持该副本) 199 | 200 | 时空裂缝的特殊深渊图(刷深渊专属):推荐等级:71 - 95级 201 | 202 | 7542 = 海上列车(时空裂缝) Lv.71(难度填写2、只能深渊) 203 | 7527 = 时空之门(时空裂缝) Lv.77(难度填写2、只能深渊) 204 | 7530 = 能源中心(时空裂缝) Lv.82(难度填写2、只能深渊) 205 | 7533 = 寂静城(时空裂缝) Lv.85(难度填写2、只能深渊) 206 | 7539 = 中央公园(时空裂缝) Lv.92(难度填写2、只能深渊) 207 | 7191 = 超星空 Lv.95(难度填写1、只能深渊) 208 | 209 | 210 | 211 | 其他特殊图、不支持的副本: 212 | 213 | 7101 = 王的遗迹 214 | 7102 = 诺伊佩拉 215 | 7103 = 悲鸣洞穴 216 | 7104 = 比尔马克帝国试验场 217 | 7105 = 痛苦之村列瑟芬 218 | 7106 = 卡勒特指挥部 219 | 7107 = 幽灵列车 220 | 3200 = 维特拉的复仇 221 | 3201 = 双子巨人的背叛 222 | 3202 = 圣地:龙之魂 223 | 5101 = 诞生之圣所 224 | 5102 = 泯灭之圣所 -------------------------------------------------------------------------------- /static/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/static/qq.png -------------------------------------------------------------------------------- /static/wePay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/static/wePay.png -------------------------------------------------------------------------------- /static/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Python/76b3db179b7296ee76203b4c0fa7ed10d580c57e/static/wechat.png --------------------------------------------------------------------------------